[SCM] Packaging for python-ase branch, master, updated. 562587867e6f082faa197051a5408d437f30f4c6

Ask Hjorth Larsen asklarsen at gmail.com
Thu Jun 14 16:11:33 UTC 2012


The following commit has been merged in the master branch:
commit 562587867e6f082faa197051a5408d437f30f4c6
Author: Ask Hjorth Larsen <asklarsen at gmail.com>
Date:   Thu Jun 14 18:11:14 2012 +0200

    python-ase upstream files, initial commit

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LESSER b/COPYING.LESSER
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING.LESSER
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d09fccd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+ASE is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 2.1 of the License, or
+(at your option) any later version.
+
+ASE is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with ASE.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..98926a8
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include ase/lattice/spacegroup/spacegroup.dat
+include ase/gui/po/ag.pot
+include ase/gui/po/makefile
+include ase/gui/po/??_??/LC_MESSAGES/ag.po
+
diff --git a/README.txt b/README.txt
new file mode 100755
index 0000000..dab49e8
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,5 @@
+==========
+Python ASE
+==========
+
+Webpage: http://wiki.fysik.dtu.dk/ase
diff --git a/ase/__init__.py b/ase/__init__.py
new file mode 100644
index 0000000..568f56a
--- /dev/null
+++ b/ase/__init__.py
@@ -0,0 +1,8 @@
+# Copyright 2008, 2009 CAMd
+# (see accompanying license files for details).
+
+"""Atomic Simulation Environment."""
+
+
+from ase.atom import Atom
+from ase.atoms import Atoms
diff --git a/ase/all.py b/ase/all.py
new file mode 100644
index 0000000..147171f
--- /dev/null
+++ b/ase/all.py
@@ -0,0 +1,37 @@
+"""This module imports many important modules at once."""
+
+from ase.atom import Atom
+from ase.atoms import Atoms
+from ase.units import *
+from ase.io import read, write
+from ase.io.trajectory import PickleTrajectory
+from ase.dft import STM, monkhorst_pack, DOS
+from ase.optimize.mdmin import MDMin
+from ase.optimize.lbfgs import HessLBFGS
+from ase.optimize.fire import FIRE
+from ase.optimize.lbfgs import LBFGS, LBFGSLineSearch
+from ase.optimize.bfgs import BFGS
+from ase.optimize import QuasiNewton
+from ase.md.verlet import VelocityVerlet
+from ase.md.langevin import Langevin
+from ase.constraints import *
+from ase.calculators.lj import LennardJones
+from ase.calculators.emt import EMT
+from ase.calculators.siesta import Siesta
+from ase.calculators.dacapo import Dacapo
+from ase.calculators.vasp import Vasp
+from ase.calculators.aims import Aims, AimsCube
+from ase.calculators.turbomole import Turbomole
+from ase.calculators.dftb import Dftb
+from ase.neb import NEB, SingleCalculatorNEB
+from ase.dimer import DimerControl, DimerAtoms, DimerTranslate, \
+     MinModeAtoms, MinModeTranslate
+from ase.visualize import view
+from ase.data import chemical_symbols, atomic_numbers, atomic_names, \
+     atomic_masses, covalent_radii, reference_states
+from ase.data.molecules import molecule
+from ase.structure import *
+#from ase.lattice import bulk
+
+from math import pi, sqrt
+import numpy as np
diff --git a/ase/atom.py b/ase/atom.py
new file mode 100644
index 0000000..2525c3c
--- /dev/null
+++ b/ase/atom.py
@@ -0,0 +1,214 @@
+"""This module defines the Atom object."""
+
+import warnings
+
+import numpy as np
+
+from ase.data import atomic_numbers, chemical_symbols, atomic_masses
+
+
+#         singular,    plural,     default value
+names = {'position': ('positions', np.zeros(3)),
+         'number':   ('numbers',   0),
+         'tag':      ('tags',      0),
+         'momentum': ('momenta',   np.zeros(3)),
+         'mass':     ('masses',    None),
+         'magmom':   ('magmoms',   0.0),
+         'charge':   ('charges',   0.0)
+         }
+
+
+def atomproperty(name, doc):
+    """Helper function to easily create Atom attribute property."""
+
+    def getter(self):
+        return self.get(name)
+
+    def setter(self, value):
+        self.set(name, value)
+
+    def deleter(self):
+        self.delete(name)
+
+    return property(getter, setter, deleter, doc)
+
+
+def xyzproperty(index):
+    """Helper function to easily create Atom XYZ-property."""
+
+    def getter(self):
+        return self.position[index]
+
+    def setter(self, value):
+        self.position[index] = value
+
+    return property(getter, setter, doc='XYZ'[index] + '-coordinate')
+
+
+class Atom(object):
+    """Class for representing a single atom.
+
+    Parameters:
+    
+    symbol: str or int
+        Can be a chemical symbol (str) or an atomic number (int).
+    position: sequence of 3 floats
+        Atomi position.
+    tag: int
+        Special purpose tag.
+    momentum: sequence of 3 floats
+        Momentum for atom.
+    mass: float
+        Atomic mass in atomic units.
+    magmom: float or 3 floats
+        Magnetic moment.
+    charge: float
+        Atomic charge.
+    """
+    __slots__ = ['data', 'atoms', 'index']
+
+    def __init__(self, symbol='X', position=(0, 0, 0),
+                 tag=None, momentum=None, mass=None,
+                 magmom=None, charge=None,
+                 atoms=None, index=None):
+
+        self.data = d = {}
+
+        if atoms is None:
+            # This atom is not part of any Atoms object:
+            if isinstance(symbol, str):
+                d['number'] = atomic_numbers[symbol]
+            else:
+                d['number'] = symbol
+            d['position'] = np.array(position, float)
+            d['tag'] = tag
+            if momentum is not None:
+                momentum = np.array(momentum, float)
+            d['momentum'] = momentum
+            d['mass'] = mass
+            if magmom is not None:
+                magmom = np.array(magmom, float)
+            d['magmom'] = magmom
+            d['charge'] = charge
+
+        self.index = index
+        self.atoms = atoms
+
+    def __repr__(self):
+        s = "Atom('%s', %s" % (self.symbol, list(self.position))
+        for name in ['tag', 'momentum', 'mass', 'magmom', 'charge']:
+            value = self.get_raw(name)
+            if value is not None:
+                if isinstance(value, np.ndarray):
+                    value = value.tolist()
+                s += ', %s=%s' % (name, value)
+        if self.atoms is None:
+            s += ')'
+        else:
+            s += ', index=%d)' % self.index
+        return s
+
+    def cut_reference_to_atoms(self):
+        """Cut reference to atoms object."""
+        for name in names:
+            self.data[name] = self.get_raw(name)
+        self.index = None
+        self.atoms = None
+        
+    def get_raw(self, name):
+        """Get attribute, return None if not explicitely set."""
+        if name == 'symbol':
+            return chemical_symbols[self.get_raw('number')]
+
+        if self.atoms is None:
+            return self.data[name]
+        
+        plural = names[name][0]
+        if plural in self.atoms.arrays:
+            return self.atoms.arrays[plural][self.index]
+        else:
+            return None
+
+    def get(self, name):
+        """Get attribute, return default if not explicitely set."""
+        value = self.get_raw(name)
+        if value is None:
+            if name == 'mass':
+                value = atomic_masses[self.number]
+            else:
+                value = names[name][1]
+        return value
+
+    def set(self, name, value):
+        """Set attribute."""
+        if name == 'symbol':
+            name = 'number'
+            value = atomic_numbers[value]
+
+        if self.atoms is None:
+            assert name in names
+            self.data[name] = value
+        else:
+            plural, default = names[name]
+            if plural in self.atoms.arrays:
+                array = self.atoms.arrays[plural]
+                if name == 'magmom' and array.ndim == 2:
+                    assert len(value) == 3
+                array[self.index] = value
+            else:
+                if name == 'magmom' and np.asarray(value).ndim == 1:
+                    array = np.zeros((len(self.atoms), 3))
+                elif name == 'mass':
+                    array = self.atoms.get_masses()
+                else:
+                    default = np.asarray(default)
+                    array = np.zeros((len(self.atoms),) + default.shape,
+                                     default.dtype)
+                array[self.index] = value
+                self.atoms.new_array(plural, array)
+
+    def delete(self, name):
+        """Delete attribute."""
+        assert self.atoms is None
+        assert name not in ['number', 'symbol', 'position']
+        self.data[name] = None
+
+    symbol = atomproperty('symbol', 'Chemical symbol')
+    number = atomproperty('number', 'Atomic number')
+    position = atomproperty('position', 'XYZ-coordinates')
+    tag = atomproperty('tag', 'Integer tag')
+    momentum = atomproperty('momentum', 'XYZ-momentum')
+    mass = atomproperty('mass', 'Atomic mass')
+    magmom = atomproperty('magmom', 'Initial magnetic moment')
+    charge = atomproperty('charge', 'Atomic charge')
+    x = xyzproperty(0)
+    y = xyzproperty(1)
+    z = xyzproperty(2)
+
+    def _get(self, name):
+        """Helper function for deprecated get methods."""
+        warnings.warn('Use atom.%s' % name, stacklevel=3)
+        return getattr(self, name)
+
+    def _set(self, name, value):
+        """Helper function for deprecated set methods."""
+        warnings.warn('Use atom.%s = ...' % name, stacklevel=3)
+        setattr(self, name, value)
+
+    def get_symbol(self): return self._get('symbol')
+    def get_atomic_number(self): return self._get('number')
+    def get_position(self): return self._get('position')
+    def get_tag(self): return self._get('tag')
+    def get_momentum(self): return self._get('momentum')
+    def get_mass(self): return self._get('mass')
+    def get_initial_magnetic_moment(self): return self._get('magmom')
+    def get_charge(self): return self._get('charge')
+
+    def set_symbol(self, value): self._set('symbol', value)
+    def set_atomic_number(self, value): self._set('number', value)
+    def set_position(self, value): self._set('position', value)
+    def set_tag(self, value): self._set('tag', value)
+    def set_momentum(self, value): self._set('momentum', value)
+    def set_mass(self, value): self._set('mass', value)
+    def set_initial_magnetic_moment(self, value): self._set('magmom', value)
+    def set_charge(self, value): self._set('charge', value)
diff --git a/ase/atoms.py b/ase/atoms.py
new file mode 100644
index 0000000..a1f9afa
--- /dev/null
+++ b/ase/atoms.py
@@ -0,0 +1,1473 @@
+# Copyright 2008, 2009 CAMd
+# (see accompanying license files for details).
+
+"""Definition of the Atoms class.
+
+This module defines the central object in the ASE package: the Atoms
+object.
+"""
+
+from math import cos, sin
+
+import numpy as np
+
+from ase.atom import Atom
+from ase.data import atomic_numbers, chemical_symbols, atomic_masses
+import ase.units as units
+
+
+class Atoms(object):
+    """Atoms object.
+
+    The Atoms object can represent an isolated molecule, or a
+    periodically repeated structure.  It has a unit cell and
+    there may be periodic boundary conditions along any of the three
+    unit cell axes.
+
+    Information about the atoms (atomic numbers and position) is
+    stored in ndarrays.  Optionally, there can be information about
+    tags, momenta, masses, magnetic moments and charges.
+
+    In order to calculate energies, forces and stresses, a calculator
+    object has to attached to the atoms object.
+
+    Parameters:
+
+    symbols: str (formula) or list of str
+        Can be a string formula, a list of symbols or a list of
+        Atom objects.  Examples: 'H2O', 'COPt12', ['H', 'H', 'O'],
+        [Atom('Ne', (x, y, z)), ...].
+    positions: list of xyz-positions
+        Atomic positions.  Anything that can be converted to an
+        ndarray of shape (n, 3) will do: [(x1,y1,z1), (x2,y2,z2),
+        ...].
+    scaled_positions: list of scaled-positions
+        Like positions, but given in units of the unit cell.
+        Can not be set at the same time as positions.
+    numbers: list of int
+        Atomic numbers (use only one of symbols/numbers).
+    tags: list of int
+        Special purpose tags.
+    momenta: list of xyz-momenta
+        Momenta for all atoms.
+    masses: list of float
+        Atomic masses in atomic units.
+    magmoms: list of float or list of xyz-values
+        Magnetic moments.  Can be either a single value for each atom
+        for collinear calculations or three numbers for each atom for
+        non-collinear calculations.
+    charges: list of float
+        Atomic charges.
+    cell: 3x3 matrix
+        Unit cell vectors.  Can also be given as just three
+        numbers for orthorhombic cells.  Default value: [1, 1, 1].
+    pbc: one or three bool
+        Periodic boundary conditions flags.  Examples: True,
+        False, 0, 1, (1, 1, 0), (True, False, False).  Default
+        value: False.
+    constraint: constraint object(s)
+        Used for applying one or more constraints during structure
+        optimization.
+    calculator: calculator object
+        Used to attach a calculator for calculating energies and atomic
+        forces.
+    info: dict of key-value pairs
+        Dictionary of key-value pairs with additional information
+        about the system.  The following keys may be used by ase:
+
+          - spacegroup: Spacegroup instance
+          - unit_cell: 'conventional' | 'primitive' | int | 3 ints
+          - adsorbate_info:
+
+        Items in the info attribute survives copy and slicing and can
+        be store to and retrieved from trajectory files given that the
+        key is a string, the value is picklable and, if the value is a
+        user-defined object, its base class is importable.  One should
+        not make any assumptions about the existence of keys.
+
+    Examples:
+
+    These three are equivalent:
+
+    >>> d = 1.104  # N2 bondlength
+    >>> a = Atoms('N2', [(0, 0, 0), (0, 0, d)])
+    >>> a = Atoms(numbers=[7, 7], positions=[(0, 0, 0), (0, 0, d)])
+    >>> a = Atoms([Atom('N', (0, 0, 0)), Atom('N', (0, 0, d)])
+
+    FCC gold:
+
+    >>> a = 4.05  # Gold lattice constant
+    >>> b = a / 2
+    >>> fcc = Atoms('Au',
+    ...             cell=[(0, b, b), (b, 0, b), (b, b, 0)],
+    ...             pbc=True)
+
+    Hydrogen wire:
+
+    >>> d = 0.9  # H-H distance
+    >>> L = 7.0
+    >>> h = Atoms('H', positions=[(0, L / 2, L / 2)],
+    ...           cell=(d, L, L),
+    ...           pbc=(1, 0, 0))
+    """
+
+    def __init__(self, symbols=None,
+                 positions=None, numbers=None,
+                 tags=None, momenta=None, masses=None,
+                 magmoms=None, charges=None,
+                 scaled_positions=None,
+                 cell=None, pbc=None,
+                 constraint=None,
+                 calculator=None, 
+                 info=None):
+
+        atoms = None
+
+        if hasattr(symbols, 'GetUnitCell'):
+            from ase.old import OldASEListOfAtomsWrapper
+            atoms = OldASEListOfAtomsWrapper(symbols)
+            symbols = None
+        elif hasattr(symbols, 'get_positions'):
+            atoms = symbols
+            symbols = None
+        elif (isinstance(symbols, (list, tuple)) and
+              len(symbols) > 0 and isinstance(symbols[0], Atom)):
+            # Get data from a list or tuple of Atom objects:
+            data = [[atom.get_raw(name) for atom in symbols]
+                    for name in
+                    ['position', 'number', 'tag', 'momentum',
+                     'mass', 'magmom', 'charge']]
+            atoms = self.__class__(None, *data)
+            symbols = None
+
+        if atoms is not None:
+            # Get data from another Atoms object:
+            if scaled_positions is not None:
+                raise NotImplementedError
+            if symbols is None and numbers is None:
+                numbers = atoms.get_atomic_numbers()
+            if positions is None:
+                positions = atoms.get_positions()
+            if tags is None and atoms.has('tags'):
+                tags = atoms.get_tags()
+            if momenta is None and atoms.has('momenta'):
+                momenta = atoms.get_momenta()
+            if magmoms is None and atoms.has('magmoms'):
+                magmoms = atoms.get_initial_magnetic_moments()
+            if masses is None and atoms.has('masses'):
+                masses = atoms.get_masses()
+            if charges is None and atoms.has('charges'):
+                charges = atoms.get_charges()
+            if cell is None:
+                cell = atoms.get_cell()
+            if pbc is None:
+                pbc = atoms.get_pbc()
+            if constraint is None:
+                constraint = [c.copy() for c in atoms.constraints]
+            if calculator is None:
+                calculator = atoms.get_calculator()
+
+        self.arrays = {}
+        
+        if symbols is None:
+            if numbers is None:
+                if positions is not None:
+                    natoms = len(positions)
+                elif scaled_positions is not None:
+                    natoms = len(scaled_positions)
+                else:
+                    natoms = 0
+                numbers = np.zeros(natoms, int)
+            self.new_array('numbers', numbers, int)
+        else:
+            if numbers is not None:
+                raise ValueError(
+                    'Use only one of "symbols" and "numbers".')
+            else:
+                self.new_array('numbers', symbols2numbers(symbols), int)
+
+        if cell is None:
+            cell = np.eye(3)
+        self.set_cell(cell)
+
+        if positions is None:
+            if scaled_positions is None:
+                positions = np.zeros((len(self.arrays['numbers']), 3))
+            else:
+                positions = np.dot(scaled_positions, self._cell)
+        else:
+            if scaled_positions is not None:
+                raise RuntimeError('Both scaled and cartesian positions set!')
+        self.new_array('positions', positions, float, (3,))
+
+        self.set_constraint(constraint)
+        self.set_tags(default(tags, 0))
+        self.set_momenta(default(momenta, (0.0, 0.0, 0.0)))
+        self.set_masses(default(masses, None))
+        self.set_initial_magnetic_moments(default(magmoms, 0.0))
+        self.set_charges(default(charges, 0.0))
+        if pbc is None:
+            pbc = False
+        self.set_pbc(pbc)
+
+        if info is None:
+            self.info = {}
+        else:
+            self.info = dict(info)
+
+        self.adsorbate_info = {}
+
+        self.set_calculator(calculator)
+
+    def set_calculator(self, calc=None):
+        """Attach calculator object."""
+        if hasattr(calc, '_SetListOfAtoms'):
+            from ase.old import OldASECalculatorWrapper
+            calc = OldASECalculatorWrapper(calc, self)
+        if hasattr(calc, 'set_atoms'):
+            calc.set_atoms(self)
+        self._calc = calc
+
+    def get_calculator(self):
+        """Get currently attached calculator object."""
+        return self._calc
+
+    def _del_calculator(self):
+        self._calc = None
+
+    calc = property(get_calculator, set_calculator, _del_calculator,
+                    doc='Calculator object.')
+    
+    def set_constraint(self, constraint=None):
+        """Apply one or more constrains.
+
+        The *constraint* argument must be one constraint object or a
+        list of constraint objects."""
+        if constraint is None:
+            self._constraints = []
+        else:
+            if isinstance(constraint, (list, tuple)):
+                self._constraints = constraint
+            else:
+                self._constraints = [constraint]
+
+    def _get_constraints(self):
+        return self._constraints
+
+    def _del_constraints(self):
+        self._constraints = []
+
+    constraints = property(_get_constraints, set_constraint, _del_constraints,
+                           'Constraints of the atoms.')
+    
+    def set_cell(self, cell, scale_atoms=False, fix=None):
+        """Set unit cell vectors.
+
+        Parameters:
+
+        cell : 
+            Unit cell.  A 3x3 matrix (the three unit cell vectors) or
+            just three numbers for an orthorhombic cell.
+        scale_atoms : bool
+            Fix atomic positions or move atoms with the unit cell?
+            Default behavior is to *not* move the atoms (scale_atoms=False).
+
+        Examples:
+
+        Two equivalent ways to define an orthorhombic cell:
+        
+        >>> a.set_cell([a, b, c])
+        >>> a.set_cell([(a, 0, 0), (0, b, 0), (0, 0, c)])
+
+        FCC unit cell:
+
+        >>> a.set_cell([(0, b, b), (b, 0, b), (b, b, 0)])
+        """
+
+        if fix is not None:
+            raise TypeError('Please use scale_atoms=%s' % (not fix))
+
+        cell = np.array(cell, float)
+        if cell.shape == (3,):
+            cell = np.diag(cell)
+        elif cell.shape != (3, 3):
+            raise ValueError('Cell must be length 3 sequence or '
+                             '3x3 matrix!')
+        if scale_atoms:
+            M = np.linalg.solve(self._cell, cell)
+            self.arrays['positions'][:] = np.dot(self.arrays['positions'], M)
+        self._cell = cell
+
+    def get_cell(self):
+        """Get the three unit cell vectors as a 3x3 ndarray."""
+        return self._cell.copy()
+
+    def get_reciprocal_cell(self):
+        """Get the three reciprocal lattice vectors as a 3x3 ndarray.
+
+        Note that the commonly used factor of 2 pi for Fourier
+        transforms is not included here."""
+        
+        rec_unit_cell = np.linalg.inv(self.get_cell()).transpose()
+        return rec_unit_cell
+
+    def set_pbc(self, pbc):
+        """Set periodic boundary condition flags."""
+        if isinstance(pbc, int):
+            pbc = (pbc,) * 3
+        self._pbc = np.array(pbc, bool)
+        
+    def get_pbc(self):
+        """Get periodic boundary condition flags."""
+        return self._pbc.copy()
+
+    def new_array(self, name, a, dtype=None, shape=None):
+        """Add new array.
+
+        If *shape* is not *None*, the shape of *a* will be checked."""
+        
+        if dtype is not None:
+            a = np.array(a, dtype)
+        else:
+            a = a.copy()
+            
+        if name in self.arrays:
+            raise RuntimeError
+
+        for b in self.arrays.values():
+            if len(a) != len(b):
+                raise ValueError('Array has wrong length: %d != %d.' %
+                                 (len(a), len(b)))
+            break
+
+        if shape is not None and a.shape[1:] != shape:
+            raise ValueError('Array has wrong shape %s != %s.' %
+                             (a.shape, (a.shape[0:1] + shape)))
+        
+        self.arrays[name] = a
+
+    def get_array(self, name, copy=True):
+        """Get an array.
+
+        Returns a copy unless the optional argument copy is false.
+        """
+        if copy:
+            return self.arrays[name].copy()
+        else:
+            return self.arrays[name]
+    
+    def set_array(self, name, a, dtype=None, shape=None):
+        """Update array.
+
+        If *shape* is not *None*, the shape of *a* will be checked.
+        If *a* is *None*, then the array is deleted."""
+        
+        b = self.arrays.get(name)
+        if b is None:
+            if a is not None:
+                self.new_array(name, a, dtype, shape)
+        else:
+            if a is None:
+                del self.arrays[name]
+            else:
+                a = np.asarray(a)
+                if a.shape != b.shape:
+                    raise ValueError('Array has wrong shape %s != %s.' %
+                                     (a.shape, b.shape))
+                b[:] = a
+
+    def has(self, name):
+        """Check for existence of array.
+
+        name must be one of: 'tags', 'momenta', 'masses', 'magmoms',
+        'charges'."""
+        return name in self.arrays
+    
+    def set_atomic_numbers(self, numbers):
+        """Set atomic numbers."""
+        self.set_array('numbers', numbers, int, ())
+
+    def get_atomic_numbers(self):
+        """Get integer array of atomic numbers."""
+        return self.arrays['numbers'].copy()
+
+    def set_chemical_symbols(self, symbols):
+        """Set chemical symbols."""
+        self.set_array('numbers', symbols2numbers(symbols), int, ())
+
+    def get_chemical_symbols(self, reduce=False):
+        """Get list of chemical symbol strings.
+
+        If reduce is True, a single string is returned, where repeated
+        elements have been contracted to a single symbol and a number.
+        E.g. instead of ['C', 'O', 'O', 'H'], the string 'CO2H' is returned.
+        """
+        if not reduce:
+            # XXX
+            return [chemical_symbols[Z] for Z in self.arrays['numbers']]
+        else:
+            num = self.get_atomic_numbers()
+            N = len(num)
+            dis = np.concatenate(([0], np.arange(1, N)[num[1:] != num[:-1]]))
+            repeat = np.append(dis[1:], N) - dis
+            symbols = ''.join([chemical_symbols[num[d]] + str(r) * (r != 1)
+                               for r, d in zip(repeat, dis)])
+            return symbols
+
+    def set_tags(self, tags):
+        """Set tags for all atoms."""
+        self.set_array('tags', tags, int, ())
+        
+    def get_tags(self):
+        """Get integer array of tags."""
+        if 'tags' in self.arrays:
+            return self.arrays['tags'].copy()
+        else:
+            return np.zeros(len(self), int)
+
+    def set_momenta(self, momenta):
+        """Set momenta."""
+        if len(self.constraints) > 0 and momenta is not None:
+            momenta = np.array(momenta)  # modify a copy
+            for constraint in self.constraints:
+                constraint.adjust_forces(self.arrays['positions'], momenta)
+        self.set_array('momenta', momenta, float, (3,))
+
+    def set_velocities(self, velocities):
+        """Set the momenta by specifying the velocities."""
+        self.set_momenta(self.get_masses()[:, np.newaxis] * velocities)
+        
+    def get_momenta(self):
+        """Get array of momenta."""
+        if 'momenta' in self.arrays:
+            return self.arrays['momenta'].copy()
+        else:
+            return np.zeros((len(self), 3))
+        
+    def set_masses(self, masses='defaults'):
+        """Set atomic masses.
+
+        The array masses should contain a list of masses.  In case
+        the masses argument is not given or for those elements of the
+        masses list that are None, standard values are set."""
+        
+        if masses == 'defaults':
+            masses = atomic_masses[self.arrays['numbers']]
+        elif isinstance(masses, (list, tuple)):
+            newmasses = []
+            for m, Z in zip(masses, self.arrays['numbers']):
+                if m is None:
+                    newmasses.append(atomic_masses[Z])
+                else:
+                    newmasses.append(m)
+            masses = newmasses
+        self.set_array('masses', masses, float, ())
+
+    def get_masses(self):
+        """Get array of masses."""
+        if 'masses' in self.arrays:
+            return self.arrays['masses'].copy()
+        else:
+            return atomic_masses[self.arrays['numbers']]
+        
+    def set_initial_magnetic_moments(self, magmoms=None):
+        """Set the initial magnetic moments.
+
+        Use either one or three numbers for every atom (collinear
+        or non-collinear spins)."""
+        
+        if magmoms is None:
+            self.set_array('magmoms', None)
+        else:
+            magmoms = np.asarray(magmoms)
+            self.set_array('magmoms', magmoms, float, magmoms.shape[1:])
+
+    def get_initial_magnetic_moments(self):
+        """Get array of initial magnetic moments."""
+        if 'magmoms' in self.arrays:
+            return self.arrays['magmoms'].copy()
+        else:
+            return np.zeros(len(self))
+
+    def get_magnetic_moments(self):
+        """Get calculated local magnetic moments."""
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        if self._calc.get_spin_polarized():
+            return self._calc.get_magnetic_moments(self)
+        else:
+            return np.zeros(len(self))
+        
+    def get_magnetic_moment(self):
+        """Get calculated total magnetic moment."""
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        if self._calc.get_spin_polarized():
+            return self._calc.get_magnetic_moment(self)
+        else:
+            return 0.0
+
+    def set_charges(self, charges):
+        """Set charges."""
+        self.set_array('charges', charges, float, ())
+
+    def get_charges(self):
+        """Get array of charges."""
+        if 'charges' in self.arrays:
+            return self.arrays['charges'].copy()
+        else:
+            return np.zeros(len(self))
+
+    def set_positions(self, newpositions):
+        """Set positions."""
+        positions = self.arrays['positions']
+        if self.constraints:
+            newpositions = np.asarray(newpositions, float)
+            for constraint in self.constraints:
+                constraint.adjust_positions(positions, newpositions)
+                
+        self.set_array('positions', newpositions, shape=(3,))
+
+    def get_positions(self):
+        """Get array of positions."""
+        return self.arrays['positions'].copy()
+
+    def get_calculation_done(self):
+        """Let the calculator calculate its thing,
+           using the current input.
+           """
+        if self.calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        self.calc.initialize(self)
+        self.calc.calculate(self)
+
+    def get_potential_energy(self):
+        """Calculate potential energy."""
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        return self._calc.get_potential_energy(self)
+
+    def get_potential_energies(self):
+        """Calculate the potential energies of all the atoms.
+
+        Only available with calculators supporting per-atom energies
+        (e.g. classical potentials).
+        """
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        return self._calc.get_potential_energies(self)
+
+    def get_kinetic_energy(self):
+        """Get the kinetic energy."""
+        momenta = self.arrays.get('momenta')
+        if momenta is None:
+            return 0.0
+        return 0.5 * np.vdot(momenta, self.get_velocities())
+
+    def get_velocities(self):
+        """Get array of velocities."""
+        momenta = self.arrays.get('momenta')
+        if momenta is None:
+            return None
+        m = self.arrays.get('masses')
+        if m is None:
+            m = atomic_masses[self.arrays['numbers']]
+        return momenta / m.reshape(-1, 1)
+
+    def get_total_energy(self):
+        """Get the total energy - potential plus kinetic energy."""
+        return self.get_potential_energy() + self.get_kinetic_energy()
+
+    def get_forces(self, apply_constraint=True):
+        """Calculate atomic forces.
+
+        Ask the attached calculator to calculate the forces and apply
+        constraints.  Use *apply_constraint=False* to get the raw
+        forces."""
+
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        forces = self._calc.get_forces(self)
+        if apply_constraint:
+            for constraint in self.constraints:
+                constraint.adjust_forces(self.arrays['positions'], forces)
+        return forces
+
+    def get_stress(self):
+        """Calculate stress tensor.
+
+        Returns an array of the six independent components of the
+        symmetric stress tensor, in the traditional order
+        (s_xx, s_yy, s_zz, s_yz, s_xz, s_xy).
+        """
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        stress = self._calc.get_stress(self)
+        shape = getattr(stress, 'shape', None)
+        if shape == (3, 3):
+            return np.array([stress[0, 0], stress[1, 1], stress[2, 2],
+                             stress[1, 2], stress[0, 2], stress[0, 1]])
+        else:
+            # Hopefully a 6-vector, but don't check in case some weird
+            # calculator does something else.
+            return stress
+    
+    def get_stresses(self):
+        """Calculate the stress-tensor of all the atoms.
+
+        Only available with calculators supporting per-atom energies and
+        stresses (e.g. classical potentials).  Even for such calculators
+        there is a certain arbitrariness in defining per-atom stresses.
+        """
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        return self._calc.get_stresses(self)
+
+    def get_dipole_moment(self):
+        """Calculate the electric dipole moment for the atoms object.
+
+        Only available for calculators which has a get_dipole_moment()
+        method."""
+        
+        if self._calc is None:
+            raise RuntimeError('Atoms object has no calculator.')
+        try:
+            dipole = self._calc.get_dipole_moment(self)
+        except AttributeError:
+            raise AttributeError(
+                'Calculator object has no get_dipole_moment method.')
+        return dipole
+    
+    def copy(self):
+        """Return a copy."""
+        import copy
+        atoms = self.__class__(cell=self._cell, pbc=self._pbc, info=self.info)
+
+        atoms.arrays = {}
+        for name, a in self.arrays.items():
+            atoms.arrays[name] = a.copy()
+        atoms.constraints = copy.deepcopy(self.constraints)
+        atoms.adsorbate_info = copy.deepcopy(self.adsorbate_info)
+        return atoms
+
+    def __len__(self):
+        return len(self.arrays['positions'])
+
+    def get_number_of_atoms(self):
+        """Returns the number of atoms.
+
+        Equivalent to len(atoms) in the standard ASE Atoms class.
+        """
+        return len(self)
+    
+    def __repr__(self):
+        num = self.get_atomic_numbers()
+        N = len(num)
+        if N == 0:
+            symbols = ''
+        elif N <= 60:
+            symbols = self.get_chemical_symbols(reduce=True)
+        else:
+            symbols = ''.join([chemical_symbols[Z] for Z in num[:15]]) + '...'
+        s = "%s(symbols='%s', " % (self.__class__.__name__, symbols)
+        for name in self.arrays:
+            if name == 'numbers':
+                continue
+            s += '%s=..., ' % name
+        if (self._cell - np.diag(self._cell.diagonal())).any():
+            s += 'cell=%s, ' % self._cell.tolist()            
+        else:
+            s += 'cell=%s, ' % self._cell.diagonal().tolist()
+        s += 'pbc=%s, ' % self._pbc.tolist()
+        if len(self.constraints) == 1:
+            s += 'constraint=%s, ' % repr(self.constraints[0])
+        if len(self.constraints) > 1:
+            s += 'constraint=%s, ' % repr(self.constraints)
+        if self._calc is not None:
+            s += 'calculator=%s(...), ' % self._calc.__class__.__name__
+        return s[:-2] + ')'
+
+    def __add__(self, other):
+        atoms = self.copy()
+        atoms += other
+        return atoms
+
+    def extend(self, other):
+        """Extend atoms object by appending atoms from *other*."""
+        if isinstance(other, Atom):
+            other = self.__class__([other])
+            
+        n1 = len(self)
+        n2 = len(other)
+        
+        for name, a1 in self.arrays.items():
+            a = np.zeros((n1 + n2,) + a1.shape[1:], a1.dtype)
+            a[:n1] = a1
+            a2 = other.arrays.get(name)
+            if a2 is not None:
+                a[n1:] = a2
+            self.arrays[name] = a
+
+        for name, a2 in other.arrays.items():
+            if name in self.arrays:
+                continue
+            a = np.empty((n1 + n2,) + a2.shape[1:], a2.dtype)
+            a[n1:] = a2
+            if name == 'masses':
+                a[:n1] = self.get_masses()
+            else:
+                a[:n1] = 0
+
+            self.set_array(name, a)
+
+        return self
+
+    __iadd__ = extend
+
+    def append(self, atom):
+        """Append atom to end."""
+        self.extend(self.__class__([atom]))
+
+    def __getitem__(self, i):
+        """Return a subset of the atoms.
+
+        i -- scalar integer, list of integers, or slice object
+        describing which atoms to return.
+
+        If i is a scalar, return an Atom object. If i is a list or a
+        slice, return an Atoms object with the same cell, pbc, and
+        other associated info as the original Atoms object. The
+        indices of the constraints will be shuffled so that they match
+        the indexing in the subset returned.
+
+        """
+        if isinstance(i, int):
+            natoms = len(self)
+            if i < -natoms or i >= natoms:
+                raise IndexError('Index out of range.')
+
+            return Atom(atoms=self, index=i)
+        
+        import copy
+        from ase.constraints import FixConstraint
+        
+        atoms = self.__class__(cell=self._cell, pbc=self._pbc, info=self.info)
+        # TODO: Do we need to shuffle indices in adsorbate_info too?
+        atoms.adsorbate_info = self.adsorbate_info
+        
+        atoms.arrays = {}
+        for name, a in self.arrays.items():
+            atoms.arrays[name] = a[i].copy()
+        
+        # Constraints need to be deepcopied, since we need to shuffle
+        # the indices
+        atoms.constraints = copy.deepcopy(self.constraints)
+        condel = []
+        for con in atoms.constraints:
+            if isinstance(con, FixConstraint):
+                try:
+                    con.index_shuffle(i)
+                except IndexError:
+                    condel.append(con)
+        for con in condel:
+            atoms.constraints.remove(con)
+        return atoms
+
+    def __delitem__(self, i):
+        from ase.constraints import FixAtoms
+        check_constraint = np.array([isinstance(c, FixAtoms)
+                                     for c in self._constraints])
+        if len(self._constraints) > 0 and not check_constraint.all():
+            raise RuntimeError('Remove constraint using set_constraint() ' +
+                               'before deleting atoms.')
+        mask = np.ones(len(self), bool)
+        mask[i] = False
+        for name, a in self.arrays.items():
+            self.arrays[name] = a[mask]
+        if len(self._constraints) > 0:
+            for n in range(len(self._constraints)):
+                self._constraints[n].delete_atom(range(len(mask))[i])
+
+    def pop(self, i=-1):
+        """Remove and return atom at index *i* (default last)."""
+        atom = self[i]
+        atom.cut_reference_to_atoms()
+        del self[i]
+        return atom
+    
+    def __imul__(self, m):
+        """In-place repeat of atoms."""
+        if isinstance(m, int):
+            m = (m, m, m)
+
+        M = np.product(m)
+        n = len(self)
+        
+        for name, a in self.arrays.items():
+            self.arrays[name] = np.tile(a, (M,) + (1,) * (len(a.shape) - 1))
+
+        positions = self.arrays['positions']
+        i0 = 0
+        for m0 in range(m[0]):
+            for m1 in range(m[1]):
+                for m2 in range(m[2]):
+                    i1 = i0 + n
+                    positions[i0:i1] += np.dot((m0, m1, m2), self._cell)
+                    i0 = i1
+
+        if self.constraints is not None:
+            self.constraints = [c.repeat(m, n) for c in self.constraints]
+
+        self._cell = np.array([m[c] * self._cell[c] for c in range(3)])
+
+        return self
+
+    def repeat(self, rep):
+        """Create new repeated atoms object.
+
+        The *rep* argument should be a sequence of three positive
+        integers like *(2,3,1)* or a single integer (*r*) equivalent
+        to *(r,r,r)*."""
+
+        atoms = self.copy()
+        atoms *= rep
+        return atoms
+
+    __mul__ = repeat
+    
+    def translate(self, displacement):
+        """Translate atomic positions.
+
+        The displacement argument can be a float an xyz vector or an
+        nx3 array (where n is the number of atoms)."""
+
+        self.arrays['positions'] += np.array(displacement)
+
+    def center(self, vacuum=None, axis=None):
+        """Center atoms in unit cell.
+
+        Centers the atoms in the unit cell, so there is the same
+        amount of vacuum on all sides.
+
+        Parameters:
+
+        vacuum (default: None): If specified adjust the amount of
+        vacuum when centering.  If vacuum=10.0 there will thus be 10
+        Angstrom of vacuum on each side.
+
+        axis (default: None): If specified, only act on the specified
+        axis.  Default: Act on all axes.
+        """
+        # Find the orientations of the faces of the unit cell
+        c = self.get_cell()
+        dirs = np.zeros_like(c)
+        for i in range(3):
+            dirs[i] = np.cross(c[i - 1], c[i - 2])
+            dirs[i] /= np.sqrt(np.dot(dirs[i], dirs[i]))  # normalize
+            if np.dot(dirs[i], c[i]) < 0.0:
+                dirs[i] *= -1
+
+        # Now, decide how much each basis vector should be made longer
+        if axis is None:
+            axes = (0, 1, 2)
+        else:
+            axes = (axis,)
+        p = self.arrays['positions']
+        longer = np.zeros(3)
+        shift = np.zeros(3)
+        for i in axes:
+            p0 = np.dot(p, dirs[i]).min()
+            p1 = np.dot(p, dirs[i]).max()
+            height = np.dot(c[i], dirs[i])
+            if vacuum is not None:
+                lng = (p1 - p0 + 2 * vacuum) - height
+            else:
+                lng = 0.0  # Do not change unit cell size!
+            top = lng + height - p1
+            shf = 0.5 * (top - p0)
+            cosphi = np.dot(c[i], dirs[i]) / np.sqrt(np.dot(c[i], c[i]))
+            longer[i] = lng / cosphi
+            shift[i] = shf / cosphi
+
+        # Now, do it!
+        translation = np.zeros(3)
+        for i in axes:
+            nowlen = np.sqrt(np.dot(c[i], c[i]))
+            self._cell[i] *= 1 + longer[i] / nowlen
+            translation += shift[i] * c[i] / nowlen
+        self.arrays['positions'] += translation
+
+    def get_center_of_mass(self, scaled=False):
+        """Get the center of mass.
+
+        If scaled=True the center of mass in scaled coordinates
+        is returned."""
+        m = self.arrays.get('masses')
+        if m is None:
+            m = atomic_masses[self.arrays['numbers']]
+        com = np.dot(m, self.arrays['positions']) / m.sum()
+        if scaled:
+            return np.linalg.solve(self._cell.T, com)
+        else:
+            return com
+
+    def get_moments_of_inertia(self, vectors=False):
+        """Get the moments of inertia along the principal axes.
+
+        The three principal moments of inertia are computed from the
+        eigenvalues of the symmetric inertial tensor. Periodic boundary
+        conditions are ignored. Units of the moments of inertia are
+        amu*angstrom**2.
+        """
+        com = self.get_center_of_mass()
+        positions = self.get_positions()
+        positions -= com  # translate center of mass to origin
+        masses = self.get_masses()
+
+        #initialize elements of the inertial tensor
+        I11 = I22 = I33 = I12 = I13 = I23 = 0.0
+        for i in range(len(self)):
+            x, y, z = positions[i]
+            m = masses[i]
+
+            I11 += m * (y**2 + z**2)
+            I22 += m * (x**2 + z**2)
+            I33 += m * (x**2 + y**2)
+            I12 += -m * x * y
+            I13 += -m * x * z
+            I23 += -m * y * z
+
+        I = np.array([[I11, I12, I13],
+                      [I12, I22, I23],
+                      [I13, I23, I33]])
+
+        evals, evecs = np.linalg.eigh(I)
+        if vectors:
+            return evals, evecs.transpose()
+        else:
+            return evals
+
+    def get_angular_momentum(self):
+        """Get total angular momentum with respect to the center of mass."""
+        com = self.get_center_of_mass()
+        positions = self.get_positions()
+        positions -= com  # translate center of mass to origin
+        return np.cross(positions, self.get_momenta()).sum(0)
+
+    def rotate(self, v, a=None, center=(0, 0, 0), rotate_cell=False):
+        """Rotate atoms.
+
+        Rotate the angle *a* around the vector *v*.  If *a* is not
+        given, the length of *v* is used as the angle.  If *a* is a
+        vector, then *v* is rotated into *a*.  The point at *center*
+        is fixed.  Use *center='COM'* to fix the center of mass.
+        Vectors can also be strings: 'x', '-x', 'y', ... .
+
+        Examples:
+
+        Rotate 90 degrees around the z-axis, so that the x-axis is
+        rotated into the y-axis:
+
+        >>> a = pi / 2
+        >>> atoms.rotate('z', a)
+        >>> atoms.rotate((0, 0, 1), a)
+        >>> atoms.rotate('-z', -a)
+        >>> atoms.rotate((0, 0, a))
+        >>> atoms.rotate('x', 'y')
+        """
+
+        norm = np.linalg.norm
+        v = string2vector(v)
+        if a is None:
+            a = norm(v)
+        if isinstance(a, (float, int)):
+            v /= norm(v)
+            c = cos(a)
+            s = sin(a)
+        else:
+            v2 = string2vector(a)
+            v /= norm(v)
+            v2 /= norm(v2)
+            c = np.dot(v, v2)
+            v = np.cross(v, v2)
+            s = norm(v)
+            # In case *v* and *a* are parallel, np.cross(v, v2) vanish
+            # and can't be used as a rotation axis. However, in this
+            # case any rotation axis perpendicular to v2 will do.
+            eps = 1e-7
+            if s < eps:
+                v = np.cross((0, 0, 1), v2)
+            if norm(v) < eps:
+                v = np.cross((1, 0, 0), v2)
+            assert norm(v) >= eps
+            if s > 0:
+                v /= s
+        
+        if isinstance(center, str) and center.lower() == 'com':
+            center = self.get_center_of_mass()
+
+        p = self.arrays['positions'] - center
+        self.arrays['positions'][:] = (c * p - 
+                                       np.cross(p, s * v) + 
+                                       np.outer(np.dot(p, v), (1.0 - c) * v) +
+                                       center)
+        if rotate_cell:
+            rotcell = self.get_cell()
+            rotcell[:] = (c * rotcell - 
+                          np.cross(rotcell, s * v) + 
+                          np.outer(np.dot(rotcell, v), (1.0 - c) * v))
+            self.set_cell(rotcell)
+                
+    def rotate_euler(self, center=(0, 0, 0), phi=0.0, theta=0.0, psi=0.0):
+        """Rotate atoms via Euler angles.
+        
+        See e.g http://mathworld.wolfram.com/EulerAngles.html for explanation.
+        
+        Parameters:
+        
+        center :
+            The point to rotate about. A sequence of length 3 with the
+            coordinates, or 'COM' to select the center of mass.
+        phi :
+            The 1st rotation angle around the z axis.
+        theta :
+            Rotation around the x axis.
+        psi :
+            2nd rotation around the z axis.
+        
+        """
+        if isinstance(center, str) and center.lower() == 'com':
+            center = self.get_center_of_mass()
+        else:
+            center = np.array(center)
+        # First move the molecule to the origin In contrast to MATLAB,
+        # numpy broadcasts the smaller array to the larger row-wise,
+        # so there is no need to play with the Kronecker product.
+        rcoords = self.positions - center
+        # First Euler rotation about z in matrix form
+        D = np.array(((cos(phi), sin(phi), 0.),
+                      (-sin(phi), cos(phi), 0.),
+                      (0., 0., 1.)))
+        # Second Euler rotation about x:
+        C = np.array(((1., 0., 0.),
+                      (0., cos(theta), sin(theta)),
+                      (0., -sin(theta), cos(theta))))
+        # Third Euler rotation, 2nd rotation about z:
+        B = np.array(((cos(psi), sin(psi), 0.),
+                      (-sin(psi), cos(psi), 0.),
+                      (0., 0., 1.)))
+        # Total Euler rotation
+        A = np.dot(B, np.dot(C, D))
+        # Do the rotation
+        rcoords = np.dot(A, np.transpose(rcoords))
+        # Move back to the rotation point
+        self.positions = np.transpose(rcoords) + center
+
+    def get_dihedral(self, list):
+        """Calculate dihedral angle.
+
+        Calculate dihedral angle between the vectors list[0]->list[1]
+        and list[2]->list[3], where list contains the atomic indexes
+        in question.
+        """
+
+        # vector 0->1, 1->2, 2->3 and their normalized cross products:
+        a = self.positions[list[1]] - self.positions[list[0]]
+        b = self.positions[list[2]] - self.positions[list[1]]
+        c = self.positions[list[3]] - self.positions[list[2]]
+        bxa = np.cross(b, a)
+        bxa /= np.linalg.norm(bxa)
+        cxb = np.cross(c, b)
+        cxb /= np.linalg.norm(cxb)
+        angle = np.vdot(bxa, cxb)
+        # check for numerical trouble due to finite precision:
+        if angle < -1:
+            angle = -1
+        if angle > 1:
+            angle = 1
+        angle = np.arccos(angle)
+        if np.vdot(bxa, c) > 0:
+            angle = 2 * np.pi - angle
+        return angle
+
+    def _masked_rotate(self, center, axis, diff, mask):
+        # do rotation of subgroup by copying it to temporary atoms object
+        # and then rotating that
+        #
+        # recursive object definition might not be the most elegant thing,
+        # more generally useful might be a rotation function with a mask?
+        group = self.__class__()
+        for i in range(len(self)):
+            if mask[i]:
+                group += self[i]
+        group.translate(-center)
+        group.rotate(axis, diff)
+        group.translate(center)
+        # set positions in original atoms object
+        j = 0
+        for i in range(len(self)):
+            if mask[i]:
+                self.positions[i] = group[j].get_position()
+                j += 1
+
+    def set_dihedral(self, list, angle, mask=None):
+        """
+        set the dihedral angle between vectors list[0]->list[1] and 
+        list[2]->list[3] by changing the atom indexed by list[3]
+        if mask is not None, all the atoms described in mask 
+        (read: the entire subgroup) are moved
+        
+        example: the following defines a very crude 
+        ethane-like molecule and twists one half of it by 30 degrees.
+
+        >>> atoms = Atoms('HHCCHH', [[-1, 1, 0], [-1, -1, 0], [0, 0, 0],
+                                     [1, 0, 0], [2, 1, 0], [2, -1, 0]])
+        >>> atoms.set_dihedral([1,2,3,4],7*pi/6,mask=[0,0,0,1,1,1])
+        """
+        # if not provided, set mask to the last atom in the
+        # dihedral description
+        if mask is None:
+            mask = np.zeros(len(self))
+            mask[list[3]] = 1
+        # compute necessary in dihedral change, from current value
+        current = self.get_dihedral(list)
+        diff = angle - current
+        axis = self.positions[list[2]] - self.positions[list[1]]
+        center = self.positions[list[2]]
+        self._masked_rotate(center, axis, diff, mask)
+        
+    def rotate_dihedral(self, list, angle, mask=None):
+        """Rotate dihedral angle.
+
+        Complementing the two routines above: rotate a group by a
+        predefined dihedral angle, starting from its current
+        configuration
+        """
+        start = self.get_dihedral(list)
+        self.set_dihedral(list, angle + start, mask)
+    
+    def get_angle(self, list):
+        """Get angle formed by three atoms.
+        
+        calculate angle between the vectors list[0]->list[1] and
+        list[1]->list[2], where list contains the atomic indexes in
+        question."""
+        # normalized vector 1->0, 1->2:
+        v10 = self.positions[list[0]] - self.positions[list[1]]
+        v12 = self.positions[list[2]] - self.positions[list[1]]
+        v10 /= np.linalg.norm(v10)
+        v12 /= np.linalg.norm(v12)
+        angle = np.vdot(v10, v12)
+        angle = np.arccos(angle)
+        return angle
+
+    def set_angle(self, list, angle, mask=None):
+        """Set angle formed by three atoms.
+        
+        Sets the angle between vectors list[1]->list[0] and 
+        list[1]->list[2].
+
+        Same usage as in set_dihedral."""
+        # If not provided, set mask to the last atom in the angle description
+        if mask is None:
+            mask = np.zeros(len(self))
+            mask[list[2]] = 1
+        # Compute necessary in angle change, from current value
+        current = self.get_angle(list)
+        diff = current - angle
+        # Do rotation of subgroup by copying it to temporary atoms object and
+        # then rotating that
+        v10 = self.positions[list[0]] - self.positions[list[1]]
+        v12 = self.positions[list[2]] - self.positions[list[1]]
+        v10 /= np.linalg.norm(v10)
+        v12 /= np.linalg.norm(v12)
+        axis = np.cross(v10, v12)
+        center = self.positions[list[1]]
+        self._masked_rotate(center, axis, diff, mask)
+    
+    def rattle(self, stdev=0.001, seed=42):
+        """Randomly displace atoms.
+
+        This method adds random displacements to the atomic positions,
+        taking a possible constraint into account.  The random numbers are
+        drawn from a normal distribution of standard deviation stdev.
+
+        For a parallel calculation, it is important to use the same
+        seed on all processors!  """
+        
+        rs = np.random.RandomState(seed)
+        positions = self.arrays['positions']
+        self.set_positions(positions +
+                           rs.normal(scale=stdev, size=positions.shape))
+        
+    def get_distance(self, a0, a1, mic=False):
+        """Return distance between two atoms.
+
+        Use mic=True to use the Minimum Image Convention.
+        """
+
+        R = self.arrays['positions']
+        D = R[a1] - R[a0]
+        if mic:
+            Dr = np.linalg.solve(self._cell.T, D)
+            D = np.dot(Dr - np.round(Dr) * self._pbc, self._cell)
+        return np.linalg.norm(D)
+
+    def set_distance(self, a0, a1, distance, fix=0.5):
+        """Set the distance between two atoms.
+
+        Set the distance between atoms *a0* and *a1* to *distance*.
+        By default, the center of the two atoms will be fixed.  Use
+        *fix=0* to fix the first atom, *fix=1* to fix the second
+        atom and *fix=0.5* (default) to fix the center of the bond."""
+
+        R = self.arrays['positions']
+        D = R[a1] - R[a0]
+        x = 1.0 - distance / np.linalg.norm(D)
+        R[a0] += (x * fix) * D
+        R[a1] -= (x * (1.0 - fix)) * D
+
+    def get_scaled_positions(self):
+        """Get positions relative to unit cell.
+
+        Atoms outside the unit cell will be wrapped into the cell in
+        those directions with periodic boundary conditions so that the
+        scaled coordinates are between zero and one."""
+
+        scaled = np.linalg.solve(self._cell.T, self.arrays['positions'].T).T
+        for i in range(3):
+            if self._pbc[i]:
+                # Yes, we need to do it twice.
+                # See the scaled_positions.py test
+                scaled[:, i] %= 1.0
+                scaled[:, i] %= 1.0
+        return scaled
+
+    def set_scaled_positions(self, scaled):
+        """Set positions relative to unit cell."""
+        self.arrays['positions'][:] = np.dot(scaled, self._cell)
+
+    def get_temperature(self):
+        """Get the temperature. in Kelvin"""
+        ekin = self.get_kinetic_energy() / len(self)
+        return ekin / (1.5 * units.kB)
+
+    def get_isotropic_pressure(self, stress):
+        """Get the current calculated pressure, assume isotropic medium.
+            in Bar
+        """
+        if type(stress) == type(1.0) or type(stress) == type(1):
+            return -stress * 1e-5 / units.Pascal
+        elif stress.shape == (3, 3):
+            return (-(stress[0, 0] + stress[1, 1] + stress[2, 2]) / 3.0) * \
+                    1e-5 / units.Pascal
+        elif stress.shape == (6,):
+            return (-(stress[0] + stress[1] + stress[2]) / 3.0) * \
+                   1e-5 / units.Pascal
+        else:
+            raise ValueError('The external stress has the wrong shape.')
+
+    def __eq__(self, other):
+        """Check for identity of two atoms objects.
+
+        Identity means: same positions, atomic numbers, unit cell and
+        periodic boundary conditions."""
+        try:
+            a = self.arrays
+            b = other.arrays
+            return (len(self) == len(other) and
+                    (a['positions'] == b['positions']).all() and
+                    (a['numbers'] == b['numbers']).all() and
+                    (self._cell == other.cell).all() and
+                    (self._pbc == other.pbc).all())
+        except AttributeError:
+            return NotImplemented
+
+    def __ne__(self, other):
+        eq = self.__eq__(other)
+        if eq is NotImplemented:
+            return eq
+        else:
+            return not eq
+
+    __hash__ = None
+
+    def get_volume(self):
+        """Get volume of unit cell."""
+        return abs(np.linalg.det(self._cell))
+    
+    def _get_positions(self):
+        """Return reference to positions-array for in-place manipulations."""
+        return self.arrays['positions']
+
+    def _set_positions(self, pos):
+        """Set positions directly, bypassing constraints."""
+        self.arrays['positions'][:] = pos
+
+    positions = property(_get_positions, _set_positions,
+                         doc='Attribute for direct ' +
+                         'manipulation of the positions.')
+
+    def _get_atomic_numbers(self):
+        """Return reference to atomic numbers for in-place 
+        manipulations."""
+        return self.arrays['numbers']
+
+    numbers = property(_get_atomic_numbers, set_atomic_numbers,
+                       doc='Attribute for direct ' +
+                       'manipulation of the atomic numbers.')
+
+    def _get_cell(self):
+        """Return reference to unit cell for in-place manipulations."""
+        return self._cell
+    
+    cell = property(_get_cell, set_cell, doc='Attribute for direct ' +
+                       'manipulation of the unit cell.')
+
+    def _get_pbc(self):
+        """Return reference to pbc-flags for in-place manipulations."""
+        return self._pbc
+    
+    pbc = property(_get_pbc, set_pbc,
+                   doc='Attribute for direct manipulation ' +
+                   'of the periodic boundary condition flags.')
+
+    def get_name(self):
+        """Return a name extracted from the elements."""
+        elements = {}
+        for a in self:
+            try:
+                elements[a.symbol] += 1
+            except:
+                elements[a.symbol] = 1
+        name = ''
+        for element in elements:
+            name += element
+            if elements[element] > 1:
+                name += str(elements[element])
+        return name
+
+    def write(self, filename, format=None, **kwargs):
+        """Write yourself to a file."""
+        from ase.io import write
+        write(filename, self, format, **kwargs)
+
+    def edit(self):
+        """Modify atoms interactively through ag viewer. 
+
+        Conflicts leading to undesirable behaviour might arise
+        when matplotlib has been pre-imported with certain
+        incompatible backends and while trying to use the
+        plot feature inside the interactive ag. To circumvent,
+        please set matplotlib.use('gtk') before calling this
+        method. 
+        """
+        from ase.gui.images import Images
+        from ase.gui.gui import GUI
+        images = Images([self])
+        gui = GUI(images)
+        gui.run()
+        # use atoms returned from gui:
+        # (1) delete all currently available atoms
+        self.set_constraint()
+        for z in range(len(self)):
+            self.pop()
+        edited_atoms = gui.images.get_atoms(0)
+        # (2) extract atoms from edit session
+        self.extend(edited_atoms)
+        self.set_constraint(edited_atoms._get_constraints())
+        self.set_cell(edited_atoms.get_cell())
+        self.set_initial_magnetic_moments(edited_atoms.get_magnetic_moments())
+        self.set_tags(edited_atoms.get_tags())
+        return
+        
+
+def string2symbols(s):
+    """Convert string to list of chemical symbols."""
+    n = len(s)
+
+    if n == 0:
+        return []
+    
+    c = s[0]
+    
+    if c.isdigit():
+        i = 1
+        while i < n and s[i].isdigit():
+            i += 1
+        return int(s[:i]) * string2symbols(s[i:])
+
+    if c == '(':
+        p = 0
+        for i, c in enumerate(s):
+            if c == '(':
+                p += 1
+            elif c == ')':
+                p -= 1
+                if p == 0:
+                    break
+        j = i + 1
+        while j < n and s[j].isdigit():
+            j += 1
+        if j > i + 1:
+            m = int(s[i + 1:j])
+        else:
+            m = 1
+        return m * string2symbols(s[1:i]) + string2symbols(s[j:])
+
+    if c.isupper():
+        i = 1
+        if 1 < n and s[1].islower():
+            i += 1
+        j = i
+        while j < n and s[j].isdigit():
+            j += 1
+        if j > i:
+            m = int(s[i:j])
+        else:
+            m = 1
+        return m * [s[:i]] + string2symbols(s[j:])
+    else:
+        raise ValueError
+
+
+def symbols2numbers(symbols):
+    if isinstance(symbols, str):
+        symbols = string2symbols(symbols)
+    numbers = []
+    for s in symbols:
+        if isinstance(s, str):
+            numbers.append(atomic_numbers[s])
+        else:
+            numbers.append(s)
+    return numbers
+
+
+def string2vector(v):
+    if isinstance(v, str):
+        if v[0] == '-':
+            return -string2vector(v[1:])
+        w = np.zeros(3)
+        w['xyz'.index(v)] = 1.0
+        return w
+    return np.array(v, float)
+
+
+def default(data, dflt):
+    """Helper function for setting default values."""
+    if data is None:
+        return None
+    elif isinstance(data, (list, tuple)):
+        newdata = []
+        allnone = True
+        for x in data:
+            if x is None:
+                newdata.append(dflt)
+            else:
+                newdata.append(x)
+                allnone = False
+        if allnone:
+            return None
+        return newdata
+    else:
+        return data
diff --git a/ase/calculators/__init__.py b/ase/calculators/__init__.py
new file mode 100644
index 0000000..c475a6c
--- /dev/null
+++ b/ase/calculators/__init__.py
@@ -0,0 +1 @@
+"""Interfaces to different ASE compatible force-calculators."""
diff --git a/ase/calculators/abinit.py b/ase/calculators/abinit.py
new file mode 100644
index 0000000..6faa374
--- /dev/null
+++ b/ase/calculators/abinit.py
@@ -0,0 +1,731 @@
+"""This module defines an ASE interface to ABINIT.
+
+http://www.abinit.org/
+"""
+
+import os
+from glob import glob
+from os.path import join, isfile, islink
+
+import numpy as np
+
+from ase.data import chemical_symbols
+from ase.data import atomic_numbers
+from ase.units import Bohr, Hartree
+
+
+class Abinit:
+    """Class for doing ABINIT calculations.
+
+    The default parameters are very close to those that the ABINIT
+    Fortran code would use.  These are the exceptions::
+
+      calc = Abinit(label='abinit', xc='LDA', pulay=5, mix=0.1)
+
+    Use the set_inp method to set extra INPUT parameters::
+
+      calc.set_inp('nstep', 30)
+
+    """
+    def __init__(self, label='abinit', xc='LDA', kpts=None, nbands=None,
+                 nstep=None, width=0.04*Hartree, ecut=None, charge=0,
+                 pulay=5, mix=0.1, pps='fhi', toldfe=1.0e-6
+                 ):
+        """Construct ABINIT-calculator object.
+
+        Parameters
+        ==========
+        label: str
+            Prefix to use for filenames (label.in, label.txt, ...).
+            Default is 'abinit'.
+        xc: str
+            Exchange-correlation functional.  Must be one of LDA, PBE,
+            revPBE, RPBE.
+        kpts: list of three int
+            Monkhost-Pack sampling.
+        nbands: int
+            Number of bands.
+            For the values of occopt not equal to 0 or 2, nbands can be omitted.
+        nstep: int
+            Number of self-consistent field STEPS.
+        width: float
+            Fermi-distribution width in eV.
+            Default is 0.04 Hartree.
+        ecut: float
+            Planewave cutoff energy in eV.
+            No default.
+        charge: float
+            Total charge of the system.
+            Default is 0.
+        pulay: int
+            Number of old densities to use for Pulay mixing.
+        mix: float
+            Mixing parameter between zero and one for density mixing.
+
+        Examples
+        ========
+        Use default values:
+
+        >>> h = Atoms('H', calculator=Abinit())
+        >>> h.center(vacuum=3.0)
+        >>> e = h.get_potential_energy()
+
+        """
+
+        self.nband = nbands # called nband in abinit
+        self.nstep = nstep
+
+        if ecut is None:
+            raise ValueError('Planewave cutoff energy in eV (ecut) not set')
+
+        self.label = label#################### != out
+        self.xc = xc
+        self.kpts = kpts
+        self.width = width
+        self.ecut = ecut
+        self.charge = charge
+        self.pulay = pulay
+        self.mix = mix
+        self.pps = pps
+        self.toldfe = toldfe
+        if not pps in ['fhi', 'hgh', 'hgh.sc', 'hgh.k', 'tm', 'paw']:
+            raise ValueError('Unexpected PP identifier %s' % pps)
+
+        self.converged = False
+        self.inp = {}
+        self.n_entries_int = 20 # integer entries per line
+        self.n_entries_float = 8 # float entries per line
+
+    def update(self, atoms):
+        if (not self.converged or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        self.numbers = atoms.get_atomic_numbers().copy()
+        self.species = []
+        for a, Z in enumerate(self.numbers):
+            if Z not in self.species:
+                self.species.append(Z)
+
+        if not hasattr(self, 'spinpol'):
+            self.spinpol = atoms.get_initial_magnetic_moments().any()
+
+        if 'ABINIT_PP_PATH' in os.environ:
+            pppaths = os.environ['ABINIT_PP_PATH'].split(':')
+        else:
+            pppaths = []
+
+        self.ppp_list = []
+        if self.xc != 'LDA':
+            xcname = 'GGA'
+        else:
+            xcname = 'LDA'
+
+        for Z in self.species:
+            symbol = chemical_symbols[abs(Z)]
+            number = atomic_numbers[symbol]
+
+            pps = self.pps
+            if pps == 'fhi':
+                name = '%02d-%s.%s.fhi' % (number, symbol, xcname)
+            elif pps in ['paw']:
+                hghtemplate = '%s-%s-%s.paw' # E.g. "H-GGA-hard-uspp.paw"
+                name = hghtemplate % (symbol, xcname, '*')
+            elif pps in ['hgh.k']:
+                hghtemplate = '%s-q%s.hgh.k' # E.g. "Co-q17.hgh.k"
+                name = hghtemplate % (symbol, '*')
+            elif pps in ['tm']:
+                hghtemplate = '%d%s%s.pspnc' # E.g. "44ru.pspnc"
+                name = hghtemplate % (number, symbol.lower(), '*')
+            elif pps in ['hgh', 'hgh.sc']:
+                hghtemplate = '%d%s.%s.hgh' # E.g. "42mo.6.hgh"
+                # There might be multiple files with different valence
+                # electron counts, so we must choose between
+                # the ordinary and the semicore versions for some elements.
+                #
+                # Therefore we first use glob to get all relevant files,
+                # then pick the correct one afterwards.
+                name = hghtemplate % (number, symbol.lower(), '*')
+
+            found = False
+            for path in pppaths:
+                if (pps.startswith('paw') or
+                    pps.startswith('hgh') or
+                    pps.startswith('tm')):
+                    filenames = glob(join(path, name))
+                    if not filenames:
+                        continue
+                    assert len(filenames) in [0, 1, 2]
+                    if pps == 'paw':
+                        selector = max # Semicore or hard
+                        # warning: see download.sh in 
+                        # abinit-pseudopotentials*tar.gz for additional information!
+                        S = selector([str(os.path.split(name)[1].split('-')[2][:-4])
+                                      for name in filenames])
+                        name = hghtemplate % (symbol, xcname, S)
+                    elif pps == 'hgh':
+                        selector = min # Lowest possible valence electron count
+                        Z = selector([int(os.path.split(name)[1].split('.')[1])
+                                      for name in filenames])
+                        name = hghtemplate % (number, symbol.lower(), str(Z))
+                    elif pps == 'hgh.k':
+                        selector = min # Semicore - highest electron count
+                        Z = selector([int(os.path.split(name)[1].split('-')[1][:-6][1:])
+                                      for name in filenames])
+                        name = hghtemplate % (symbol, Z)
+                    elif pps == 'tm':
+                        selector = max # Semicore - highest electron count
+                        # currently only one version of psp per atom
+                        name = hghtemplate % (number, symbol.lower(), '')
+                    else:
+                        assert pps == 'hgh.sc'
+                        selector = max # Semicore - highest electron count
+                        Z = selector([int(os.path.split(name)[1].split('.')[1])
+                                      for name in filenames])
+                        name = hghtemplate % (number, symbol.lower(), str(Z))
+                filename = join(path, name)
+                if isfile(filename) or islink(filename):
+                    found = True
+                    self.ppp_list.append(filename)
+                    break
+            if not found:
+                raise RuntimeError('No pseudopotential for %s!' % symbol)
+
+        self.converged = False
+
+    def get_potential_energy(self, atoms, force_consistent=False):
+        self.update(atoms)
+
+        if force_consistent:
+            return self.efree
+        else:
+            # Energy extrapolated to zero Kelvin:
+            return  (self.etotal + self.efree) / 2
+
+    def get_number_of_iterations(self):
+        return self.niter
+
+    def read_number_of_iterations(self):
+        niter = None
+        for line in open(self.label + '.txt'):
+            if line.find(' At SCF step') != -1: # find the last iteration number
+                niter = int(line.split(',')[0].split()[-1].strip())
+        return niter
+
+    def get_electronic_temperature(self):
+        return self.width
+
+    def get_number_of_electrons(self):
+        return self.nelect
+
+    def read_number_of_electrons(self):
+        nelect = None
+        # only in log file!
+        for line in open(self.label + '.log'): # find last one
+            if line.find('nelect') != -1:
+                nelect = float(line.split('=')[1].strip())
+        return nelect
+
+    def get_number_of_bands(self):
+        return self.nband
+
+    def read_number_of_bands(self):
+        nband = None
+        for line in open(self.label + '.txt'): # find last one
+            if line.find('     nband') != -1: # nband, or nband1, nband*
+                nband = int(line.split()[-1].strip())
+        return nband
+
+    def get_kpts_info(self, kpt=0, spin=0, mode='eigenvalues'):
+        return self.read_kpts_info(kpt, spin, mode)
+
+    def get_k_point_weights(self):
+        return self.get_kpts_info(kpt=0, spin=0, mode='k_point_weights')
+
+    def get_bz_k_points(self):
+        raise NotImplementedError
+
+    def get_ibz_k_points(self):
+        return self.get_kpts_info(kpt=0, spin=0, mode='ibz_k_points')
+
+    def get_spin_polarized(self):
+        return self.spinpol
+
+    def get_number_of_spins(self):
+        return 1 + int(self.spinpol)
+
+    def get_magnetic_moment(self, atoms):
+        self.update(atoms)
+        return self.magnetic_moment
+
+    def read_magnetic_moment(self):
+        magmom = None
+        if not self.get_spin_polarized():
+            magmom = 0.0
+        else: # only for spinpolarized system Magnetisation is printed
+            for line in open(self.label + '.txt'):
+                if line.find('Magnetisation') != -1: # last one
+                    magmom = float(line.split('=')[-1].strip())
+        return magmom
+
+    def get_magnetic_moments(self, atoms):
+        # local magnetic moments are not available in abinit
+        # so set the total magnetic moment on the atom no. 0 and fill with 0.0
+        self.update(atoms)
+        magmoms = [0.0 for a in range(len(atoms))]
+        magmoms[0] = self.get_magnetic_moment(atoms)
+        return np.array(magmoms)
+
+    def get_fermi_level(self):
+        return self.read_fermi()
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        return self.get_kpts_info(kpt, spin, 'eigenvalues')
+
+    def get_occupations(self, kpt=0, spin=0):
+        return self.get_kpts_info(kpt, spin, 'occupations')
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress.copy()
+
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        self.write_files()
+
+        self.write_inp(atoms)
+
+        # Now, because (stupidly) abinit when it finds a name it uses nameA
+        # and when nameA exists it uses nameB, etc.
+        # we need to rename our *.txt file to *.txt.bak
+        filename = self.label + '.txt'
+        if islink(filename) or isfile(filename):
+            os.rename(filename, filename+'.bak')
+
+        if 'ABINIT_SCRIPT' in os.environ:
+            abinit = os.environ['ABINIT_SCRIPT']
+            locals = {'label': self.label}
+            execfile(abinit, {}, locals)
+            exitcode = locals['exitcode']
+        else:
+            exitcode = os.system('abinis < %s.files > %s.log' %
+                                 (self.label, self.label))
+
+        if exitcode != 0:
+            raise RuntimeError(('Abinit exited with exit code: %d.  ' +
+                                'Check %s.log for more information.') %
+                               (exitcode, self.label))
+
+        self.read()
+
+        self.converged = True
+
+    def write_files(self):
+        """Write input parameters to files-file."""
+        fh = open(self.label + '.files', 'w')
+
+        import getpass
+        #find a suitable default scratchdir (should be writeable!)
+        username=getpass.getuser()
+
+        if os.access("/scratch/"+username,os.W_OK):
+                scratch = "/scratch/"+username
+        elif os.access("/scratch/",os.W_OK):
+                scratch = "/scratch/"
+        else:
+                if os.access(os.curdir,os.W_OK):
+                        scratch = os.curdir #if no /scratch use curdir
+                else:
+                        raise IOError,"No suitable scratch directory and no write access to current dir"
+
+        fh.write('%s\n' % (self.label+'.in')) # input
+        fh.write('%s\n' % (self.label+'.txt')) # output
+        fh.write('%s\n' % (self.label+'i')) # input
+        fh.write('%s\n' % (self.label+'o')) # output
+        # scratch files
+        scratchdir = os.path.dirname(os.path.join(scratch, self.label))
+        if not os.path.exists(scratchdir):
+            os.makedirs(scratchdir)
+        fh.write('%s\n' % (os.path.join(scratch, self.label+'.abinit')))
+        # Provide the psp files
+        for ppp in self.ppp_list:
+            fh.write('%s\n' % (ppp)) # psp file path
+
+        fh.close()
+
+    def set_inp(self, key, value):
+        """Set INPUT parameter."""
+        self.inp[key] = value
+
+    def write_inp(self, atoms):
+        """Write input parameters to in-file."""
+        fh = open(self.label + '.in', 'w')
+
+        inp = {
+            #'SystemLabel': self.label,
+            #'LatticeConstant': 1.0,
+            'natom': len(atoms),
+            'charge': self.charge,
+            #'DM.UseSaveDM': self.converged,
+            #'SolutionMethod': 'diagon',
+            'npulayit': self.pulay, # default 7
+            'diemix': self.mix
+            }
+
+        if not self.nband is None:
+            inp.update({'nband': self.nband})
+
+        if not self.nstep is None:
+            inp.update({'nstep': self.nstep})
+
+        if self.ecut is not None:
+            inp['ecut'] = str(self.ecut)+' eV' # default Ha
+
+        if self.width is not None:
+            inp['tsmear'] = str(self.width)+' eV' # default Ha
+            fh.write('occopt 3 # Fermi-Dirac smearing\n')
+
+        inp['ixc'] = { # default 1
+            'LDA':     7,
+            'PBE':    11,
+            'revPBE': 14,
+            'RPBE':   15,
+            'WC':     23
+            }[self.xc]
+
+        magmoms = atoms.get_initial_magnetic_moments()
+        if magmoms.any():
+            inp['nsppol'] = 2
+            fh.write('spinat\n')
+            for n, M in enumerate(magmoms):
+                fh.write('%.14f %.14f %.14f\n' % (0, 0, M))
+        else:
+            inp['nsppol'] = 1
+
+        inp.update(self.inp)
+
+        for key, value in inp.items():
+            if value is None:
+                continue
+
+            if isinstance(value, list):
+                fh.write('%block %s\n' % key)
+                for line in value:
+                    fh.write(' '.join(['%s' % x for x in line]) + '\n')
+                fh.write('%endblock %s\n' % key)
+
+            unit = keys_with_units.get(inpify(key))
+            if unit is None:
+                fh.write('%s %s\n' % (key, value))
+            else:
+                if 'fs**2' in unit:
+                    value /= fs**2
+                elif 'fs' in unit:
+                    value /= fs
+                fh.write('%s %f %s\n' % (key, value, unit))
+
+        fh.write('#Definition of the unit cell\n')
+        fh.write('acell\n')
+        fh.write('%.14f %.14f %.14f Angstrom\n' %  (1.0, 1.0, 1.0))
+        fh.write('rprim\n')
+        for v in self.cell:
+            fh.write('%.14f %.14f %.14f\n' %  tuple(v))
+
+        fh.write('chkprim 0 # Allow non-primitive cells\n')
+
+        fh.write('#Definition of the atom types\n')
+        fh.write('ntypat %d\n' % (len(self.species)))
+        fh.write('znucl')
+        for n, Z in enumerate(self.species):
+            fh.write(' %d' % (Z))
+        fh.write('\n')
+        fh.write('#Enumerate different atomic species\n')
+        fh.write('typat')
+        fh.write('\n')
+        self.types = []
+        for Z in self.numbers:
+            for n, Zs in enumerate(self.species):
+                if Z == Zs:
+                    self.types.append(n+1)
+        for n, type in enumerate(self.types):
+            fh.write(' %d' % (type))
+            if n > 1 and ((n % self.n_entries_int) == 1):
+                fh.write('\n')
+        fh.write('\n')
+
+        fh.write('#Definition of the atoms\n')
+        fh.write('xangst\n')
+        a = 0
+        for pos, Z in zip(self.positions, self.numbers):
+            a += 1
+            fh.write('%.14f %.14f %.14f\n' %  tuple(pos))
+
+        if self.kpts is not None:
+            fh.write('kptopt 1\n')
+            fh.write('ngkpt %d %d %d\n' % tuple(self.kpts))
+            fh.write('nshiftk 1\n')
+            fh.write('shiftk\n')
+            fh.write('%.1f %.1f %.1f\n' %
+                     tuple((np.array(self.kpts) + 1) % 2 * 0.5))
+
+        fh.write('#Definition of the SCF procedure\n')
+        fh.write('toldfe %.1g\n' %  self.toldfe)
+        fh.write('chkexit 1 # abinit.exit file in the running directory terminates after the current SCF\n')
+
+        fh.close()
+
+    def read_fermi(self):
+        """Method that reads Fermi energy in Hartree from the output file
+        and returns it in eV"""
+        E_f=None
+        filename = self.label + '.txt'
+        text = open(filename).read().lower()
+        assert 'error' not in text
+        for line in iter(text.split('\n')):
+            if line.rfind('fermi (or homo) energy (hartree) =') > -1:
+                E_f = float(line.split('=')[1].strip().split()[0])
+        return E_f*Hartree
+
+    def read_kpts_info(self, kpt=0, spin=0, mode='eigenvalues'):
+        """ Returns list of last eigenvalues, occupations, kpts weights, or
+        kpts coordinates for given kpt and spin.
+        Due to the way of reading output the spins are exchanged in spin-polarized case.  """
+        # output may look like this (or without occupation entries); 8 entries per line:
+        #
+        #  Eigenvalues (hartree) for nkpt=  20  k points:
+        # kpt#   1, nband=  3, wtk=  0.01563, kpt=  0.0625  0.0625  0.0625 (reduced coord)
+        #  -0.09911   0.15393   0.15393
+        #      occupation numbers for kpt#   1
+        #   2.00000   0.00000   0.00000
+        # kpt#   2, nband=  3, wtk=  0.04688, kpt=  0.1875  0.0625  0.0625 (reduced coord)
+        # ...
+        #
+        assert mode in ['eigenvalues' , 'occupations', 'ibz_k_points', 'k_point_weights'], 'mode not in [\'eigenvalues\' , \'occupations\', \'ibz_k_points\', \'k_point_weights\']'
+        if self.get_spin_polarized():
+            spin = {0: 1, 1: 0}[spin]
+        if spin == 0:
+           spinname = ''
+        else:
+           spinname = 'SPIN UP'.lower()
+        # number of lines of eigenvalues/occupations for a kpt
+        nband = self.get_number_of_bands()
+        n_entry_lines = max(1, int((nband - 0.1)/self.n_entries_float) + 1)
+        #
+        filename = self.label + '.txt'
+        text = open(filename).read().lower()
+        assert 'error' not in text
+        lines = text.split('\n')
+        text_list = []
+        # find the begining line of last eigenvalues
+        contains_eigenvalues = 0
+        for n, line in enumerate(lines):
+            if spin == 0:
+                if line.rfind('eigenvalues (hartree) for nkpt') > -1:
+                #if line.rfind('eigenvalues (   ev  ) for nkpt') > -1: #MDTMP
+                    contains_eigenvalues = n
+            else:
+                if (line.rfind('eigenvalues (hartree) for nkpt') > -1 and
+                    line.rfind(spinname) > -1): # find the last 'SPIN UP'
+                        contains_eigenvalues = n
+        # find the end line of eigenvalues starting from contains_eigenvalues
+        text_list = [lines[contains_eigenvalues]]
+        for line in lines[contains_eigenvalues + 1:]:
+            text_list.append(line)
+            # find a blank line or eigenvalues of second spin
+            if (not line.strip() or
+                line.rfind('eigenvalues (hartree) for nkpt') > -1):
+                break
+        # remove last (blank) line
+        text_list = text_list[:-1]
+
+        assert contains_eigenvalues, 'No eigenvalues found in the output'
+
+        n_kpts = int(text_list[0].split('nkpt=')[1].strip().split()[0])
+
+        # get rid of the "eigenvalues line"
+        text_list = text_list[1:]
+
+        # join text eigenvalues description with eigenvalues
+        # or occupation numbers for kpt# with occupations
+        contains_occupations = False
+        for line in text_list:
+            if line.rfind('occupation numbers') > -1:
+                contains_occupations = True
+                break
+        if mode == 'occupations':
+            assert contains_occupations, 'No occupations found in the output'
+
+        if contains_occupations:
+            range_kpts = 2*n_kpts
+        else:
+            range_kpts = n_kpts
+        #
+        values_list = []
+        offset = 0
+        for kpt_entry in range(range_kpts):
+            full_line = ''
+            for entry_line in range(n_entry_lines+1):
+                full_line = full_line+str(text_list[offset+entry_line])
+            first_line = text_list[offset]
+            if mode == 'occupations':
+                if first_line.rfind('occupation numbers') > -1:
+                    # extract numbers
+                    full_line = [float(v) for v in full_line.split('#')[1].strip().split()[1:]]
+                    values_list.append(full_line)
+            elif mode in ['eigenvalues', 'ibz_k_points', 'k_point_weights']:
+                if first_line.rfind('reduced coord') > -1:
+                    # extract numbers
+                    if mode == 'eigenvalues':
+                        full_line = [Hartree*float(v) for v in full_line.split(')')[1].strip().split()[:]]
+                        #full_line = [float(v) for v in full_line.split(')')[1].strip().split()[:]] #MDTMP
+                    elif mode == 'ibz_k_points':
+                        full_line = [float(v) for v in full_line.split('kpt=')[1].strip().split('(')[0].split()]
+                    else:
+                        full_line = float(full_line.split('wtk=')[1].strip().split(',')[0].split()[0])
+                    values_list.append(full_line)
+            offset = offset+n_entry_lines+1
+        #
+        if mode in ['occupations', 'eigenvalues']:
+            return np.array(values_list[kpt])
+        else:
+            return np.array(values_list)
+
+    def read(self):
+        """Read results from ABINIT's text-output file."""
+        filename = self.label + '.txt'
+        text = open(filename).read().lower()
+        assert 'error' not in text
+        assert 'was not enough scf cycles to converge' not in text
+        # some consistency ckecks
+        for line in iter(text.split('\n')):
+            if line.rfind('natom  ') > -1:
+                natom = int(line.split()[-1])
+                assert natom == len(self.numbers)
+        for line in iter(text.split('\n')):
+            if line.rfind('znucl  ') > -1:
+                znucl = [float(Z) for Z in line.split()[1:]]
+                for n, Z in enumerate(self.species):
+                    assert Z == znucl[n]
+        lines = text.split('\n')
+        for n, line in enumerate(lines):
+            if line.rfind(' typat  ') > -1:
+                nlines = len(self.numbers) / self.n_entries_int
+                typat = [int(t) for t in line.split()[1:]] # first line
+                for nline in range(nlines): # remaining lines
+                    for t in lines[1 + n + nline].split()[:]:
+                        typat.append(int(t))
+        for n, t in enumerate(self.types):
+            assert t == typat[n]
+
+        lines = iter(text.split('\n'))
+        # Stress:
+        # Printed in the output in the following format [Hartree/Bohr^3]:
+        # sigma(1 1)=  4.02063464E-04  sigma(3 2)=  0.00000000E+00
+        # sigma(2 2)=  4.02063464E-04  sigma(3 1)=  0.00000000E+00
+        # sigma(3 3)=  4.02063464E-04  sigma(2 1)=  0.00000000E+00
+        for line in lines:
+            if line.rfind('cartesian components of stress tensor (hartree/bohr^3)') > -1:
+                self.stress = np.empty((3, 3))
+                for i in range(3):
+                    entries = lines.next().split()
+                    self.stress[i,i] = float(entries[2])
+                    self.stress[min(3, 4-i)-1, max(1, 2-i)-1] = float(entries[5])
+                    self.stress[max(1, 2-i)-1, min(3, 4-i)-1] = float(entries[5])
+                self.stress = self.stress*Hartree/Bohr**3
+                break
+        else:
+            raise RuntimeError
+
+        # Energy [Hartree]:
+        # Warning: Etotal could mean both electronic energy and free energy!
+        for line in iter(text.split('\n')):
+            if line.rfind('>>>>> internal e=') > -1:
+                self.etotal = float(line.split('=')[-1])*Hartree
+                for line1 in iter(text.split('\n')):
+                    if line1.rfind('>>>>>>>>> etotal=') > -1:
+                        self.efree = float(line1.split('=')[-1])*Hartree
+                        break
+                else:
+                    raise RuntimeError
+                break
+        else:
+            for line2 in iter(text.split('\n')):
+                if line2.rfind('>>>>>>>>> etotal=') > -1:
+                    self.etotal = float(line2.split('=')[-1])*Hartree
+                    self.efree = self.etotal
+                    break
+            else:
+                raise RuntimeError
+
+        # Forces:
+        for line in lines:
+            if line.rfind('cartesian forces (ev/angstrom) at end:') > -1:
+                forces = []
+                for i in range(len(self.numbers)):
+                    forces.append(np.array([float(f) for f in lines.next().split()[1:]]))
+                self.forces = np.array(forces)
+                break
+        else:
+            raise RuntimeError
+        #
+        self.nband = self.read_number_of_bands()
+        self.niter = self.read_number_of_iterations()
+        self.nelect = self.read_number_of_electrons()
+        self.magnetic_moment = self.read_magnetic_moment()
+
+def inpify(key):
+    return key.lower().replace('_', '').replace('.', '').replace('-', '')
+
+
+keys_with_units = {
+    }
+#keys_with_units = {
+#    'paoenergyshift': 'eV',
+#    'zmunitslength': 'Bohr',
+#    'zmunitsangle': 'rad',
+#    'zmforcetollength': 'eV/Ang',
+#    'zmforcetolangle': 'eV/rad',
+#    'zmmaxdispllength': 'Ang',
+#    'zmmaxdisplangle': 'rad',
+#    'ecut': 'eV',
+#    'dmenergytolerance': 'eV',
+#    'electronictemperature': 'eV',
+#    'oneta': 'eV',
+#    'onetaalpha': 'eV',
+#    'onetabeta': 'eV',
+#    'onrclwf': 'Ang',
+#    'onchemicalpotentialrc': 'Ang',
+#    'onchemicalpotentialtemperature': 'eV',
+#    'mdmaxcgdispl': 'Ang',
+#    'mdmaxforcetol': 'eV/Ang',
+#    'mdmaxstresstol': 'eV/Ang**3',
+#    'mdlengthtimestep': 'fs',
+#    'mdinitialtemperature': 'eV',
+#    'mdtargettemperature': 'eV',
+#    'mdtargetpressure': 'eV/Ang**3',
+#    'mdnosemass': 'eV*fs**2',
+#    'mdparrinellorahmanmass': 'eV*fs**2',
+#    'mdtaurelax': 'fs',
+#    'mdbulkmodulus': 'eV/Ang**3',
+#    'mdfcdispl': 'Ang',
+#    'warningminimumatomicdistance': 'Ang',
+#    'rcspatial': 'Ang',
+#    'kgridcutoff': 'Ang',
+#    'latticeconstant': 'Ang'}
+
+# shortcut function names
+Abinit.get_occupation_numbers = Abinit.get_occupations
diff --git a/ase/calculators/aims.py b/ase/calculators/aims.py
new file mode 100644
index 0000000..11dab6d
--- /dev/null
+++ b/ase/calculators/aims.py
@@ -0,0 +1,532 @@
+"""This module defines an ASE interface to FHI-aims.
+
+Felix Hanke hanke at liverpool.ac.uk
+Jonas Bjork j.bjork at liverpool.ac.uk
+"""
+
+from general import Calculator
+import os
+import sys
+from os.path import join, isfile, islink
+
+import numpy as np
+
+import ase
+
+float_keys = [
+    'charge',
+    'charge_mix_param',
+    'default_initial_moment',
+    'hartree_convergence_parameter',
+    'harmonic_length_scale',
+    'ini_linear_mix_param',
+    'ini_spin_mix_parma',
+    'initial_moment',
+    'MD_MB_init',
+    'MD_time_step',
+    'prec_mix_param',
+    'set_vacuum_level',
+    'spin_mix_param',
+]
+
+exp_keys = [
+    'sc_accuracy_eev',
+    'sc_accuracy_etot',
+    'sc_accuracy_forces',
+    'sc_accuracy_rho',
+]
+
+string_keys = [
+    'communication_type',
+    'density_update_method',
+    'KS_method',
+    'mixer',
+    'output_level',
+    'packed_matrix_format',
+    'relax_unit_cell',
+    'restart',
+    'restart_read_only',
+    'restart_write_only',
+    'spin',
+    'total_energy_method',
+    'qpe_calc',
+    'xc',
+]
+
+int_keys = [
+    'empty_states',
+    'ini_linear_mixing',
+    'max_relaxation_steps',
+    'multiplicity',
+    'n_max_pulay',   
+    'sc_iter_limit',
+    'walltime',
+]
+
+bool_keys = [
+    'collect_eigenvectors',
+    'compute_forces',
+    'compute_kinetic',
+    'compute_numerical_stress',
+    'distributed_spline_storage',
+    'evaluate_work_function',
+    'final_forces_cleaned',
+    'hessian_to_restart_geometry',
+    'load_balancing',
+    'MD_clean_rotations',
+    'MD_restart',
+    'restart_relaxations',
+    'squeeze_memory',
+    'use_density_matrix',
+    'use_dipole_correction',
+    'use_local_index',
+    'use_logsbt',
+    'vdw_correction_hirshfeld',
+]
+
+list_keys = [
+    'init_hess',
+    'k_grid',
+    'k_offset',
+    'MD_run',
+    'MD_schedule',
+    'MD_segment',
+    'mixer_threshold',
+    'occupation_type',
+    'output',
+    'preconditioner',
+    'relativistic',
+    'relax_geometry',
+]
+
+input_keys = [
+    'run_command',
+    'run_dir',
+    'species_dir',
+    'cubes',
+    'output_template',
+    'track_output',
+] 
+
+input_parameters_default = {'run_command':None,
+                            'run_dir':None,
+                            'species_dir':None,
+                            'cubes':None,
+                            'output_template':'aims',
+                            'track_output':False}
+
+class Aims(Calculator):
+    def __init__(self, **kwargs):
+        self.name = 'Aims'
+        self.float_params = {}
+        self.exp_params = {}
+        self.string_params = {}
+        self.int_params = {}
+        self.bool_params = {}
+        self.list_params = {}
+        self.input_parameters = {}
+        for key in float_keys:
+            self.float_params[key] = None
+        for key in exp_keys:
+            self.exp_params[key] = None
+        for key in string_keys:
+            self.string_params[key] = None
+        for key in int_keys:
+            self.int_params[key] = None
+        for key in bool_keys:
+            self.bool_params[key] = None
+        for key in list_keys:
+            self.list_params[key] = None
+        for key in input_keys:
+            self.input_parameters[key] = input_parameters_default[key]
+        if os.environ.has_key('AIMS_SPECIES_DIR'):
+            self.input_parameters['species_dir'] = os.environ['AIMS_SPECIES_DIR']
+        if os.environ.has_key('AIMS_COMMAND'):
+            self.input_parameters['run_command'] = os.environ['AIMS_COMMAND']
+
+        self.positions = None
+        self.atoms = None
+        self.run_counts = 0
+        self.set(**kwargs)
+
+    def set(self, **kwargs):
+        if 'control' in kwargs:
+            fname = kwargs['control']
+            from ase.io.aims import read_aims_calculator
+            file = open(fname, 'r')
+            calc_temp = None
+            while True:
+                line = file.readline()
+                if "List of parameters used to initialize the calculator:" in line:
+                   file.readline()
+                   calc_temp = read_aims_calculator(file)
+                   break
+            if calc_temp is not None:
+                self.float_params      = calc_temp.float_params
+                self.exp_params        = calc_temp.exp_params
+                self.string_params     = calc_temp.string_params
+                self.int_params        = calc_temp.int_params
+                self.bool_params       = calc_temp.bool_params
+                self.list_params       = calc_temp.list_params
+                self.input_parameters  = calc_temp.input_parameters
+            else:
+                raise TypeError("Control file to be imported can not be read by ASE = " + fname)
+        for key in kwargs:
+            if self.float_params.has_key(key):
+                self.float_params[key] = kwargs[key]
+            elif self.exp_params.has_key(key):
+                self.exp_params[key] = kwargs[key]
+            elif self.string_params.has_key(key):
+                self.string_params[key] = kwargs[key]
+            elif self.int_params.has_key(key):
+                self.int_params[key] = kwargs[key]
+            elif self.bool_params.has_key(key):
+                self.bool_params[key] = kwargs[key]
+            elif self.list_params.has_key(key):
+                self.list_params[key] = kwargs[key]
+            elif self.input_parameters.has_key(key):
+                self.input_parameters[key] = kwargs[key]
+            elif key is not 'control':
+                raise TypeError('Parameter not defined: ' + key)
+
+    def update(self, atoms):
+        if self.calculation_required(atoms,[]):
+            self.calculate(atoms)
+
+    def calculation_required(self, atoms,quantities):
+        if (self.positions is None or
+            (self.atoms != atoms) or
+            (self.atoms != self.old_atoms) or 
+            (self.float_params != self.old_float_params) or
+            (self.exp_params != self.old_exp_params) or
+            (self.string_params != self.old_string_params) or
+            (self.int_params != self.old_int_params) or
+            (self.bool_params != self.old_bool_params) or
+            (self.list_params != self.old_list_params) or
+            (self.input_parameters != self.old_input_parameters)):
+            return True
+        else:
+            return False
+
+    def calculate(self, atoms):
+        """Generate necessary files in the working directory.
+        
+        If the directory does not exist it will be created.
+
+        """
+        positions = atoms.get_positions()
+        have_lattice_vectors = atoms.get_pbc().any()        
+        have_k_grid = self.list_params['k_grid']
+        if have_lattice_vectors and not have_k_grid:
+            raise RuntimeError("Found lattice vectors but no k-grid!")
+        if not have_lattice_vectors and have_k_grid:
+            raise RuntimeError("Found k-grid but no lattice vectors!")
+        from ase.io.aims import write_aims
+        write_aims('geometry.in', atoms) 
+        self.write_control()
+        self.write_species()
+        self.run()
+        self.converged = self.read_convergence()
+        if not self.converged:
+            os.system("tail -20 "+self.out)
+            raise RuntimeError("FHI-aims did not converge!\n"+
+                               "The last lines of output are printed above "+
+                               "and should give an indication why.")
+
+        self.set_results(atoms)
+
+    def set_results(self,atoms):
+        self.read(atoms)
+        self.old_float_params = self.float_params.copy()
+        self.old_exp_params = self.exp_params.copy()
+        self.old_string_params = self.string_params.copy()
+        self.old_int_params = self.int_params.copy()
+        self.old_input_parameters = self.input_parameters.copy()
+        self.old_bool_params = self.bool_params.copy()
+        self.old_list_params = self.list_params.copy()
+        self.old_atoms = self.atoms.copy()
+
+    def run(self):
+        if self.input_parameters['track_output']:
+            self.out = self.input_parameters['output_template']+str(self.run_counts)+'.out'
+            self.run_counts += 1
+        else:
+            self.out = self.input_parameters['output_template']+'.out'            
+        if self.input_parameters['run_command']:
+            aims_command = self.input_parameters['run_command'] 
+        elif os.environ.has_key('AIMS_COMMAND'):
+            aims_command = os.environ['AIMS_COMMAND']
+        else:
+            raise RuntimeError("No specification for running FHI-aims. Aborting!")
+        aims_command = aims_command + ' >> ' 
+        if self.input_parameters['run_dir']:
+            aims_command = aims_command + self.input_parameters['run_dir'] + '/'
+        aims_command = aims_command + self.out
+        self.write_parameters('#',self.out)
+        exitcode = os.system(aims_command)
+        if exitcode != 0:
+            raise RuntimeError('FHI-aims exited with exit code: %d.  ' % exitcode)
+        if self.input_parameters['cubes'] and self.input_parameters['track_output']:
+            self.input_parameters['cubes'].move_to_base_name(self.input_parameters['output_template']+str(self.run_counts-1))
+
+    def write_parameters(self,prefix,filename):
+        output = open(filename,'w')
+        output.write(prefix+'=======================================================\n')
+        output.write(prefix+'FHI-aims file: '+filename+'\n')
+        output.write(prefix+'Created using the Atomic Simulation Environment (ASE)\n'+prefix+'\n')
+        output.write(prefix+'List of parameters used to initialize the calculator:\n')
+        output.write(prefix+'=======================================================\n')
+        for key, val in self.float_params.items():
+            if val is not None:
+                output.write('%-35s%5.6f\n' % (key, val))        
+        for key, val in self.exp_params.items():
+            if val is not None:
+                output.write('%-35s%5.2e\n' % (key, val))
+        for key, val in self.string_params.items():
+            if val is not None:
+                output.write('%-35s%s\n' % (key, val))
+        for key, val in self.int_params.items():
+            if val is not None:
+                output.write('%-35s%d\n' % (key, val))
+        for key, val in self.bool_params.items():
+            if val is not None:
+                if key == 'vdw_correction_hirshfeld' and val:
+                    output.write('%-35s\n' % (key))
+                elif val:
+                    output.write('%-35s.true.\n' % (key))
+                elif key != 'vdw_correction_hirshfeld':
+                    output.write('%-35s.false.\n' % (key))
+        for key, val in self.list_params.items():
+            if val is not None:
+                if key == 'output':
+                    if not isinstance(val,(list,tuple)): 
+                        val = [val]
+                    for output_type in val:
+                        output.write('%-35s%s\n' % (key,str(output_type)))
+                else:
+                    output.write('%-35s' % (key))
+                    if isinstance(val,str): 
+                        output.write(val)
+                    else:
+                        for sub_value in val:
+                            output.write(str(sub_value)+' ')
+                    output.write('\n')
+        for key, val in self.input_parameters.items():
+            if key is  'cubes':
+                if val:
+                    val.write(output)
+            elif val and val != input_parameters_default[key]:
+                output.write(prefix+'%-34s%s\n' % (key,val))
+        output.write(prefix+'=======================================================\n\n')
+        output.close()
+
+    def __repr__(self):
+        items =  self.float_params.items()+self.exp_params.items() \
+                +self.string_params.items()+self.int_params.items()
+        rep = 'Aims('
+        for key, val in items:
+            if val is not None:
+                rep += key+' '+str(val)+', '
+        for key, val in self.bool_params.items():
+            if val is not None:
+                if key == 'vdw_correction_hirshfeld' and val:
+                    rep += key + ', '
+                elif val:
+                    rep += key+' .true., '
+                elif key != 'vdw_correction_hirshfeld':
+                    rep += key+' .false., '
+        for key, val in self.list_params.items():
+            if val is not None:
+                if key == 'output':
+                    if not isinstance(val,(list,tuple)): 
+                        val = [val]
+                    for output_type in val:
+                        rep += key+' '+str(output_type)+', '
+                else:
+                    rep += key
+                    if isinstance(val,str): 
+                        rep += ' '+val
+                    else:
+                        for sub_value in val:
+                            rep += ' '+str(sub_value)
+                    rep += ', '
+        for key, val in self.input_parameters.items():
+            if val and val != input_parameters_default[key]:
+                rep += key+' '+val+', '
+        return rep[:-2]+')'
+        
+    def write_control(self, file = 'control.in'):
+        """Writes the control.in file."""
+        self.write_parameters('#',file)
+
+    def write_species(self, file = 'control.in'):
+        from ase.data import atomic_numbers
+
+        if not self.input_parameters['species_dir']:
+            raise RuntimeError('Missing species directory, THIS MUST BE SPECIFIED!')
+
+        control = open(file, 'a')
+        species_path = self.input_parameters['species_dir']
+        symbols = self.atoms.get_chemical_symbols()
+        symbols2 = []
+        for n, symbol in enumerate(symbols):
+            if symbol not in symbols2:
+                symbols2.append(symbol)
+        for symbol in symbols2:
+            fd = join(species_path, '%02i_%s_default' % (atomic_numbers[symbol], symbol))
+            for line in open(fd, 'r'):
+                control.write(line)
+        control.close()
+
+    def get_dipole_moment(self, atoms):
+        if self.list_params['output'] is None or 'dipole' not in self.list_params['output']:
+            raise RuntimeError('output=[\'dipole\'] has to be set.')
+        elif atoms.get_pbc().any():
+            raise RuntimeError('FHI-aims does not allow this for systems with periodic boundary conditions.')
+        self.update(atoms)
+        return self.dipole
+
+    def read_dipole(self):
+        """Method that reads the electric dipole moment from the output file."""
+
+        dipolemoment=np.zeros([1,3])
+        for line in open(self.out, 'r'):
+            if line.rfind('Total dipole moment [eAng]') > -1:
+                dipolemoment=np.array([float(f) for f in line.split()[6:10]])
+        return dipolemoment
+
+    def read_energy(self, all=None):
+        for line in open(self.out, 'r'):
+            if line.rfind('Total energy corrected') > -1:
+                E0 = float(line.split()[5])
+            elif line.rfind('Total energy uncorrected') > -1:
+                F = float(line.split()[5])
+        energy_free, energy_zero = F, E0
+        return [energy_free, energy_zero]
+
+    def read_forces(self, atoms, all=False):
+        """Method that reads forces from the output file.
+
+        If 'all' is switched on, the forces for all ionic steps
+        in the output file will be returned, in other case only the
+        forces for the last ionic configuration are returned."""
+        lines = open(self.out, 'r').readlines()
+        forces = np.zeros([len(atoms), 3])
+        for n, line in enumerate(lines):
+            if line.rfind('Total atomic forces') > -1:
+                for iatom in range(len(atoms)):
+                    data = lines[n+iatom+1].split()
+                    for iforce in range(3):
+                        forces[iatom, iforce] = float(data[2+iforce])
+        return forces
+
+    def read_stress(self):
+        lines = open(self.out, 'r').readlines()
+        stress = None
+        for n, line in enumerate(lines):
+            if line.rfind('Calculation of numerical stress completed') > -1:
+                stress = []
+                for i in [n+8,n+9,n+10]:
+                    data = lines[i].split()
+                    stress += [float(data[2]),float(data[3]),float(data[4])]
+        # rearrange in 6-component form and return
+        if stress is not None:
+            return np.array([stress[0], stress[4], stress[8], stress[5], stress[2], stress[1]])
+        else:
+            return
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress
+
+# methods that should be quickly implemented some time, haven't had time yet:
+    def read_fermi(self):
+        """Method that reads Fermi energy from output file"""
+        return
+
+    def read_magnetic_moment(self):
+        return
+
+    def read_convergence(self):
+        converged = False
+        lines = open(self.out, 'r').readlines()
+        for n, line in enumerate(lines):
+            if line.rfind('Have a nice day') > -1:
+                converged = True
+        return converged
+
+    def read_eigenvalues(self, kpt=0, spin=0):
+        return 
+
+class AimsCube:
+    """ object to ensure the output of cube files, can be attached to Aims object"""
+    def __init__(self,origin=(0,0,0),
+                 edges=[(0.1,0.0,0.0),(0.0,0.1,0.0),(0.0,0.0,0.1)],
+                 points=(50,50,50),plots=None):
+        """ parameters: 
+        origin, edges, points = same as in the FHI-aims output
+        plots: what to print, same names as in FHI-aims """
+
+        self.name   = 'AimsCube'
+        self.origin = origin
+        self.edges  = edges
+        self.points = points
+        self.plots  = plots
+         
+    def ncubes(self):
+        """returns the number of cube files to output """
+        if self.plots:
+            number = len(self.plots)
+        else:
+            number = 0
+        return number
+
+    def set(self,**kwargs):
+        """ set any of the parameters ... """
+        # NOT IMPLEMENTED AT THE MOMENT!
+
+    def move_to_base_name(self,basename):
+        """ when output tracking is on or the base namem is not standard,
+        this routine will rename add the base to the cube file output for 
+        easier tracking """
+        for plot in self.plots:
+            found = False
+            cube = plot.split()
+            if cube[0] == 'total_density' or cube[0] == 'spin_density' or cube[0] == 'delta_density':
+                found = True
+                old_name = cube[0]+'.cube'
+                new_name = basename+'.'+old_name
+            if cube[0] == 'eigenstate' or cube[0] == 'eigenstate_density':
+                found = True
+                state = int(cube[1])
+                s_state = cube[1]
+                for i in [10,100,1000,10000]:
+                    if state < i:
+                        s_state = '0'+s_state
+                old_name = cube[0]+'_'+s_state+'_spin_1.cube'
+                new_name = basename+'.'+old_name
+            if found:
+                os.system("mv "+old_name+" "+new_name)
+
+    def add_plot(self,name):
+        """ in case you forgot one ... """
+        plots += [name]
+
+    def write(self,file):
+        """ write the necessary output to the already opened control.in """
+        file.write('output cube '+self.plots[0]+'\n')
+        file.write('   cube origin ')
+        for ival in self.origin:
+            file.write(str(ival)+' ')
+        file.write('\n')
+        for i in range(3):
+            file.write('   cube edge '+str(self.points[i])+' ')
+            for ival in self.edges[i]:
+                file.write(str(ival)+' ')
+            file.write('\n')
+        if self.ncubes() > 1:
+            for i in range(self.ncubes()-1):
+                file.write('output cube '+self.plots[i+1]+'\n')
+
+                    
+                
diff --git a/ase/calculators/castep.py b/ase/calculators/castep.py
new file mode 100644
index 0000000..5e9f53e
--- /dev/null
+++ b/ase/calculators/castep.py
@@ -0,0 +1,1901 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""This module defines an interface to CASTEP for
+    use by the ASE (Webpage: http://wiki.fysik.dtu.dk/ase)
+
+Authors:
+    Max Hoffmann, max.hoffmann at ch.tum.de
+    Jörg Meyer, joerg.meyer at ch.tum.de
+"""
+
+__all__ = [
+    'Castep',
+    'CastepCell',
+    'CastepParam',
+    'create_castep_keywords']
+
+contact_email = 'max.hoffmann at ch.tum.de'
+
+
+from copy import deepcopy
+import difflib
+import numpy as np
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
+
+
+import ase
+from ase.calculators.general import Calculator
+from ase.constraints import FixCartesian
+from ase.parallel import paropen
+
+
+# Adapt import path to give local versions of castep_keywords
+# a higher priority, assuming that personal folder will be
+# standardized at ~/.ase, watch [ase-developers]
+sys.path = ['',
+    os.path.expanduser('~/.ase'),
+    os.path.join(ase.__path__[0], 'calculators')] + sys.path
+
+
+class Castep(Calculator):
+    r"""
+
+    CASTEP Interface Documentation
+
+Introduction
+============
+
+
+CASTEP_ [1]_ is a software package which uses density functional theory to
+provide a good atomic-level description of all manner of materials and
+molecules. CASTEP can give information about total energies, forces and
+stresses on an atomic system, as well as calculating optimum geometries, band
+structures, optical spectra, phonon spectra and much more. It can also perform
+molecular dynamics simulations.
+
+The CASTEP calculator interface class offers intuitive access to all CASTEP
+settings and most results. All CASTEP specific settings are accessible via
+attribute access (*i.e*. ``calc.param.keyword = ...`` or
+``calc.cell.keyword = ...``)
+
+
+Getting Started:
+================
+
+Set the environment variables appropriately for your system.
+
+>>> export CASTEP_COMMAND=' ... '
+>>> export CASTEP_PP_PATH=' ... '
+
+
+Running the Calculator
+======================
+
+The default initialization command for the CASTEP calculator is
+
+.. class:: Castep(directory='CASTEP', label='castep')
+
+To do a minimal run one only needs to set atoms, this will use all
+default settings of CASTEP, meaning LDA, singlepoint, etc..
+
+With a generated castep_keywords.py in place all options are accessible
+by inspection, *i.e.* tab-completion. This works best when using `ipython`.
+All options can be accessed via ``calc.param.<TAB>`` or ``calc.cell.<TAB>``
+and documentation is printed with ``calc.param.<keyword> ?`` or
+``calc.cell.<keyword> ?``. All options can also be set directly
+using ``calc.keyword = ...`` or ``calc.KEYWORD = ...`` or even
+``calc.KeYwOrD`` or directly as named arguments in the call to the constructor
+(*e.g.* ``Castep(task='GeometryOptimization')``).
+
+All options that go into the ``.param`` file are held in an ``CastepParam``
+instance, while all options that go into the ``.cell`` file and don't belong
+to the atoms object are held in an ``CastepCell`` instance. Each instance can
+be created individually and can be added to calculators by attribute
+assignment, *i.e.* ``calc.param = param`` or ``calc.cell = cell``.
+
+All internal variables of the calculator start with an underscore (_).
+All cell attributes that clearly belong into the atoms object are blocked.
+Setting ``calc.atoms_attribute`` (*e.g.* ``= positions``) is sent directly to
+the atoms object.
+
+
+Arguments:
+==========
+
+=========================  ====================================================
+Keyword                    Description
+=========================  ====================================================
+``directory``              The relative path where all input and output files
+                           will be placed. If this does not exist, it will be
+                           created.  Existing directories will be moved to
+                           directory-TIMESTAMP unless self._rename_existing_dir
+                           is set to false.
+
+``label``                  The prefix of .param, .cell, .castep, etc. files.
+
+=========================  ====================================================
+
+
+Additional Settings
+===================
+
+=========================  ====================================================
+Internal Setting           Description
+=========================  ====================================================
+``_castep_command``        (``=castep``): the actual shell command used to
+                           call CASTEP.
+
+``_check_checkfile``       (``=True``): this makes write_param() only
+                           write a continue or reuse statement if the
+                           addressed .check or .castep_bin file exists in the
+                           directory.
+
+``_copy_pspots``           (``=False``): if set to True the calculator will
+                           actually copy the needed pseudo-potential (\*.usp)
+                           file, usually it will only create symlinks.
+
+``_export_settings``       (``=True``): if this is set to
+                           True, all calculator internal settings shown here
+                           will be included in the .param in a comment line (#)
+                           and can be read again by merge_param. merge_param
+                           can be forced to ignore this directive using the
+                           optional argument ``ignore_internal_keys=True``.
+
+``_force_write``           (``=True``): this controls wether the \*cell and
+                           \*param will be overwritten.
+
+``_prepare_input_only``    (``=False``): If set to True, the calculator will
+                           create \*cell und \*param file but not
+                           start the calculation itself.
+                           If this is used to prepare jobs locally
+                           and run on a remote cluster it is recommended
+                           to set ``_copy_pspots = True``.
+
+``_castep_pp_path``        (``='.'``) : the place where the calculator
+                           will look for pseudo-potential files.
+
+``_rename_existing_dir``   (``=True``) : when using a new instance
+                           of the calculator, this will move directories out of
+                           the way that would be overwritten otherwise,
+                           appending a date string.
+
+``_set_atoms``             (``=False``) : setting this to True will overwrite
+                           any atoms object previously attached to the
+                           calculator when reading a \.castep file.  By de-
+                           fault, the read() function will only create a new
+                           atoms object if none has been attached and other-
+                           wise try to assign forces etc. based on the atom's
+                           positions.  ``_set_atoms=True`` could be necessary
+                           if one uses CASTEP's internal geometry optimization
+                           (``calc.param.task='GeometryOptimization'``)
+                           because then the positions get out of sync.
+                           *Warning*: this option is generally not recommended
+                           unless one knows one really needs it. There should
+                           never be any need, if CASTEP is used as a
+                           single-point calculator.
+
+``_track_output``          (``=False``) : if set to true, the interface
+                           will append a number to the label on all input
+                           and output files, where n is the number of calls
+                           to this instance. *Warning*: this setting may con-
+                           sume a lot more disk space because of the additio-
+                           nal \*check files.
+
+``_try_reuse``             (``=_track_output``) : when setting this, the in-
+                           terface will try to fetch the reuse file from the
+                           previous run even if _track_output is True. By de-
+                           fault it is equal to _track_output, but may be
+                           overridden.
+
+                           Since this behavior may not always be desirable for
+                           single-point calculations. Regular reuse for *e.g.*
+                           a geometry-optimization can be achieved by setting
+                           ``calc.param.reuse = True``.
+
+=========================  ====================================================
+
+Special features:
+=================
+
+
+``.dryrun_ok()``
+  Runs ``castep_command seed -dryrun`` in a temporary directory return True if
+  all variables initialized ok. This is a fast way to catch errors in the
+  input. Afterwards _kpoints_used is set.
+
+``.merge_param()``
+  Takes a filename or filehandler of a .param file or CastepParam instance and
+  merges it into the current calculator instance, overwriting current settings
+
+``.keyword.clear()``
+  Can be used on any option like ``calc.param.keyword.clear()`` or
+  ``calc.cell.keyword.clear()`` to return to the CASTEP default.
+
+``.initialize()``
+  Creates all needed input in the ``_directory``. This can then copied to and
+  run in a place without ASE or even python.
+
+``.set_pspot('<library>')``
+  This automatically sets the pseudo-potential for all present species to
+  *<Species>_<library>.usp*. Make sure that ``_castep_pp_path`` is set
+  correctly.
+
+``print(calc)``
+  Prints a short summary of the calculator settings and atoms.
+
+``ase.io.castep.read_seed('path-to/seed')``
+  Given you have a combination of seed.{param,cell,castep} this will return an
+  atoms object with the last ionic positions in the .castep file and all other
+  settings parsed from the .cell and .param file. If no .castep file is found
+  the positions are taken from the .cell file. The output directory will be
+  set to the same directory, only the label is preceded by 'copy_of\_'  to
+  avoid overwriting.
+
+
+Notes/Issues:
+==============
+
+* Currently *only* the FixAtoms *constraint* is fully supported for
+  reading and writing.
+
+* There is no support for the CASTEP *unit system*. Units of eV and Angstrom
+  are used throughout. In particular when converting total energies from
+  different calculators, one should check that the same CODATA_ version is
+  used for constants and conversion factors, respectively.
+
+.. _CASTEP: http://www.castep.org/
+
+.. _CODATA: http://physics.nist.gov/cuu/Constants/index.html
+
+.. [1] S. J. Clark, M. D. Segall, C. J. Pickard, P. J. Hasnip, M. J. Probert,
+       K. Refson, M. C. Payne Zeitschrift für Kristallographie 220(5-6)
+       pp.567- 570 (2005)
+       `online <http://goo.gl/tRJ7x>`_
+
+
+End CASTEP Interface Documentation
+    """
+
+    # Class attributes !
+    # keys set through atoms object
+    atoms_keys = [
+        'charge',
+        'ionic_constraints',
+        'lattice_abs',
+        'lattice_cart',
+        'positions_abs',
+        'positions_abs_final',
+        'positions_abs_intermediate',
+        'positions_frac',
+        'positions_frac_final',
+        'positions_frac_intermediate',
+        ]
+
+    atoms_obj_keys = [
+            'dipole',
+            'energy_free',
+            'energy_zero',
+            'fermi',
+            'forces',
+            'nbands',
+            'positions',
+            'stress',
+            ]
+    internal_keys = [
+            '_castep_command',
+            '_check_checkfile',
+            '_copy_pspots',
+            '_directory',
+            '_export_settings',
+            '_force_write',
+            '_label',
+            '_prepare_input_only',
+            '_castep_pp_path',
+            '_rename_existing_dir',
+            '_set_atoms',
+            '_track_output',
+            '_try_reuse',
+            ]
+
+    def __init__(self, directory='CASTEP', label='castep',
+        castep_command=None, check_castep_version=False,
+        castep_pp_path=None,
+        **kwargs):
+
+        self.__name__ = 'Castep'
+
+        # initialize the ase.calculators.general calculator
+        Calculator.__init__(self)
+
+        from ase.io.castep import write_cell
+        self._write_cell = write_cell
+
+        castep_keywords = import_castep_keywords()
+        self.param = CastepParam()
+        self.cell = CastepCell()
+
+        ###################################
+        # Calculator state variables      #
+        ###################################
+        self._calls = 0
+        self._castep_version = castep_keywords.castep_version
+
+        # collects warning from .castep files
+        self._warnings = []
+        # collects content from *.err file
+        self._error = None
+        # warnings raised by the ASE interface
+        self._interface_warnings = []
+
+        # store to check if recalculation is necessary
+        self._old_atoms = None
+        self._old_cell = None
+        self._old_param = None
+
+        ###################################
+        # Internal keys                   #
+        # Allow to tweak the behavior     #
+        ###################################
+        self._opt = {}
+        self._castep_command = get_castep_command(castep_command)
+        self._castep_pp_path = get_castep_pp_path(castep_pp_path)
+        self._check_checkfile = True
+        self._copy_pspots = False
+        self._directory = os.path.abspath(directory)
+        self._export_settings = True
+        self._force_write = True
+        self._label = label
+        self._prepare_input_only = False
+        self._rename_existing_dir = True
+        self._set_atoms = False
+        self._track_output = False
+        self._try_reuse = False
+
+        # will be set on during runtime
+        self._seed = None
+
+        ###################################
+        # (Physical) result variables     #
+        ###################################
+        self.atoms = None
+        # initialize result variables
+        self._forces = None
+        self._energy_total = None
+        self._energy_free = None
+        self._energy_0K = None
+        self._number_of_cell_constraints = None
+        self._output_verbosity = None
+        self._stress = None
+        self._unit_cell = None
+        self._kpoints = None
+
+        # pointers to other files used at runtime
+        self._check_file = None
+        self._castep_bin_file = None
+
+        # check version of CASTEP options module against current one
+        if check_castep_version:
+            local_castep_version = get_castep_version(self._castep_command)
+            if not hasattr(self, '_castep_version'):
+                print("No castep version found")
+                return
+            if not local_castep_version == self._castep_version:
+                print(('The options module was generated from version %s\n'
+                    + 'while your are currently using CASTEP version %s')
+                    % (self._castep_version,
+                      get_castep_version(self._castep_command)))
+                self._castep_version = local_castep_version
+
+        # processes optional arguments in kw style
+        for keyword, value in kwargs.iteritems():
+            self.__setattr__(keyword, value)
+
+    def _castep_find_last_record(self, castep_file):
+        """Checks wether a given castep file has a regular
+        ending message following the last banner message. If this
+        is the case, the line number of the last banner is message
+        is return, otherwise False.
+
+        returns (record_start, record_end, end_found, last_record_complete)
+        """
+        if type(castep_file) is str:
+            castep_file = paropen(castep_file, 'r')
+            file_opened = True
+        else:
+            file_opened = False
+        record_starts = []
+        while True:
+            line = castep_file.readline()
+            if 'Welcome' in line and 'CASTEP' in line:
+                record_starts = [castep_file.tell()] + record_starts
+            if not line:
+                break
+
+        if record_starts == []:
+            print("Could not find CASTEP label in result file: %s"
+                % castep_file)
+            print("Are you sure this is a .castep file?")
+            return
+
+        # search for regular end of file
+        end_found = False
+        # start to search from record beginning from the back
+        # and see if
+        record_end = -1
+        for record_nr, record_start in enumerate(record_starts):
+            castep_file.seek(record_start)
+            while True:
+                line = castep_file.readline()
+                if not line:
+                    break
+                if 'warn' in line.lower():
+                    self._warnings.append(line)
+                if 'Writing analysis data to' in line:
+                #if 'Writing model to' in line:
+                    end_found = True
+                    record_end = castep_file.tell()
+                    break
+
+            if end_found:
+                break
+
+        if file_opened:
+            castep_file.close()
+
+        if end_found:
+            # record_nr == 0 corresponds to the last record here
+            if record_nr == 0:
+                return (record_start, record_end, True, True)
+            else:
+                return (record_start, record_end, True, False)
+        else:
+            return (0, record_end, False, False)
+
+    def read(self, castep_file=None):
+        """Read a castep file into the current instance."""
+        if castep_file is None:
+            if self._castep_file:
+                castep_file = self._castep_file
+            else:
+                print('No CASTEP file specified')
+                return
+            if not os.path.exists(castep_file):
+                print('No CASTEP file found')
+
+        if self._seed is None:
+            self._seed = os.path.splitext(os.path.basename(castep_file))[0]
+
+        err_file = '%s.0001.err' % self._seed
+        if os.path.exists(err_file):
+            err_file = paropen(err_file)
+            self._error = err_file.read()
+            err_file.close()
+            # we return right-away because it might
+            # just be here from a previous run
+        # look for last result, if several CASTEP
+        # run are appended
+        out = paropen(castep_file, 'r')
+
+        record_start, record_end, end_found, _\
+            = self._castep_find_last_record(out)
+        if not end_found:
+            print("No regular end found in %s file" % castep_file)
+            print(self._error)
+            out.close()
+            return
+            # we return here, because the file has no a regular end
+
+        # now iterate over last CASTEP output in file to extract information
+        # could be generalized as well to extract trajectory from file
+        # holding several outputs
+        n_cell_const = 0
+        forces = []
+        stress = []
+        out.seek(record_start)
+        while True:
+            try:
+                line = out.readline()
+                if not line or out.tell() > record_end:
+                    break
+                elif "output verbosity" in line:
+                    iprint = int(line.split()[-1][1])
+                    if int(iprint) != 1:
+                        self.param.iprint = iprint
+                elif "Unit Cell" in line:
+                    lattice_real = []
+                    lattice_reci = []
+                    while True:
+                        line = out.readline()
+                        fields = line.split()
+                        if len(fields) == 6:
+                            break
+                    for i in range(3):
+                        lattice_real.append(map(float, fields[0:3]))
+                        lattice_reci.append(map(float, fields[3:7]))
+                        line = out.readline()
+                        fields = line.split()
+                elif "Cell Contents" in line:
+                    while True:
+                        line = out.readline()
+                        if "Total number of ions in cell" in line:
+                            n_atoms = int(line.split()[7])
+                        if "Total number of species in cell" in line:
+                            _ = int(line.split()[7])
+                        fields = line.split()
+                        if len(fields) == 0:
+                            break
+                elif "Fractional coordinates of atoms" in line:
+                    species = []
+                    positions_frac = []
+                    # positions_cart = []
+                    while True:
+                        line = out.readline()
+                        fields = line.split()
+                        if len(fields) == 7:
+                            break
+                    for n in range(n_atoms):
+                        species.append(fields[1])
+                        positions_frac.append(map(float, fields[3:6]))
+                        line = out.readline()
+                        fields = line.split()
+                elif "Files used for pseudopotentials" in line:
+                    while True:
+                        line = out.readline()
+                        if 'Pseudopotential generated on-the-fly' in line:
+                            continue
+                        fields = line.split()
+                        if (len(fields) >= 2):
+                            elem, pp_file = fields
+                            self.cell.species_pot = (elem, pp_file)
+                        else:
+                            break
+                elif "k-Points For BZ Sampling" in line:
+                # TODO: generalize for non-Monkhorst Pack case
+                # (i.e. kpoint lists) -
+                # kpoints_offset cannot be read this way and
+                # is hence always set to None
+                    while True:
+                        line = out.readline()
+                        if not line.strip():
+                            break
+                        if "MP grid size for SCF calculation" in line:
+                            #kpoints =  ' '.join(line.split()[-3:])
+                            #self.kpoints_mp_grid = kpoints
+                            #self.kpoints_mp_offset = '0. 0. 0.'
+                            # not set here anymore because otherwise
+                            # two calculator objects go out of sync
+                            # after each calculation triggering unecessary
+                            # recalculation
+                            break
+                elif "Symmetry and Constraints" in line:
+                    self.read_symops(castep_castep=out)
+                elif "Number of cell constraints" in line:
+                    n_cell_const = int(line.split()[4])
+                elif "Final energy" in line:
+                    self._energy_total = float(line.split()[-2])
+                elif "Final free energy" in line:
+                    self._energy_free = float(line.split()[-2])
+                elif "NB est. 0K energy" in line:
+                    self._energy_0K = float(line.split()[-2])
+                # remember to remove constraint labels in force components
+                # (lacking a space behind the actual floating point number in
+                # the CASTEP output)
+                elif "******************** Forces *********************"\
+                     in line or\
+                     "************** Symmetrised Forces ***************"\
+                     in line:
+                    fix = []
+                    fix_cart = []
+                    forces = []
+                    while True:
+                        line = out.readline()
+                        fields = line.split()
+                        if len(fields) == 7:
+                            break
+                    for n in range(n_atoms):
+                        consd = np.array([0, 0, 0])
+                        fxyz = [0, 0, 0]
+                        for (i, force_component) in enumerate(fields[-4:-1]):
+                            if force_component.count("(cons'd)") > 0:
+                                consd[i] = 1
+                            fxyz[i] = float(force_component.replace(
+                                "(cons'd)", ""))
+                        if consd.all():
+                            fix.append(n)
+                        elif consd.any():
+                            fix_cart.append(FixCartesian(n, consd))
+                        forces.append(fxyz)
+                        line = out.readline()
+                        fields = line.split()
+                elif "***************** Stress Tensor *****************"\
+                     in line:
+                    stress = []
+                    while True:
+                        line = out.readline()
+                        fields = line.split()
+                        if len(fields) == 6:
+                            break
+                    for n in range(3):
+                        stress.append(map(float, fields[2:5]))
+                        line = out.readline()
+                        fields = line.split()
+                elif "BFGS: starting iteration" in line \
+                   or   "BFGS: improving iteration" in line:
+                    if n_cell_const < 6:
+                        lattice_real = []
+                        lattice_reci = []
+                    species = []
+                    positions_frac = []
+                    #positions_cart = []
+                    forces = []
+                    stress = []
+                    self._stress = stress
+                elif "BFGS: Final Configuration:" in line:
+                    break
+                elif 'warn' in line.lower():
+                    self._warnings.append(line)
+            except Exception, exception:
+                print line,
+                print "|-> line triggered exception: " + str(exception)
+                raise
+        out.close()
+
+        positions_frac_atoms = np.array(positions_frac)
+        forces_atoms = np.array(forces)
+
+        if self.atoms and not self._set_atoms:
+            # compensate for internal reordering of atoms by CASTEP
+            # to check if all atoms are assigned
+            atoms_assigned = [False] * len(self.atoms)
+            positions_frac_ase = self.atoms.get_scaled_positions()
+            positions_frac_castep = (np.array(positions_frac) % 1) % 1
+            # % is necessary because CASTEP output may contain fractional
+            # coordinates > 1, which does not affect the calculation though
+            # Source: http://goo.gl/xfwri
+            # And, yes, the % needs to be done twice, see
+            # ase.atoms.Atoms.get_scaled_positions
+
+            species_castep = list(species)
+            forces_castep = np.array(forces)
+
+            tolerance = 1E-5
+            for n in range(n_atoms):
+                for m in range(n_atoms):
+                    if (np.linalg.norm(positions_frac_ase[n] \
+                        - positions_frac_castep[m], 1) < tolerance):
+                        if atoms_assigned[n]:
+                            raise UserWarning('Castep().read() tried to' + \
+                                ' assign forces  twice to the same' + \
+                                ' atom.\n Please file a bug report to %s' + \
+                                ' and attach your input files.' \
+                                % contact_email)
+                        species[n] = species_castep[m]
+                        positions_frac_atoms[n] = \
+                            np.array(positions_frac_castep[m])
+                        forces_atoms[n] = np.array(forces_castep[m])
+                        atoms_assigned[n] = True
+            if not all(atoms_assigned):
+                print('%s atoms not assigned.' % atoms_assigned.count(False))
+                print('The following list is True for all assigned atoms: %s'\
+                    % atoms_assigned)
+                print('If you are trying to read a .castep where the atom\'s')
+                print('positions have changed with respect to the atoms')
+                print('object, set calc._set_atoms = True\n')
+                print('On the other hand _set_atoms = True is not')
+                print('recommended if CASTEP is only used as a single-point')
+                print('calculator (e.g. in an ASE geometry optimzation)')
+                print('as this might cause redundant recalculations.')
+
+                raise UserWarning('Castep().read() did not assign forces' + \
+                    ' and positions to all input atoms\n' + \
+                    'If you think it should have assigned all of them,' + \
+                    ' please file a bug report with your input file(s)' + \
+                    'to\n\n\t%s' % contact_email)
+
+        else:
+            # If no atoms, object has been previously defined
+            # we define it here and set the Castep() instance as calculator.
+            # This covers the case that we simply want to open a .castep file.
+
+            # The next time around we will have an atoms object, since
+            # set_calculator also set atoms in the calculator.
+            if self.atoms:
+                constraints = self.atoms.constraints
+            else:
+                constraints = []
+            atoms = ase.atoms.Atoms(species,
+                                    cell=lattice_real,
+                                    constraint=constraints,
+                                    pbc=True,
+                                    scaled_positions=positions_frac,
+                                    )
+            atoms.set_calculator(self)
+
+        self._forces = forces_atoms
+
+        if self._warnings:
+            print("WARNING: %s contains warnings" % castep_file)
+            for warning in self._warnings:
+                print(warning)
+        # reset
+        self._warnings = []
+
+    # TODO: check that this is really backwards compatible
+    # with previous routine with this name...
+    def read_symops(self, castep_castep=None):
+        """Read all symmetry operations used from a .castep file."""
+        if castep_castep is None:
+            castep_castep = self._seed + ".castep"
+        if isinstance(castep_castep, str):
+            if not os.path.isfile(castep_castep):
+                print('Warning: CASTEP file %s not found!' % castep_castep)
+            f = paropen(castep_castep, 'a')
+            while True:
+                line = f.readline()
+                if not line:
+                    return
+                if "output verbosity" in line:
+                    iprint = line.split()[-1][1]
+                    # filter out the default
+                    if int(iprint) != 1:
+                        self.param.iprint = iprint
+                if "Symmetry and Constraints" in line:
+                    break
+        elif isinstance(castep_castep, file):
+            f = castep_castep
+        else:
+            raise TypeError('read_castep_castep_symops: castep_castep is' \
+                + 'not  of type file or str!')
+
+        if self.param.iprint is None or self.param.iprint < 2:
+            self._interface_warnings.append('Warning: No symmetry' \
+            + 'operations could be read from %s (iprint < 2).' % f.name)
+            return
+
+        while True:
+            line = f.readline()
+            if not line:
+                break
+            if "Number of symmetry operations" in line:
+                nsym = int(line.split()[5])
+                # print "nsym = %d" % nsym
+                # information about symmetry related atoms currently not read
+                symmetry_operations = []
+                for _ in range(nsym):
+                    rotation = []
+                    displacement = []
+                    while True:
+                        if "rotation" in f.readline():
+                            break
+                    for _ in range(3):
+                        line = f.readline()
+                        rotation.append(map(float, line.split()[1:4]))
+                    while True:
+                        if "displacement" in f.readline():
+                            break
+                    line = f.readline()
+                    displacement = map(float, line.split()[1:4])
+                    symop = {'rotation': rotation,
+                        'displacement': displacement}
+                    self.symmetry_ops = symop
+                self.symmetry = symmetry_operations
+                print "Symmetry operations successfully read from %s" % f.name
+                print self.cell.symmetry_ops
+                break
+
+        if isinstance(castep_castep, str):
+            f.close()
+
+#        return self.symmetry
+
+    def set_label(self, label):
+        """The label is part of each seed, which in turn is a prefix
+        in each CASTEP related file.
+        """
+        self._label = label
+
+    def set_pspot(self, pspot, elems=None, notelems=None, clear=True):
+        """Quickly set all pseudo-potentials: Usually CASTEP psp are named
+        like <Elem>_<LibraryName>.usp so this function function only expects
+        the <LibraryName>. It then clears any previous pseudopotential
+        settings apply the one with <LibraryName> for each element in the
+        atoms object. The optional elems and notelems arguments can be used
+        to exclusively assign to some species, or to exclude with notelemens.
+        """
+
+        if clear and not elems and not notelems:
+            self.cell.species_pot.clear()
+        for elem in set(self.atoms.get_chemical_symbols()):
+            if elems is not None and elem not in elems:
+                continue
+            if notelems is not None and elem in notelems:
+                continue
+            self.cell.species_pot = (elem, '%s_%s.usp' % (elem, pspot))
+
+    def get_forces(self, atoms):
+        """Run CASTEP calculation if needed and return forces."""
+        self.update(atoms)
+        return np.array(self._forces)
+
+    def get_total_energy(self, atoms):
+        """Run CASTEP calculation if needed and return total energy."""
+        self.update(atoms)
+        return self._energy_total
+
+    def get_free_energy(self, atoms):
+        """Run CASTEP calculation if needed and return free energy.
+           Only defined with smearing."""
+        self.update(atoms)
+        return self._energy_free
+
+    def get_0K_energy(self, atoms):
+        """Run CASTEP calculation if needed and return 0K energy.
+           Only defined with smearing."""
+        self.update(atoms)
+        return self._energy_0K
+
+    #here for compatability with ase/calculators/general.py
+    #but accessing only _name variables
+    def get_potential_energy(self, atoms, force_consistent=False):
+        """Return the total potential energy."""
+        self.update(atoms)
+        if force_consistent:
+            return self._energy_free
+        else:
+            if self._energy_0K is not None:
+                return self._energy_0K
+            else:
+                return self._energy_total
+
+    def get_stress(self, atoms):
+        """Return the stress."""
+        self.update(atoms)
+        return self._stress
+
+    def get_unit_cell(self, atoms):
+        """Return the unit cell."""
+        self.update(atoms)
+        return self._unit_cell
+
+    def get_kpoints(self, atoms):
+        """Return the kpoints."""
+        self.update(atoms)
+        return self._kpoints
+
+    def get_number_cell_constraints(self, atoms):
+        """Return the number of cell constraints."""
+        self.update(atoms)
+        return self._number_of_cell_constraints
+
+    def set_atoms(self, atoms):
+        """Sets the atoms for the calculator and vice versa."""
+        atoms.pbc = [True, True, True]
+        self.__dict__['atoms'] = atoms.copy()
+        self.atoms._calc = self
+
+    def update(self, atoms):
+        """Checks if atoms object or calculator changed and
+        runs calculation if so.
+        """
+        if self.calculation_required(atoms):
+            self.calculate(atoms)
+
+    def calculation_required(self, atoms, _=None):
+        """Checks wether anything changed in the atoms object or CASTEP
+        settings since the last calculation using this instance.
+        """
+        if not self.atoms == self._old_atoms:
+            return True
+        if self._old_param is None or self._old_cell is None:
+            return True
+        if not self.param._options == self._old_param._options:
+            return True
+        if not self.cell._options == self._old_cell._options:
+            return True
+        return False
+
+    def calculate(self, atoms):
+        """Write all necessary input file and call CASTEP."""
+        self.prepare_input_files(atoms, force_write=self._force_write)
+        if not self._prepare_input_only:
+            self.run()
+            self.read()
+
+    def push_oldstate(self):
+        """This function pushes the current state of the (CASTEP) Atoms object
+        onto the previous state. Or in other words after calling this function,
+        calculation_required will return False and enquiry functions just
+        report the current value, e.g. get_forces(), get_potential_energy().
+        """
+        # make a snapshot of all current input
+        # to be able to test if recalculation
+        # is necessary
+        self._old_atoms = self.atoms.copy()
+        self._old_param = deepcopy(self.param)
+        self._old_cell = deepcopy(self.cell)
+
+    def initialize(self, *args, **kwargs):
+        """Just an alias for prepar_input_files to comply with standard
+        function names in ASE.
+        """
+        self.prepare_input_files(*args, **kwargs)
+
+    def prepare_input_files(self, atoms=None, force_write=None):
+        """Only writes the input .cell and .param files and return
+        This can be useful if one quickly needs to prepare input files
+        for a cluster where no python or ASE is available. One can than
+        upload the file manually and read out the results using
+        Castep().read().
+        """
+
+        if self.param.reuse.value is None:
+            print("You have not set e.g. calc.param.reuse = True")
+            print("Reusing a previous calculation saves a lot of CPU time!\n")
+            print("The interface will make sure, that there is .check")
+            print("file before adding this statement to the .param file.\n")
+        if self.param.num_dump_cycles.value is None:
+            print("You have not set e.g. calc.param.num_dump_cycles = 0.")
+            print("This can save a lot of disk space. One only needs *wvfn*")
+            print("if electronic convergence isn't achieved in one go.\n")
+        from ase.io.castep import write_param
+
+        if atoms is None:
+            atoms = self.atoms
+        else:
+            self.atoms = atoms
+
+        if force_write is None:
+            force_write = self._force_write
+
+        # if we have new instance of the calculator,
+        # move existing results out of the way, first
+        if os.path.isdir(self._directory)\
+            and self._calls == 0 \
+            and self._rename_existing_dir:
+            if os.listdir(self._directory) == []:
+                os.rmdir(self._directory)
+            else:
+                # rename appending creation date of the directory
+                ctime = time.localtime(os.lstat(self._directory).st_ctime)
+                os.rename(self._directory, '%s.bak-%s'
+                   % (self._directory, time.strftime("%Y%m%d-%H%M%S", ctime)))
+
+        # create work directory
+        if not os.path.isdir(self._directory):
+            os.mkdir(self._directory, 0775)
+        if self._calls == 0:
+            self._fetch_pspots()
+
+        cwd = os.getcwd()
+        os.chdir(self._directory)
+
+        # if _try_reuse is requested and this
+        # is not the first run, we try to find
+        # the .check file from the previous run
+        # this is only necessary if _track_output
+        # is set to true
+        if self._try_reuse and self._calls > 0:
+            if os.path.exists(self._check_file):
+                self.param.reuse = self._check_file
+            elif os.path.exists(self._castep_bin_file):
+                self.param.reuse = self._castep_bin_file
+        self._seed = self._build_castep_seed()
+        self._check_file = '%s.check' % self._seed
+        self._castep_bin_file = '%s.castep_bin' % self._seed
+        self._castep_file = os.path.abspath('%s.castep' % self._seed)
+
+        # write out the input file
+        self._write_cell('%s.cell' % self._seed,
+            self.atoms, force_write=force_write)
+
+        if self._export_settings:
+            interface_options = self._opt
+        else:
+            interface_options = None
+        write_param('%s.param' % self._seed, self.param,
+                    check_checkfile=True,
+                    force_write=force_write,
+                    interface_options=interface_options,)
+        os.chdir(cwd)
+
+    def _build_castep_seed(self):
+        """Abstracts to construction of the final castep <seed>
+        with and without _tracking_output.
+        """
+        if self._track_output:
+            return "%s-%06d" % (self._label, self._calls)
+        else:
+            return "%s" % (self._label)
+
+    def run(self):
+        """Simply call castep. If the first .err file
+        contains text, this will be printed to the screen.
+        """
+        # change to target directory
+        cwd = os.getcwd()
+        os.chdir(self._directory)
+        self._calls += 1
+
+        # run castep itself
+        stdout, stderr = shell_stdouterr('%s %s' % (self._castep_command, self._seed))
+        if stdout:
+            print('castep call stdout:\n%s' % stdout)
+        if stderr:
+            print('castep call stderr:\n%s' % stderr)
+        self.push_oldstate()
+
+        # check for non-empty error files
+        err_file = '%s.0001.err' % self._seed
+        if os.path.exists(err_file):
+            err_file = open(err_file)
+            self._error = err_file.read()
+            err_file.close()
+        os.chdir(cwd)
+        if self._error:
+            print(self._error)
+
+    def __repr__(self):
+        """Returns generic, fast to capture representation of
+        CASTEP settings along with atoms object.
+        """
+        expr = ''
+        expr += '-----------------Atoms--------------------\n'
+        if self.atoms is not None:
+            expr += str('%20s\n' % self.atoms)
+        else:
+            expr += 'None\n'
+
+        expr += '-----------------Param keywords------------\n'
+        expr += str(self.param)
+        expr += '-----------------Cell keywords------------\n'
+        expr += str(self.cell)
+        expr += '-----------------Internal keys------------\n'
+        for key in self.internal_keys:
+            expr += '%20s : %s\n' % (key, self._opt[key])
+
+        return expr
+
+    def __getattr__(self, attr):
+        """___getattr___ gets overloaded to reroute the internal keys
+        and to be able to easily store them in in the param so that
+        they can be read in again in subsequent calls.
+        """
+        if attr in self.internal_keys:
+            return self._opt[attr]
+        if attr in ['__repr__', '__str__']:
+            raise AttributeError
+        elif attr not in self.__dict__:
+            raise AttributeError
+        else:
+            return self.__dict__[attr]
+
+    def __setattr__(self, attr, value):
+        """We overload the settattr method to make value assignment
+        as pythonic as possible. Internal values all start with _.
+        Value assigment is case insensitive!
+        """
+
+        if attr.startswith('_'):
+            # internal variables all start with _
+            # let's check first if they are close but not identical
+            # to one of the switches, that the user accesses directly
+            similars = difflib.get_close_matches(attr, self.internal_keys,
+                cutoff=0.9)
+            if attr not in self.internal_keys and similars:
+                print('Warning: You probably tried one of: %s' % similars)
+                print('but typed %s' % attr)
+            if attr in self.internal_keys:
+                self._opt[attr] = value
+                if attr == '_track_output':
+                    if value:
+                        self._try_reuse = True
+                        print('You switched _track_output on. This will')
+                        print('consume a lot of disk-space. The interface')
+                        print('also switched _try_reuse on, which will')
+                        print('try to find the last check file. Set')
+                        print('_try_reuse = False, if you need')
+                        print('really separate calculations')
+                    elif '_try_reuse' in self._opt and self._try_reuse:
+                        self._try_reuse = False
+                        print("_try_reuse is set to False, too")
+            else:
+                self.__dict__[attr] = value
+            return
+        elif attr  in ['atoms', 'cell', 'param']:
+            if value is not None:
+                if attr == 'atoms' and not isinstance(value, ase.atoms.Atoms):
+                    raise TypeError('%s is not an instance of ase.atoms.Atoms.'
+                                     % value)
+                elif attr == 'cell' and not isinstance(value, CastepCell):
+                    raise TypeError('%s is not an instance of CastepCell.'
+                                     % value)
+                elif attr == 'param' and not isinstance(value, CastepParam):
+                    raise TypeError('%s is not an instance of CastepParam.'
+                                     % value)
+            # These 3 are accepted right-away, no matter what
+            self.__dict__[attr] = value
+            return
+        elif attr in self.atoms_obj_keys:
+            # keywords which clearly belong to the atoms object are
+            # rerouted to go there
+            self.atoms.__dict__[attr] = value
+            return
+        elif attr  in self.atoms_keys:
+            # CASTEP keywords that should go into the atoms object
+            # itself are blocked
+            print(("Ignoring setings of '%s', since this has to be set\n" +
+            "through the atoms object") % attr)
+            return
+
+        attr = attr.lower()
+        if attr not in (self.cell._options.keys()\
+                      + self.param._options.keys()):
+            # what is left now should be meant to be a castep keyword
+            # so we first check if it defined, and if not offer some error
+            # correction
+            similars = difflib.get_close_matches(attr,
+                self.cell._options.keys() + self.param._options.keys())
+            if similars:
+                raise UserWarning(('Option "%s" not known! You mean "%s"?')
+                    % (attr, similars[0]))
+            else:
+                raise UserWarning('Option "%s" is not known!' % attr)
+
+        # here we know it must go into one of the component param or cell
+        # so we first determine which one
+        if attr in self.param._options.keys():
+            comp = 'param'
+        elif attr in self.cell._options.keys():
+            comp = 'cell'
+        else:
+            raise UserWarning('Programming error: could not attach ' \
+            + 'the keyword to an input file')
+
+        self.__dict__[comp].__setattr__(attr, value)
+
+    def merge_param(self, param, overwrite=True, ignore_internal_keys=False):
+        """Parse a param file and merge it into the current parameters."""
+        INT_TOKEN = 'ASE_INTERFACE'
+        if isinstance(param, CastepParam):
+            for key, option in param._options.iteritems():
+                if option.value  is not None:
+                    self.param.__setattr__(key, option.value)
+            return
+        elif type(param) is str:
+            param_file = open(param, 'r')
+        elif type(param) is file:
+            param_file = param
+        else:
+            print("The param filename is neither a string nor a filehandler")
+            return
+
+        for i, line in enumerate(param_file.readlines()):
+            line = line.strip()
+            # remove comments
+            for comment_char in ['#', ';', '!']:
+                if comment_char in line:
+                    if INT_TOKEN in line:
+                        # This block allows to read internal settings from
+                        # a *param file
+                        iline = line[line.index(INT_TOKEN) + len(INT_TOKEN):]
+                        if iline.split()[0] in self.internal_keys \
+                            and not ignore_internal_keys:
+                            value = ' '.join(iline.split()[2:])
+                            if value in ['True', 'False']:
+                                self._opt[iline.split()[0]] = eval(value)
+                            else:
+                                self._opt[iline.split()[0]] = value
+                    line = line[:line.index(comment_char)]
+            # if nothing remains
+            if not line.strip():
+                continue
+
+            line = re.sub(':', ' ', line)
+
+            if line == 'reuse':
+                self.param.reuse.value = 'default'
+                continue
+            if line == 'continuation':
+                self.param.continuation.value = 'default'
+                continue
+
+            try:
+                key, value = line.split()
+            except:
+                print("Could not parse line %s of your param file: %s"
+                    % (i, line))
+                raise UserWarning("Seems to me malformed")
+
+            if not overwrite and getattr(self.param, key).value is not None:
+                continue
+            self.__setattr__(key, value)
+
+    def dryrun_ok(self, dryrun_flag='-dryrun'):
+        """Starts a CASTEP run with the -dryrun flag [default]
+        in a temporary and check wether all variables are initialized
+        correctly. This is recommended for every bigger simulation.
+        """
+        from ase.io.castep import write_param
+
+        temp_dir = tempfile.mkdtemp()
+        curdir = os.getcwd()
+        self._fetch_pspots(temp_dir)
+        os.chdir(temp_dir)
+        self._fetch_pspots(temp_dir)
+        seed = 'dryrun'
+
+        cell_written = self._write_cell('%s.cell' % seed, self.atoms)
+        if not cell_written:
+            print "%s.cell not written - aborting dryrun" % seed
+            return
+        write_param('%s.param' % seed, self.param, )
+
+        stdouterr = shell_stdouterr(('%s %s %s' % (self._castep_command,
+                                      seed,
+                                      dryrun_flag)))
+
+        if stdouterr:
+            print(stdouterr)
+        result_file = open('%s.castep' % seed)
+
+        txt = result_file.read()
+        ok_string = r'.*DRYRUN finished.*No problems found with input files.*'
+        match = re.match(ok_string, txt, re.DOTALL)
+
+        try:
+            self._kpoints_used = int(
+                re.search(
+                    r'Number of kpoints used = *([0-9]+)', txt).group(1))
+        except:
+            print('Couldn\'t fetch number of kpoints from dryrun CASTEP file')
+
+        err_file = '%s.0001.err' % seed
+        if match is None and os.path.exists(err_file):
+            err_file = open(err_file)
+            self._error = err_file.read()
+            err_file.close()
+
+        result_file.close()
+        os.chdir(curdir)
+        shutil.rmtree(temp_dir)
+
+        # re.match return None is the string does not match
+        return match is not None
+
+    # this could go into the Atoms() class at some point...
+    def _get_number_in_species(self, at, atoms=None):
+        """Return the number of the atoms within the set of it own
+        species. If you are an ASE commiter: why not move this into
+        ase.atoms.Atoms ?"""
+        if atoms is None:
+            atoms = self.atoms
+        numbers = atoms.get_atomic_numbers()
+        n = numbers[at]
+        nis = numbers.tolist()[:at + 1].count(n)
+        return nis
+
+    def _get_absolute_number(self, species, nic, atoms=None):
+        """This is the inverse function to _get_number in species."""
+        if atoms is None:
+            atoms = self.atoms
+        ch = atoms.get_chemical_symbols()
+        ch.reverse()
+        total_nr = 0
+        assert nic > 0, 'Number in species needs to be 1 or larger'
+        while True:
+            if ch.pop() == species:
+                if nic == 1:
+                    return total_nr
+                nic -= 1
+            total_nr += 1
+
+    def _fetch_pspots(self, directory=None):
+        """Print all specified pseudo-potentials into the working directory.
+        """
+        if directory is None:
+            directory = self._directory
+        if not os.path.isdir(self._castep_pp_path):
+            print("PSPs directory %s not found" % self._castep_pp_path)
+        pspots = {}
+        if self.cell.species_pot.value is not None:
+            for line in self.cell.species_pot.value.split('\n'):
+                line = line.split()
+                if line:
+                    pspots[line[0]] = line[1]
+        for species in self.atoms.get_chemical_symbols():
+            if not pspots or species not in pspots.keys():
+                print("Warning: you have no PP specified for %s." % species)
+                print("CASTEP will now generate an on-the-fly potentials.")
+                print("For sake of numerical consistency and efficiency")
+                print("this is discouraged.")
+        if self.cell.species_pot.value:
+            for (species, pspot) in pspots.iteritems():
+                orig_pspot_file = os.path.join(self._castep_pp_path, pspot)
+                cp_pspot_file = os.path.join(directory, pspot)
+                if os.path.exists(orig_pspot_file)\
+                    and not os.path.exists(cp_pspot_file):
+                    if self._copy_pspots:
+                        shutil.copy(orig_pspot_file, directory)
+                    else:
+                        os.symlink(os.path.join(self._castep_pp_path, pspot),
+                            cp_pspot_file)
+
+
+def get_castep_version(castep_command):
+    """This returns the version number as printed in the CASTEP banner.
+    """
+    temp_dir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(temp_dir)
+    jname = 'dummy_jobname'
+    stdout, stderr = "", ""
+    try:
+        stdout, stderr = subprocess.Popen(castep_command.split()
+            + [jname, '-dryrun'],
+            stderr=subprocess.PIPE,
+            stdout=subprocess.PIPE, cwd=temp_dir).communicate()
+    except:
+        msg = ""
+        msg += "Could not determine the version of your CASTEP binary \n"
+        msg += "This usually means one of the following \n"
+        msg += "   * you don't have CASTEP installed \n"
+        msg += "   * you have not set the CASTEP_COMMAND to call it \n"
+        msg += "   * you have provided a wrong CASTEP_COMMAND. \n"
+        msg += "     Make sure it is in your PATH\n\n"
+        msg += stdout
+        msg += stderr
+        raise Exception(msg)
+    output = open('%s.castep' % jname)
+    output_txt = output.readlines()
+    output.close()
+    os.chdir(curdir)
+    shutil.rmtree(temp_dir)
+    for line in output_txt:
+        if 'CASTEP version' in line:
+            return  float(re.findall(r'(?<=CASTEP version )[0-9.]*', line)[0])
+
+
+def create_castep_keywords(castep_command, filename='castep_keywords.py',
+    force_write=True, path='.', fetch_only=None):
+    """This function allows to fetch all available keywords from stdout
+    of an installed castep binary. It furthermore collects the documentation
+    to harness the power of (ipython) inspection and type for some basic
+    type checking of input. All information is stored in two 'data-store'
+    objects that are not distributed by default to avoid breaking the license
+    of CASTEP.
+    """
+    # Takes a while ...
+    # Fetch all allowed parameters
+    # fetch_only : only fetch that many parameters (for testsuite only)
+    code = {}
+    suffixes = ['cell', 'param']
+    for suffix in suffixes:
+        code[suffix] = ''
+
+    if os.path.exists(filename) and not force_write:
+        print('CASTEP Options Module file exists.')
+        print('You can overwrite it by calling')
+        print('python castep.py -f [CASTEP_COMMAND].')
+        return False
+
+    fh = open(os.path.join(path, filename), 'w')
+    fh.write('"""This file is generated by')
+    fh.write('ase/calculators/castep.py\n')
+    fh.write('and is not distributed with ASE to avoid breaking')
+    fh.write('CASTEP copyright\n"""\n')
+    fh.write('class Opt:\n')
+    fh.write('    """"A CASTEP option"""\n')
+    fh.write("""    def __init__(self):
+        self.keyword = None
+        self.level = None
+        self.type = None
+        self.type = None
+    def clear(self):
+        \"\"\"Reset the value of the option to None again\"\"\"
+        self.value = None\n""")
+    fh.write('    def __repr__(self):\n')
+    fh.write('        expr = \'\'\n')
+    fh.write('        if self.value:\n')
+    fh.write('            expr += \'Option: %s(%s, %s):\\n%s\\n\''\
+        + '% (self.keyword, self.type, self.level, self.value)\n')
+    fh.write('        else:\n')
+    fh.write('            expr += \'Option: %s[unset]\' % self.keyword\n')
+    fh.write('            expr += \'(%s, %s)\' % (self.type, self.level)\n')
+    fh.write('        return expr\n\n')
+    fh.write("""class ComparableDict(dict):
+    \"\"\"Extends a dict to make to sets of options comparable\"\"\"
+    def __init__(self):
+        dict.__init__(self)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __eq__(self, other):
+        if not isinstance(other, ComparableDict):
+            return False
+        if set(self) - set(other):
+            return False
+        for key in sorted(self):
+            if self[key].value != other[key].value:
+                return False
+        return True\n""")
+
+    code['cell'] += '\n\nclass CastepCellDict(object):\n'
+    code['param'] += '\n\nclass CastepParamDict(object):\n'
+
+    types = []
+    levels = []
+
+    for suffix in suffixes:
+        code[suffix] += '    """A flat object that holds %s options"""\n'\
+            % suffix
+        code[suffix] += '    def __init__(self):\n'
+        code[suffix] += '        object.__init__(self)\n'
+        code[suffix] += '        self._options = ComparableDict()\n'
+    castep_version = get_castep_version(castep_command)
+
+    help_all, _ = shell_stdouterr('%s -help all' % castep_command)
+
+    # Filter out proper keywords
+    try:
+        raw_options = re.findall(r'(?<=^ )[A-Z_]+', help_all, re.MULTILINE)
+    except:
+        print('Problem parsing: %s' % help_all)
+        raise
+
+    processed_options = 0
+    for option in raw_options[:fetch_only]:
+        doc, _ = shell_stdouterr('%s -help %s' % (castep_command, option))
+
+        # Stand Back! I know regular expressions (http://xkcd.com/208/) :-)
+        match = re.match(r'(?P<before_type>.*)Type: (?P<type>[^ ]+).*' + \
+                         r'Level: (?P<level>[^ ]+)\n\s*\n' + \
+                         r'(?P<doc>.*?)(\n\s*\n|$)', doc, re.DOTALL)
+
+        if match is not None:
+            match = match.groupdict()
+            processed_options += 1
+
+            if re.findall(r'PARAMETERS keywords:\n\n None found', doc):
+                suffix = 'cell'
+            else:
+                suffix = 'param'
+
+            sys.stdout.write('.')
+            sys.stdout.flush()
+
+            code[suffix] += '        opt_obj = Opt()\n'
+
+            code[suffix] += ('        opt_obj.keyword = \'%s\'\n'
+                % option.lower())
+            if 'type' in match:
+                code[suffix] += ('        opt_obj.type = \'%s\'\n'
+                    % match['type'])
+                if match['type'] not in types:
+                    types.append(match['type'])
+            else:
+                raise Exception('Found no type for %s' % option)
+
+            if 'level' in match:
+                code[suffix] += ('        opt_obj.level = \'%s\'\n'
+                    % match['level'])
+                if match['level'] not in levels:
+                    levels.append(match['level'])
+            else:
+                raise Exception('Found no level for %s' % option)
+
+            if 'doc' in match:
+                code[suffix] += ('        opt_obj.__doc__ = """%s\n"""\n'
+                    % match['doc'])
+            else:
+                raise Exception('Found no doc string for %s' % option)
+            code[suffix] += ('        opt_obj.value = None\n')
+
+            code[suffix] += ('        self._options[\'%s\'] = opt_obj\n\n'
+                % option.lower())
+            code[suffix] += ('        self.__dict__[\'%s\'] = opt_obj\n\n'
+                % option.lower())
+        else:
+            sys.stdout.write(doc)
+            sys.stdout.flush()
+
+            raise Exception('create_castep_keywords: Could not process %s'
+                % option)
+
+    # write classes out
+    for suffix in suffixes:
+        fh.write(code[suffix])
+
+    fh.write('types = %s\n' % types)
+    fh.write('levels = %s\n' % levels)
+    fh.write('castep_version = %s\n\n' % castep_version)
+
+    fh.close()
+
+    print('\nCASTEP v%s, fetched %s keywords'
+        % (castep_version, processed_options))
+    return True
+
+
+class CastepParam(object):
+    """CastepParam abstracts the settings that go into the .param file"""
+    def __init__(self):
+        object.__init__(self)
+        castep_keywords = import_castep_keywords()
+        castep_param_dict = castep_keywords.CastepParamDict()
+        self._options = castep_param_dict._options
+        self.__dict__.update(self._options)
+
+    def __repr__(self):
+        expr = ''
+        if filter(lambda x: x.value is not None, self._options.values()):
+            for key, option in sorted(self._options.iteritems()):
+                if option.value is not None:
+                    expr += ("%20s : %s\n" % (key, option.value))
+        else:
+            expr += 'Default\n'
+        return expr
+
+    def __setattr__(self, attr, value):
+        if attr.startswith('_'):
+            self.__dict__[attr] = value
+            return
+        if attr not in self._options.keys():
+            similars = difflib.get_close_matches(attr, self._options.keys())
+            if similars:
+                raise UserWarning(('Option "%s" not known! You mean "%s"?')
+                    % (attr, similars[0]))
+            else:
+                raise UserWarning('Option "%s" is not known!' % attr)
+        attr = attr.lower()
+        opt = self._options[attr]
+        if not opt.type == 'Block' and type(value) is str:
+            value = value.replace(':', ' ')
+        if opt.type in ['Boolean', 'Defined']:
+            if False:
+                pass
+            else:
+                try:
+                    value = bool(eval(str(value).title()))
+                except:
+                    raise ConversionError('bool', attr, value)
+                self._options[attr].value = value
+        elif opt.type == 'String':
+            if attr == 'reuse':
+                if self._options['continuation'].value:
+                    print('Cannot set reuse if continuation is set, and')
+                    print('vice versa. Set the other to None, if you want')
+                    print('this setting.')
+                else:
+                    if value is True:
+                        self._options['reuse'].value = 'default'
+                    else:
+                        self._options['reuse'].value = str(value)
+            elif attr == 'continuation':
+                if self._options['reuse'].value:
+                    print('Cannot set continuation if reuse is set, and')
+                    print('vice versa. Set the other to None, if you want')
+                    print('this setting.')
+                else:
+                    if value is True:
+                        self._options['continuation'].value = 'default'
+                    else:
+                        self._options['continuation'].value = str(value)
+            else:
+                try:
+                    value = str(value)
+                except:
+                    raise ConversionError('str', attr, value)
+                self._options[attr].value = value
+        elif opt.type == 'Integer':
+            if False:
+                pass
+            else:
+                try:
+                    value = int(value)
+                except:
+                    raise ConversionError('int', attr, value)
+                self._options[attr].value = value
+        elif opt.type in ['Real', 'Physical']:
+            # Usage of the CASTEP unit system is not implemented for now.
+            # We assume, that the user is happy with setting/getting the
+            # CASTEP default units refer to http://goo.gl/bqYf2
+            # page 13, accessed Apr 6, 2011
+            try:
+                value = float(value)
+            except:
+                raise ConversionError('float', attr, value)
+            self._options[attr].value = value
+        # So far there is no block type in .param
+        else:
+            raise RuntimeError("Caught unhandled option: %s = %s"
+                % (attr, value))
+
+
+class CastepCell(object):
+    """CastepCell abstracts all setting that go into the .cell file"""
+    def __init__(self):
+        object.__init__(self)
+        castep_keywords = import_castep_keywords()
+        castep_cell_dict = castep_keywords.CastepCellDict()
+        self._options = castep_cell_dict._options
+        self.__dict__.update(self._options)
+
+    def __repr__(self):
+        expr = ''
+        if filter(lambda x: x.value is not None, self._options.values()):
+            for key, option in sorted(self._options.iteritems()):
+                if option.value is not None:
+                    expr += ("%20s : %s\n" % (key, option.value))
+        else:
+            expr += 'Default\n'
+
+        return expr
+
+    def __setattr__(self, attr, value):
+        if attr.startswith('_'):
+            self.__dict__[attr] = value
+            return
+
+        if attr not in self._options.keys():
+            similars = difflib.get_close_matches(attr, self._options.keys())
+            if similars:
+                raise UserWarning(('Option "%s" not known! You mean "%s"?')
+                    % (attr, similars[0]))
+            else:
+                raise UserWarning('Option "%s" is not known!' % attr)
+            return
+        attr = attr.lower()
+        opt = self._options[attr]
+        if not opt.type == 'Block' and type(value) is str:
+            value = value.replace(':', ' ')
+        if opt.type in ['Boolean', 'Defined']:
+            try:
+                value = bool(eval(str(value).title()))
+            except:
+                raise ConversionError('bool', attr, value)
+            self._options[attr].value = value
+        elif opt.type == 'String':
+            if False:
+                pass
+            else:
+                try:
+                    value = str(value)
+                except:
+                    raise ConversionError('str', attr, value)
+            self._options[attr].value = value
+        elif opt.type == 'Integer':
+            if attr == 'kpoint_mp_grid':
+                opt = self._options['kpoints_mp_grid']
+            if attr in ['kpoints_mp_grid', 'kpoint_mp_grid']:
+                if ',' in value:
+                    value = value.replace(',', ' ')
+                if type(value) is str and len(value.split()) == 3:
+                    try:
+                        _ = [int(x) for x in value.split()]
+                    except:
+                        raise ConversionError('int', attr, value)
+                    opt.value = value
+                else:
+                    print('Wrong format for kpoints_mp_grid: expected R R R')
+                    print('and you said %s' % value)
+            else:
+                try:
+                    value = int(value)
+                except:
+                    raise ConversionError('int', attr, value)
+                self._options[attr].value = value
+        elif opt.type in ['Real', 'Physical']:
+            if attr == 'kpoint_mp_offset':
+                opt = self._options['kpoints_mp_offset']
+            if attr in ['kpoints_mp_offset', 'kpoint_mp_offset']:
+                if type(value) is str and len(value.split()) == 3:
+                    try:
+                        _ = [float(x) for x in value.split()]
+                    except:
+                        raise ConversionError('float', attr, value)
+                    opt.value = value
+            else:
+                try:
+                    value = float(value)
+                except:
+                    raise ConversionError('float', attr, value)
+            self._options[attr].value = value
+        elif opt.type == 'Block':
+            if attr == 'species_pot':
+                if type(value) is not tuple \
+                    or len(value) != 2:
+                    print("Please specify pseudopotentials in python as")
+                    print("a tuple, like:")
+                    print("(species, file), e.g. ('O', 'path-to/O_OTFG.usp')")
+                    print("Anything else will be ignored")
+                else:
+                    if self.__dict__['species_pot'].value is None:
+                        self.__dict__['species_pot'].value = ''
+                    self.__dict__['species_pot'].value = \
+                        re.sub(r'\n?\s*%s\s+.*' % value[0], '',
+                            self.__dict__['species_pot'].value)
+                    if value[1]:
+                        self.__dict__['species_pot'].value += '\n%s %s' \
+                                                                % value
+
+                    # now sort lines as to match the CASTEP output
+                    pspots = self.__dict__['species_pot'].value.split('\n')
+                    # throw out empty lines
+                    pspots = filter(lambda x: x, pspots)
+
+                    # sort based on atomic numbers
+                    pspots.sort(key=lambda x: ase.data.atomic_numbers[
+                        x.split()[0]])
+
+                    # rejoin; the first blank-line
+                    # makes the print(calc) output look prettier
+                    self.__dict__['species_pot'].value = \
+                        '\n' + '\n'.join(pspots)
+                    return
+
+            elif attr == 'symmetry_ops':
+                if type(value) is not dict \
+                        or not 'rotation' in value \
+                        or not len(value['rotation']) == 3 \
+                        or not len(value['displacement']) == 3 \
+                        or not  'displacement' in value:
+                    print("Cannot process your symmetry_op %s" % value)
+                    print("It has statet like {'rotation':[a, b, c], ")
+                    print("                    'displacement': [x, y, z]}")
+                    return
+                if self.__dict__['symmetry_ops'].value is None:
+                    self.__dict__['symmetry_ops'].value = ''
+                n = (len(self.__dict__['symmetry_ops'].value.split('\n'))
+                      / 4) + 1
+                for i in range(3):
+                    self.__dict__['symmetry_ops'].value += \
+                        (("%9.6f " * 3 + "! rotation     %5d\n")\
+                        % (tuple(value['rotation'][i] + (n, ))))
+                self.__dict__['symmetry_ops'].value\
+                    += (("%9.6f " * 3 + "! displacement %5d \n")\
+                    % (tuple(value['displacement'] + (n, ))))
+            elif attr in ['positions_abs_intermediate',
+                          'positions_abs_product']:
+                if not isinstance(value, ase.atoms.Atoms):
+                    raise UserWarning('castep.cell.%s expects Atoms object'
+                        % attr)
+                target = self.__dict__[attr]
+                target.value = ''
+                for elem, pos in zip(value.get_chemical_symbols(),
+                                     value.get_positions()):
+                    target.value += ('%4s %9.6f %9.6f %9.6f\n' % (elem,
+                                                                  pos[0],
+                                                                  pos[1],
+                                                                  pos[2]))
+                return
+            elif attr in ['cell_constraints']:
+                # put block type options here, that don't need special care
+                try:
+                    value = str(value)
+                except:
+                    raise ConversionError('str', attr, value)
+            else:
+                print('Not implemented')
+                print('The option %s is of block type, which usually' % attr)
+                print('needs some special care to get the formattings right.')
+                print('Please feel free to add it and send the')
+                print('patch to %s, so we can all benefit.' % contact_email)
+                raise
+            self._options[attr].value = value
+        else:
+            raise RuntimeError('Caught unhandled option: %s = %s'
+                % (attr, value))
+
+
+class ConversionError(Exception):
+    """Print customized error for options that are not converted correctly
+    and point out that they are maybe not implemented, yet"""
+    def __init__(self, key_type, attr, value):
+        Exception.__init__(self)
+        self.key_type = key_type
+        self.value = value
+        self.attr = attr
+
+    def __str__(self):
+        return "Could not convert %s = %s to %s\n" \
+            % (self.attr, self.value, self.key_type) \
+            + "This means you either tried to set a value of the wrong\n"\
+            + "type or this keyword needs some special care. Please feel\n"\
+            + "to add it to the corresponding __setattr__ method and send\n"\
+            + "the patch to max.hoffmann at tum.de, so we can all benefit."
+
+
+def get_castep_pp_path(castep_pp_path=''):
+    """Abstract the quest for a CASTEP PSP directory."""
+    if castep_pp_path:
+        return os.path.abspath(os.path.expanduser(castep_pp_path))
+    elif 'CASTEP_PP_PATH' in os.environ:
+        return os.environ['CASTEP_PP_PATH']
+    else:
+        return os.path.abspath('.')
+
+
+def get_castep_command(castep_command=''):
+    """Abstract the quest for a castep_command string."""
+    if castep_command:
+        return castep_command
+    elif 'CASTEP_COMMAND' in os.environ:
+        return os.environ['CASTEP_COMMAND']
+    else:
+        return 'castep'
+
+
+def shell_stdouterr(raw_command):
+    """Abstracts the standard call of the commandline, when
+    we are only interested in the stdout and stderr
+    """
+    stdout, stderr = subprocess.Popen(raw_command,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=True).communicate()
+    return stdout.strip(), stderr.strip()
+
+
+def import_castep_keywords():
+    try:
+        import castep_keywords
+    except ImportError:
+        print("""    Generating castep_keywords.py ... hang on.
+    The castep_keywords.py contains abstractions for CASTEP input
+    parameters (for both .cell and .param input files), including some
+    format checks and descriptions. The latter are extracted from the
+    internal online help facility of a CASTEP binary, thus allowing to
+    easily keep the calculator synchronized with (different versions of)
+    the CASTEP code. Consequently, avoiding licensing issues (CASTEP is
+    distributed commercially by accelrys), we consider it wise not to
+    provide castep_keywords.py in the first place.
+""")
+        create_castep_keywords(get_castep_command())
+        print("""\n\n    Stored castep_keywords.py in %s.  Copy castep_keywords.py to your
+    ASE installation under ase/calculators for system-wide installation
+""" % os.path.abspath(os.path.curdir))
+
+
+
+        import castep_keywords
+    return castep_keywords
+
+
+if __name__ == '__main__':
+    print("When called directly this calculator will fetch all available")
+    print("keywords from the binarys help function into a castep_keywords.py")
+    print("in the current directory %s" % os.getcwd())
+    print("For system wide usage, it can be copied into an ase installation")
+    print("at ASE/calculators.\n")
+    print("This castep_keywords.py usually only needs to be generated once")
+    print("for a CASTEP binary/CASTEP version.")
+
+    import optparse
+    parser = optparse.OptionParser()
+    parser.add_option('-f', '--force-write', dest='force_write',
+        help='Force overwriting existing castep_keywords.py', default=False,
+        action='store_true')
+    (options, args) = parser.parse_args()
+
+    if args:
+        opt_castep_command = ''.join(args)
+    else:
+        opt_castep_command = ''
+    generated = create_castep_keywords(get_castep_command(opt_castep_command),
+        force_write=options.force_write)
+
+    if generated:
+        try:
+            execfile('castep_keywords.py')
+        except Exception, e:
+            print(e)
+            print("Ooops, something went wrong with the CASTEP keywords")
+        else:
+            print("Import works. Looking good!")
diff --git a/ase/calculators/dacapo.py b/ase/calculators/dacapo.py
new file mode 100644
index 0000000..62ade3b
--- /dev/null
+++ b/ase/calculators/dacapo.py
@@ -0,0 +1,238 @@
+import numpy as np
+
+from ase.old import OldASEListOfAtomsWrapper
+
+try:
+    import Numeric as num
+except ImportError:
+    pass
+
+def np2num(a, typecode=None):
+    if num.__version__ > '23.8':
+        return num.array(a, typecode)
+    if typecode is None:
+        typecode = num.Float
+    b = num.fromstring(a.tostring(), typecode)
+    b.shape = a.shape
+    return b
+
+def restart(filename, **kwargs):
+    calc = Dacapo(filename, **kwargs)
+    atoms = calc.get_atoms()
+    return atoms, calc
+
+class Dacapo:
+    def __init__(self, filename=None, stay_alive=False, stress=False,
+                 **kwargs):
+
+        self.kwargs = kwargs
+        self.stay_alive = stay_alive
+        self.stress = stress
+        
+        if filename is not None:
+            from Dacapo import Dacapo
+            self.loa = Dacapo.ReadAtoms(filename, **kwargs)
+            self.calc = self.loa.GetCalculator()
+        else:
+            self.loa = None
+            self.calc = None
+
+        self.pps = []
+        
+    def set_pp(self, Z, path):
+        self.pps.append((Z, path))
+
+    def set_txt(self, txt):
+        if self.calc is None:
+            self.kwargs['txtout'] = txt
+        else:
+            self.calc.SetTxtFile(txt)
+
+    def set_nc(self, nc):
+        if self.calc is None:
+            self.kwargs['out'] = nc
+        else:
+            self.calc.SetNetCDFFile(nc)
+
+    def update(self, atoms):
+        from Dacapo import Dacapo
+        if self.calc is None:
+            if 'nbands' not in self.kwargs:
+                n = sum([valence[atom.symbol] for atom in atoms])
+                self.kwargs['nbands'] = int(n * 0.65) + 4
+
+            magmoms = atoms.get_initial_magnetic_moments()
+            if magmoms.any():
+                self.kwargs['spinpol'] = True
+
+            self.calc = Dacapo(**self.kwargs)
+
+            if self.stay_alive:
+                self.calc.StayAliveOn()
+            else:
+                self.calc.StayAliveOff()
+
+            if self.stress:
+                self.calc.CalculateStress()
+
+            for Z, path in self.pps:
+                self.calc.SetPseudoPotential(Z, path)
+
+        if self.loa is None:
+            from ASE import Atom, ListOfAtoms
+            numbers = atoms.get_atomic_numbers()
+            positions = atoms.get_positions()
+            magmoms = atoms.get_initial_magnetic_moments()
+            self.loa = ListOfAtoms([Atom(Z=numbers[a],
+                                         position=positions[a],
+                                         magmom=magmoms[a])
+                                    for a in range(len(atoms))],
+                                   cell=np2num(atoms.get_cell()),
+                                   periodic=tuple(atoms.get_pbc()))
+            self.loa.SetCalculator(self.calc)
+        else:
+            self.loa.SetCartesianPositions(np2num(atoms.get_positions()))
+            self.loa.SetUnitCell(np2num(atoms.get_cell()), fix=True)
+            
+    def get_atoms(self):
+        atoms = OldASEListOfAtomsWrapper(self.loa).copy()
+        atoms.set_calculator(self)
+        return atoms
+    
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.calc.GetPotentialEnergy()
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return np.array(self.calc.GetCartesianForces())
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        stress = np.array(self.calc.GetStress())
+        if stress.ndim == 2:
+            return stress.ravel()[[0, 4, 8, 5, 2, 1]]
+        else:
+            return stress
+
+    def calculation_required(self, atoms, quantities):
+        if self.calc is None:
+            return True
+
+        if atoms != self.get_atoms():
+            return True
+
+        return False
+        
+    def get_number_of_bands(self):
+        return self.calc.GetNumberOfBands()
+
+    def get_k_point_weights(self):
+        return np.array(self.calc.GetIBZKPointWeights())
+
+    def get_number_of_spins(self):
+        return 1 + int(self.calc.GetSpinPolarized())
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        return np.array(self.calc.GetEigenvalues(kpt, spin))
+
+    def get_fermi_level(self):
+        return self.calc.GetFermiLevel()
+
+    def get_magnetic_moment(self):
+        return self.calc.GetMagneticMoment()
+
+    def get_number_of_electrons(self):
+        return self.calc.GetValenceElectrons()
+
+    def get_number_of_grid_points(self):
+        return np.array(self.get_pseudo_wave_function(0, 0, 0).shape)
+
+    def get_pseudo_density(self, spin=0):
+        return np.array(self.calc.GetDensityArray(s))
+    
+    def get_pseudo_wave_function(self, band=0, kpt=0, spin=0, pad=True):
+        kpt_c = self.get_bz_k_points()[kpt]
+        state = self.calc.GetElectronicStates().GetState(band=band, spin=spin,
+                                                         kptindex=kpt)
+
+        # Get wf, without bloch phase (Phase = True doesn't do anything!)
+        wave = state.GetWavefunctionOnGrid(phase=False)
+
+        # Add bloch phase if this is not the Gamma point
+        if np.all(kpt_c == 0):
+            return wave
+        coord = state.GetCoordinates()
+        phase = coord[0] * kpt_c[0] + coord[1] * kpt_c[1] + coord[2] * kpt_c[2]
+        return np.array(wave) * np.exp(-2.j * np.pi * phase) # sign! XXX
+
+        #return np.array(self.calc.GetWaveFunctionArray(n, k, s)) # No phase!
+
+    def get_bz_k_points(self):
+        return np.array(self.calc.GetBZKPoints())
+
+    def get_ibz_k_points(self):
+        return np.array(self.calc.GetIBZKPoints())
+
+    def get_wannier_localization_matrix(self, nbands, dirG, kpoint,
+                                        nextkpoint, G_I, spin):
+        return np.array(self.calc.GetWannierLocalizationMatrix(
+            G_I=G_I.tolist(), nbands=nbands, dirG=dirG.tolist(),
+            kpoint=kpoint, nextkpoint=nextkpoint, spin=spin))
+    
+    def initial_wannier(self, initialwannier, kpointgrid, fixedstates,
+                        edf, spin):
+        # Use initial guess to determine U and C
+        init = self.calc.InitialWannier(initialwannier, self.atoms,
+                                        np2num(kpointgrid, num.Int))
+
+        states = self.calc.GetElectronicStates()
+        waves = [[state.GetWaveFunction()
+                  for state in states.GetStatesKPoint(k, spin)]
+                 for k in self.calc.GetIBZKPoints()] 
+
+        init.SetupMMatrix(waves, self.calc.GetBZKPoints())
+        c, U = init.GetListOfCoefficientsAndRotationMatrices(
+            (self.calc.GetNumberOfBands(), fixedstates, edf))
+        U = np.array(U)
+        for k in range(len(c)):
+            c[k] = np.array(c[k])
+        return c, U
+
+valence = {
+'H':   1,
+'B':   3,
+'C':   4,
+'N':   5,
+'O':   6,
+'Li':  1,
+'Na':  1,
+'K':   9,
+'Mg':  8,
+'Ca': 10,
+'Sr': 10,
+'Al':  3,
+'Ga': 13,
+'Sc': 11,
+'Ti': 12,
+'V':  13,
+'Cr': 14,
+'Mn':  7,
+'Fe':  8,
+'Co':  9,
+'Ni': 10,
+'Cu': 11,
+'Zn': 12,
+'Y':  11,
+'Zr': 12,
+'Nb': 13,
+'Mo':  6,
+'Ru':  8,
+'Rh':  9,
+'Pd': 10,
+'Ag': 11,
+'Cd': 12,
+'Ir': 9,
+'Pt': 10,
+'Au': 11,
+}
diff --git a/ase/calculators/dftb.py b/ase/calculators/dftb.py
new file mode 100644
index 0000000..116968b
--- /dev/null
+++ b/ase/calculators/dftb.py
@@ -0,0 +1,678 @@
+"""This module defines an ASE interface to DftbPlus
+
+http://http://www.dftb-plus.info//
+http://www.dftb.org/
+
+-Markus Kaukonen markus.kaukonen at iki.fi
+"""
+
+
+import numpy as np
+import os
+
+#from ase.data import chemical_symbols
+from ase.units import Hartree, Bohr
+
+
+class Dftb:
+    """Class for doing DFTB+ calculations.
+    """
+    def __init__(self, label='dftb', write_dftb=False,
+                 charge=0.0, include_dispersion=False,
+                 do_spin_polarized=False, 
+                 unpaired_electrons=0.0,
+                 fermi_temperature=0.0, scc=False,
+                 kpt_n11=1, kpt_n12=0, kpt_n13=0,
+                 kpt_n21=0, kpt_n22=1, kpt_n23=0,
+                 kpt_n31=0, kpt_n32=0, kpt_n33=1,
+                 kpt_s1=0.0, kpt_s2=0.0, kpt_s3=0.0,
+                 md_dftb=False, dftb_nsteps=0, md_restart_frequency=100,
+                 md_dftb_write_vel=True, md_dftb_init_t=300.0,
+                 md_dftb_berendsen_nvt=False,md_dftb_berendsen_t=300,
+                 md_dftb_keep_stationary=True,
+                 md_dftb_dt=0.1,md_dftb_berendsen_strength=1.0e-4):
+        """Construct DFTB-calculator object.
+
+
+        For example:
+        calc = Dftb(label='dftb',write_dftb=True,include_dispersion=True )
+
+        Parameters
+        ==========
+        label: str
+            Prefix to use for filenames (label.txt, ...).
+            Default is 'dftb'.
+        write_dftb: boolean
+            True: a minimal input file (name of which is always 'dftb_in.hsd')
+            is written based on values given here.
+            False: input file for dftb+ is not written. User must have
+            generated file 'dftb_in.hsd' in the working directory.
+            Use write_dftb=False to use your own 'dftb_in.hsd'-file.
+        charge: float
+            Total charge of the system.
+        include_dispersion: boolean
+            True: Default dispersion parameters are written in the 
+            file 'dftb_in.hsd' (requires that also write_dftb_input_file==True)
+            False: dispersion parameters are not written here.
+        do_spin_polarized: boolean
+            True: Spin polarized calculation
+            (requires that also write_dftb_input_file==True)
+            False: Spin unpolarized calculation
+        unpaired_electrons: float
+            Number of spin unpaired electrons in the system.
+            Relevant only if do_spin_polarized==True
+        fermi_temperature: float
+            Fermi temperature for electrons.
+        scc: boolean
+            True: Do charge self consistent dftb+
+            False: No SCC, charges on atoms are not iterated
+        kpt_nxx: int(s)
+            The 9 integers for K-point generation scheme SupercellFolding
+        kpt_s1, kpt_s2, kpt_s3: float(s)
+            Shifts for the K-point generation scheme SupercellFolding
+        md_dftb: boolean
+            True: MD is run by DFTB
+            False: MD is run by ase (or A CG run is done by ase or DFTB)
+            (requires that also write_dftb_input_file==True)
+        dftb_nsteps: int
+            Number of simulation steps (either in CG or MD)
+            run by DFTB+ program.
+            If dftb_nsteps==0 it is assumed that minimisation/dynamics is run
+            by ase (after DFTB+ positions and velocities are read).
+            If dftb_nsteps>0 minimisation/dynamics is run by DFTB+.
+            (requires that also write_dftb_input_file==True)
+        md_restart_frequency: int
+            How often coordinates are written.
+            (requires that md_dftb==True, ie MD is run by DFTB+ program)
+            (requires that also write_dftb_input_file==True)
+        md_dftb_write_vel: bool
+            True:
+                velocities are written in dftb_in.hsd file to be read by
+                dftb (in this case md_dftb_init_t is ignored)
+            False:
+                velocities are not written in dftb_in.hsd
+                (requires that md_dftb==True, ie MD is run by DFTB+ program)
+                (requires that also write_dftb_input_file==True)    
+        md_dftb_init_t: float
+            Initial temperature is set randomly to md_dftb_init_t
+            This is dummy if md_dftb_write_vel==True
+            (requires that md_dftb==True, ie MD is run by DFTB+ program)
+            (requires that also write_dftb_input_file==True)            
+        md_dftb_berendsen_nvt: boolean
+            True: Berendsen NVT dynamics is run by DFTB+ (for dftb_nsteps)
+            False: no Berendsen NVT MD
+            (requires that also write_dftb_input_file==True)
+        md_dftb_berendsen_t: float
+            Temperature for DFTB+ Berendsen thermostate
+            (requires that also write_dftb_input_file==True)
+        md_dftb_keep_stationary: boolean
+            True: In DFTB+ MD the translational movement
+            of the system is removed
+            False: Center of mass movement is not removed in DFTB+ MD
+            (requires that also write_dftb_input_file==True)
+        md_dftb_dt: float
+            Timestep in [fs] for DFTB+ Berendsen thermostate.
+            (requires that also write_dftb_input_file==True)
+        md_dftb_berendsen_strength:float
+            Coupling strength in DFTB+ Berendsen thermostate
+            (requires that also write_dftb_input_file==True)
+            
+        Input file for DFTB+ file is 'dftb_in.hsd'. Keywords in it are
+        written here or read from an existing file. The atom positions
+        in file 'dftb_in.hsd' are updated during ASE geometry
+        optimization.
+        """
+
+
+        if not(write_dftb):
+            if os.path.isfile('dftb_in.hsd'):
+                myf = open('dftb_in.hsd')
+            else:
+                print 'Input file for DFTB+ dftb_in.hsd is missing'
+                raise RuntimeError, \
+                    'Provide it or set write_dftb=True '
+            #lines = f.readlines()
+            myf.close()
+
+        self.label = label
+        self.write_dftb = write_dftb
+        self.charge = charge
+        self.include_dispersion = include_dispersion
+        self.do_spin_polarized = do_spin_polarized
+        self.unpaired_electrons = unpaired_electrons
+        #if (do_spin_polarized):
+        #    print 'Sorry, generation of file "dftb_in.hsd" with spin '
+        #    print 'polarization is not inplemented for DFTB+'
+        #    print 'Set write_dftb=False and'
+        #    raise RuntimeError, \
+        #        'Generate file "dftb_in.hsd by hand"'
+        
+        self.etotal = 0.0
+        self.cell = None
+        self.fermi_temperature = fermi_temperature
+        self.scc = scc
+        self.kpt_n11 = kpt_n11
+        self.kpt_n12 = kpt_n12
+        self.kpt_n13 = kpt_n13
+        self.kpt_n21 = kpt_n21
+        self.kpt_n22 = kpt_n22
+        self.kpt_n23 = kpt_n23
+        self.kpt_n31 = kpt_n31
+        self.kpt_n32 = kpt_n32
+        self.kpt_n33 = kpt_n33
+        self.kpt_s1 = kpt_s1
+        self.kpt_s2 = kpt_s2
+        self.kpt_s3 = kpt_s3
+        self.md_dftb = md_dftb
+        self.dftb_nsteps = dftb_nsteps
+        self.md_restart_frequency = md_restart_frequency
+        self.md_dftb_write_vel = md_dftb_write_vel
+        self.md_dftb_init_t = md_dftb_init_t
+        self.md_dftb_berendsen_nvt = md_dftb_berendsen_nvt        
+        self.md_dftb_berendsen_t = md_dftb_berendsen_t
+        self.md_dftb_keep_stationary = md_dftb_keep_stationary
+        self.md_dftb_dt = md_dftb_dt
+        self.md_dftb_berendsen_strength = md_dftb_berendsen_strength
+        self.converged = False
+
+        #dftb has no stress
+        self.stress = np.empty((3, 3))
+        
+
+    def update(self, atoms):
+        """Energy and forces are calculated when atoms have moved
+        by calling self.calculate
+        """
+        if (not self.converged or
+            len(self.typenumber) != len(atoms)):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        """ Set arrays of species, number of atoms in species and 
+            max angular momentum for each species.
+            Also write dftb_in.hsd file if desired
+        """
+        #from ase.io.dftb import read_dftb
+
+        atomtypes = atoms.get_chemical_symbols()
+        self.allspecies = []
+        self.typenumber = []
+        self.max_angular_momentum = []
+        for species in (atomtypes):
+            if species not in self.allspecies:
+                self.allspecies.append(species)
+        for species in (atomtypes):
+            myindex = 1 + self.allspecies.index(species)
+            self.typenumber.append(myindex)
+        for i in self.allspecies:
+            if i == 'H':
+                self.max_angular_momentum.append('s')
+            elif i in ['C','N','O']:
+                self.max_angular_momentum.append('p')
+            elif i in ['Si','S','Fe','Ni']:
+                self.max_angular_momentum.append('d')
+            else:
+                print 'anglular momentum is not imlemented in ASE-DFTB+'
+                print 'for species '+i
+                raise RuntimeError('Use option write_dftb=False')
+        self.converged = False
+        
+        #write DFTB input file if desired 
+        self.positions = atoms.get_positions().copy()
+        if self.write_dftb:
+            self.write_dftb_input_file(atoms)
+
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.etotal
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+    
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress.copy()
+
+    def calculate(self, atoms):
+        """Total DFTB energy is calculated (to file 'energy'
+        also forces are calculated (to file 'gradient')
+        In case of md_dftb==True (md is run by DFTB+)
+        the positions and velocities are updated after the DFTB+ md run.
+        """
+        
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        if self.write_dftb:
+            self.write_dftb_input_file(atoms)
+        else:
+            #write current coordinates to file 'dftb_in.hsd' for DFTB+
+            self.change_atom_positions_dftb(atoms)
+
+        #print "OK1"
+        #DFTB energy&forces calculation (or possibly full MD if md_dftb==True)
+        if (os.environ.has_key('DFTB_COMMAND') and
+            (os.environ.has_key('DFTB_PREFIX'))):
+            dftb = os.environ['DFTB_COMMAND']
+            exitcode = os.system(dftb+ '> dftb.output' )
+        elif not(os.environ.has_key('DFTB_COMMAND')):
+            raise RuntimeError('Please set DFTB_COMMAND environment variable')
+        elif not(os.environ.has_key('DFTB_PREFIX')):
+            print 'Path for DFTB+ slater koster files is missing'    
+            raise RuntimeError('Please set DFTB_PREFIX environment variable')
+        if exitcode != 0:
+            raise RuntimeError('Dftb exited with exit code: %d.  ' % exitcode)
+
+        #read positions and velocities in case MD/CG was run by DFTB
+        if (self.md_dftb):
+            #print "READINGG POSITIONS"
+            self.read_positions(atoms)
+            self.read_velocities(atoms)
+            #print "setting vel after DFTB+",atoms.get_velocities().copy()
+        else:
+            # in case of dynamics/minimisation is done by ASE
+            # read energies&forces
+            self.read_energy()
+            #DFTB atomic forces calculation, to be read in file detailed.out
+            #os.system(self.dftb_program_forces +'> output.forces.dummy')
+            self.read_forces(atoms)
+            #in case of cg minimisation with more than 0 steps 
+            #we read the DFTB+ minimised geometry
+            if (self.dftb_nsteps > 0):
+                self.read_positions(atoms)
+            
+        self.converged = True
+
+        
+    def read_energy(self):
+        """Read Energy from DFTB energy file."""
+        text = open('dftb.output', 'r').read().lower()
+        lines = iter(text.split('\n'))
+
+        # Energy:
+        for line in lines:
+            if 'total energy' in line:
+                energy_tmp = float(line.split()[2])
+                #print 'energy_tmp', energy_tmp
+        self.etotal = energy_tmp * Hartree
+
+
+    def read_forces(self, atoms):
+        """Read Forces from DFTB+ detailed.out output file"""
+
+        myf = open('detailed.out','r')
+        line = myf.readline()
+        line = myf.readline()
+        tmpforces = np.array([[0, 0, 0]])
+        while line:
+            if 'Total Forces' in line:
+                for dummy in atoms:
+                    line = myf.readline()
+                    line2 = str.replace(line, 'D', 'E')
+                    tmp = np.array\
+                        ([[float(f) for f in line2.split()[0:3]]])
+                    tmpforces = np.concatenate((tmpforces, tmp))  
+            line = myf.readline()
+            
+
+        self.forces = (np.delete(tmpforces, np.s_[0:1], axis=0))*Hartree/Bohr
+
+    def read_positions(self, atoms):
+        """Read positions from LABEL.xyz output file"""
+
+        myf = open(self.label+'.xyz','r')
+
+        # take last coordinates of the LABEL.xyz file
+        lines = myf.readlines()
+        linesreversed = []
+        for linereverse in reversed(lines):
+            if ('MD iter' in linereverse):
+                break
+            else:
+                linesreversed.append(linereverse)
+        tmpcoord = np.array([[0, 0, 0]])
+        for lineok in reversed(linesreversed):
+            tmp = np.array\
+                  ([[float(f) for f in lineok.split()[1:4]]])
+            tmpcoord = np.concatenate((tmpcoord, tmp))  
+        atoms.set_positions(np.delete(tmpcoord, np.s_[0:1], axis=0))
+
+    def read_velocities(self, atoms):
+        """Read velocities from LABEL.xyz output file"""
+
+        myf = open(self.label+'.xyz','r')
+
+        # take last coordinates of the LABEL.xyz file
+        lines = myf.readlines()
+        linesreversed = []
+        for linereverse in reversed(lines):
+            if ('MD iter' in linereverse):
+                break
+            else:
+                linesreversed.append(linereverse)
+        tmpvel = np.array([[0, 0, 0]])
+        for lineok in reversed(linesreversed):
+            tmp = np.array\
+                  ([[float(f) for f in lineok.split()[4:7]]])
+            tmpvel = np.concatenate((tmpvel, tmp))  
+        tmpvel = tmpvel / 98.22693531550318
+        atoms.set_velocities(np.delete(tmpvel, np.s_[0:1], axis=0))
+        print "velocities after dftb", atoms.get_velocities().copy()
+
+
+    def read(self):
+        """Dummy stress for dftb"""
+        self.stress = np.empty((3, 3))
+
+    def write_dftb_input_file(self, atoms):
+        """Write input parameters to DFTB+ input file 'dftb_in.hsd'."""
+        import math
+
+        myf = open('dftb_in.hsd', 'w')
+        # geometry
+        myf.write('Geometry = {\n')
+        myf.write('TypeNames = {')
+        for i in self.allspecies:
+            myf.write(' "'+i+'"')
+        myf.write(' }\n')
+        myf.write('TypesAndCoordinates [Angstrom] = {\n')
+        self.positions = atoms.get_positions().copy()
+        for i, pos in zip(self.typenumber, self.positions):
+            myf.write('%6d ' % (i))
+            myf.write('%20.14f %20.14f %20.14f' %  tuple(pos))
+            myf.write('\n')
+        myf.write(' }\n')
+
+        #is it periodic and when is write also lattice vectors
+        periodic = atoms.get_pbc().any()
+        if periodic:
+            myf.write('Periodic = Yes\n')
+        else:
+            myf.write('Periodic = No\n')
+        if periodic:
+            cell = atoms.get_cell().copy()
+            myf.write('LatticeVectors [Angstrom] = {\n')
+            for latvec in cell:
+                myf.write('%20.14f %20.14f %20.14f \n' %  tuple(latvec))
+            myf.write('  }\n')
+
+        #end of geometry session
+        myf.write('}\n')
+
+        #print self.dftb_nsteps
+        if self.md_dftb:
+            #Md run by DFTB
+            myf.write('\n')
+            myf.write('Driver = VelocityVerlet{\n')
+            myf.write('  Steps = '+str(self.dftb_nsteps)+' \n')
+            if self.md_dftb_keep_stationary:
+                myf.write('  KeepStationary = Yes \n')
+            else:
+                myf.write('  KeepStationary = No \n')
+            myf.write('  TimeStep [fs] = '+str(self.md_dftb_dt)+' \n')
+
+            if self.md_dftb_berendsen_nvt:
+                myf.write('  Thermostat = Berendsen{ \n')
+                myf.write('  Temperature [K] = '+
+                    str(self.md_dftb_berendsen_t)+ '\n')
+                myf.write('    CouplingStrength = '+
+                    str(self.md_dftb_berendsen_strength)+ ' \n')
+                myf.write('  }\n')
+            else:
+                myf.write('  Thermostat = None{ \n')
+                if not self.md_dftb_write_vel:
+                    myf.write('    InitialTemperature [K] = '+
+                             str(self.md_dftb_init_t)+ '\n')
+                myf.write('  }\n')
+
+                
+            myf.write('  OutputPrefix = '+self.label+ '\n')
+            myf.write('  MDRestartFrequency = '+
+                     str(self.md_restart_frequency)+ '\n')
+            if self.md_dftb_write_vel:
+                myf.write('  Velocities [AA/ps]={ \n')
+                tmpvelocities = atoms.get_velocities().copy()
+                #from ase units to dftb+ units Ang/ps
+                tmpvelocities = tmpvelocities*98.22693531550318
+                for vel in tmpvelocities:
+                    if math.isnan(vel[0]) or \
+                            math.isnan(vel[1]) or \
+                            math.isnan(vel[2]):
+                        myf.write('  %20.14f %20.14f %20.14f' %  (0, 0, 0))
+                    else:
+                        myf.write('  %20.14f %20.14f %20.14f' %  tuple(vel))
+                    myf.write('\n')
+                myf.write('  }\n')
+            myf.write('}\n')
+        else:
+            #zero step CG relaxation to get forces and energies
+            myf.write('\n') 
+            myf.write('Driver = ConjugateGradient {\n')
+            myf.write('MovedAtoms = Range { 1 -1 }\n')
+            myf.write('  MaxForceComponent = 1.0e-4\n')
+            myf.write('  MaxSteps = '+str(self.dftb_nsteps)+' \n')
+            myf.write('  OutputPrefix = '+self.label+ '\n')
+            myf.write('}\n')
+
+        #Hamiltonian
+        myf.write('\n') 
+        myf.write('Hamiltonian = DFTB { # DFTB Hamiltonian\n')
+        if (self.scc):
+            myf.write('  SCC = Yes')
+            myf.write(' # Use self consistent charges\n')               
+            myf.write('  SCCTolerance = 1.0e-5')
+            myf.write(' # Tolerance for charge consistence\n')            
+            myf.write('  MaxSCCIterations = 50')
+            myf.write(' # Nr. of maximal SCC iterations\n')          
+            myf.write('  Mixer = Broyden {') 
+            myf.write(' # Broyden mixer for charge mixing\n')          
+            myf.write('    MixingParameter = 0.2')  
+            myf.write(' # Mixing parameter\n')
+            myf.write('  }\n')
+        else:
+            myf.write('  SCC = No # NO self consistent charges\n')
+        myf.write('  SlaterKosterFiles = Type2FileNames {')
+        myf.write(' # File names from two atom type names\n')
+        sk_prefix = os.environ['DFTB_PREFIX']
+        myf.write('    Prefix = "'+sk_prefix+'"')
+        myf.write(' # Path as prefix\n')
+        myf.write('    Separator = "-"')
+        myf.write(' # Dash between type names\n')
+        myf.write('    Suffix = ".skf"')
+        myf.write(' # Suffix after second type name\n')
+        myf.write('  }\n')
+        myf.write('  MaxAngularMomentum = {')
+        myf.write(' # Maximal l-value of the various species\n')
+        for i, j in zip(self.allspecies, self.max_angular_momentum):
+            myf.write('   '+i+' = "'+j+'"\n')
+        myf.write('  }\n')
+        myf.write('  Charge = ')
+        myf.write('%10.6f' % (self.charge))
+        myf.write(' # System neutral\n')
+        if self.do_spin_polarized:
+            myf.write('  SpinPolarisation = Colinear {\n') 
+            myf.write('  UnpairedElectrons = ' + \
+                          str(self.unpaired_electrons)+'\n')
+            myf.write('  } \n')
+            myf.write('  SpinConstants = {\n') 
+            for i in self.allspecies:
+                if i == 'H':
+                    myf.write('   H={\n') 
+                    myf.write('    # Wss\n') 
+                    myf.write('    -0.072\n') 
+                    myf.write('    }\n')
+                elif i == 'C': 
+                    myf.write('   C={\n') 
+                    myf.write('    # Wss Wsp Wps Wpp\n') 
+                    myf.write('    -0.031 -0.025 -0.025 -0.023\n') 
+                    myf.write('    }\n') 
+                elif i == 'N': 
+                    myf.write('   N={\n') 
+                    myf.write('    # Wss Wsp Wps Wpp\n')
+                    myf.write('    -0.033 -0.027 -0.027 -0.026\n') 
+                    myf.write('     }\n') 
+                elif i == 'O':
+                    myf.write('   O={\n') 
+                    myf.write('    # Wss Wsp Wps Wpp\n') 
+                    myf.write('    -0.035 -0.030 -0.030 -0.028\n') 
+                    myf.write('     }\n') 
+                elif (i == 'Si' or i == 'SI'):
+                    myf.write('   Si={\n') 
+                    myf.write('    # Wss Wsp Wsd Wps Wpp Wpd Wds Wdp Wdd\n')
+                    myf.write('    -0.020 -0.015 0.000 -0.015 \
+-0.014 0.000 0.002 0.002 -0.032\n')
+                    myf.write('    }\n')
+                elif (i == 'S'):
+                    myf.write('   S={\n') 
+                    myf.write('    # Wss Wsp Wsd Wps Wpp Wpd Wds Wdp Wdd\n') 
+                    myf.write('    -0.021 -0.017 0.000 -0.017 \
+-0.016 0.000 0.000 0.000 -0.080\n')
+                    myf.write('    }\n')
+                elif (i == 'Fe' or i == 'FE'):
+                    myf.write('   Fe={\n') 
+                    myf.write('    # Wss Wsp Wsd Wps Wpp Wpd Wds Wdp Wdd\n') 
+                    myf.write('    -0.016 -0.012 -0.003 -0.012 \
+-0.029 -0.001 -0.003 -0.001 -0.015\n')
+                    myf.write('    }\n')
+                elif (i == 'Ni' or i == 'NI'):
+                    myf.write('   Ni={\n') 
+                    myf.write('    # Wss Wsp Wsd Wps Wpp Wpd Wds Wdp Wdd\n') 
+                    myf.write('    -0.016 -0.012 -0.003 -0.012 \
+-0.022 -0.001 -0.003 -0.001 -0.018\n')
+                    myf.write('    }\n')
+                else:
+                    print 'Missing spin polarisation parameters for species'+i
+                    raise RuntimeError, \
+                        'Run spin unpolarised calculation'
+            myf.write('   }\n') 
+        else:
+            #myf.write('  SpinPolarisation = {}')
+            myf.write(' # No spin polarisation\n')
+        myf.write('  Filling = Fermi {\n')
+        myf.write('    Temperature [Kelvin] = ')
+        myf.write('%10.6f\n' % (self.fermi_temperature))
+        myf.write('  }\n')
+        if periodic:
+            myf.write('  KPointsAndWeights = SupercellFolding { \n')
+            myf.write('%6d %6d %6d \n' 
+                     % (self.kpt_n11, self.kpt_n12, self.kpt_n13))
+            myf.write('%6d %6d %6d \n'
+                     % (self.kpt_n21, self.kpt_n22, self.kpt_n23))
+            myf.write('%6d %6d %6d \n' 
+                     % (self.kpt_n31, self.kpt_n32, self.kpt_n33))
+            myf.write('  %10.6f %10.6f %10.6f \n' 
+                     % (self.kpt_s1, self.kpt_s2, self.kpt_s3))
+            myf.write('  }\n')
+
+        #Dispersion parameters
+        if (self.include_dispersion):
+            myf.write('Dispersion = SlaterKirkwood {\n')
+            myf.write(' PolarRadiusCharge = HybridDependentPol {\n')
+            myf.write('\n')
+            myf.write('  C={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.8\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('      1.382 1.382 1.382 \
+1.064 1.064 1.064 3.8 3.8 3.8 3.8 3.8 3.8 2.5\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write('\n')
+            myf.write('  N={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.8\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('      1.030 1.030 1.090 1.090 1.090 1.090 \
+3.8 3.8 3.8 3.8 3.8 3.8 2.82\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write('\n')
+            myf.write('  O={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.8\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('    # All polarisabilities and radii set the same\n')
+            myf.write('      0.560 0.560 0.000 0.000 0.000 0.000 \
+3.8 3.8 3.8 3.8 3.8 3.8 3.15\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write('\n')
+
+
+            myf.write('  H={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.4\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('    # Different polarisabilities depending on the hybridisation\n')
+            myf.write('      0.386 0.396 0.000 0.000 0.000 0.000 \
+3.5 3.5 3.5 3.5 3.5 3.5 0.8\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write('\n')
+
+            myf.write('  P={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.9\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('    # Different polarisabilities depending \
+on the hybridisation\n')
+            myf.write('      1.600 1.600 1.600 1.600 1.600 1.600 \
+4.7 4.7 4.7 4.7 4.7 4.7 4.50\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write('\n')
+
+            myf.write('  S={\n')
+            myf.write('    CovalentRadius [Angstrom] = 0.9\n')
+            myf.write('    HybridPolarisations [Angstrom^3,Angstrom,] = {\n')
+            myf.write('    # Different polarisabilities depending \
+on the hybridisation\n')
+            myf.write('      3.000 3.000 3.000 3.000 3.000 3.000 \
+4.7 4.7 4.7 4.7 4.7 4.7 4.80\n')
+            myf.write('    }\n')
+            myf.write('  }\n')
+            myf.write(' }\n')        
+            myf.write('}\n') 
+        myf.write('}\n') 
+        myf.write('Options = {}\n')
+        myf.write('ParserOptions = {\n')
+        myf.write('  ParserVersion = 3\n')
+        myf.write('}\n')
+ 
+        myf.close()
+
+    def change_atom_positions_dftb(self, atoms):
+        """Write coordinates in DFTB+ input file dftb_in.hsd
+        """
+
+        filename = 'dftb_in.hsd'
+        myf = open(filename)
+        lines = myf.readlines()
+        myf.close()
+
+        myf = open(filename, 'w')
+
+        coord = atoms.get_positions().copy()
+
+        start_writing_coords = False
+        stop_writing_coords = False
+        i = 0
+        for line in lines:
+            if ('TypesAndCoordinates' in line):
+                start_writing_coords = True
+            if (start_writing_coords and not(stop_writing_coords)):
+                if ('}' in line):
+                    stop_writing_coords = True
+            if (start_writing_coords and not(stop_writing_coords)and 
+                not ('TypesAndCoordinates' in line)):
+                atom_type_index = line.split()[0]
+                myf.write('%6s  %20.14f  %20.14f  %20.14f \n'
+                        % (atom_type_index,coord[i][0],coord[i][1],coord[i][2]))
+                i = i + 1
+            else:
+                myf.write(line)
+
+        myf.close()
diff --git a/ase/calculators/elk.py b/ase/calculators/elk.py
new file mode 100644
index 0000000..ab2100a
--- /dev/null
+++ b/ase/calculators/elk.py
@@ -0,0 +1,407 @@
+import os
+
+import numpy as np
+
+from ase.units import Bohr, Hartree
+
+elk_parameters = {
+    'swidth': Hartree,
+    }
+
+class ELK:
+    def __init__(self, dir='.', xc=None, kpts=None, tasks=[0], **kwargs):
+        for key, value in kwargs.items():
+            if key in elk_parameters:
+                kwargs[key] /= elk_parameters[key]
+        try:
+            self.exe = os.environ['ELK']
+        except KeyError:
+            self.exe = 'elk'
+
+        if xc is not None:
+            if 'xctype' in kwargs:
+                raise ValueError("You can't use both 'xc' and 'xctype'!")
+            else:
+                kwargs['xctype'] = {'LDA': 3, # PW92
+                                    'PBE': 20,
+                                    'REVPBE': 21,
+                                    'PBESOL': 22,
+                                    'WC06': 26,
+                                    'AM05': 30}[xc.upper()]
+
+        if kpts is not None:
+            if 'autokpt' in kwargs:
+                if kwargs['autokpt']:
+                    raise ValueError("You can't use both 'kpts' and 'autokpt'!")
+            if 'ngridk' in kwargs:
+                raise ValueError("You can't use both 'kpts' and 'ngridk'!")
+            if 'vkloff' in kwargs:
+                raise ValueError("You can't use both 'kpts' and 'vkloff'!")
+            else:
+                kwargs['ngridk'] = kpts
+                vkloff = []
+                for nk in kpts:
+                    if nk % 2 == 0:  # shift kpoint away from gamma point
+                        vkloff.append(0.5 / nk)
+                    else:
+                        vkloff.append(0)
+                kwargs['vkloff'] = vkloff
+
+        kwargs['tasks'] = tasks
+
+        if 'rmt' in kwargs:
+            self.rmt = kwargs['rmt']
+            assert len(self.rmt.keys()) == len(list(set(self.rmt.keys()))), 'redundant rmt definitions'
+        else:
+            self.rmt = None
+
+        self.parameters = kwargs
+        if 'rmt' in self.parameters:
+            self.parameters.pop('rmt') # this is not an elk keyword!
+
+        self.dir = dir
+        self.energy = None
+
+        self.converged = False
+
+    def update(self, atoms):
+        if (not self.converged or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        self.numbers = atoms.get_atomic_numbers().copy()
+
+        if not hasattr(self, 'spinpol'):
+            self.spinpol = atoms.get_initial_magnetic_moments().any()
+
+        self.write(atoms)
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress.copy()
+
+    def get_ibz_k_points(self):
+        return self.read_kpts(mode='ibz_k_points')
+
+    def read_kpts(self, mode='ibz_k_points'):
+        """ Returns list of kpts weights or kpts coordinates.  """
+        values = []
+        assert mode in ['ibz_k_points' , 'k_point_weights'], 'mode not in [\'ibz_k_points\' , \'k_point_weights\']'
+        KPOINTS_file = '%s/KPOINTS.OUT' % self.dir
+        if os.path.isfile(KPOINTS_file) or os.path.islink(KPOINTS_file):
+            lines = open(KPOINTS_file).readlines()
+            kpts = None
+            for line in lines:
+                if line.rfind(': nkpt') > -1:
+                    kpts = int(line.split(':')[0].strip())
+                    break
+            assert not kpts is None
+            text = lines[1:] # remove first line
+            values = []
+            for line in text:
+                if mode == 'ibz_k_points':
+                    b = [float(c.strip()) for c in line.split()[1:-3]]
+                else:
+                    b = [float(c.strip()) for c in line.split()[-2]]
+                values.append(b)
+            if len(values) == 0:
+                values = None
+            return np.array(values)
+
+    def get_spin_polarized(self):
+        return self.spinpol
+
+    def get_number_of_spins(self):
+        return 1 + int(self.spinpol)
+
+    def get_magnetic_moment(self, atoms):
+        self.update(atoms)
+        return self.magnetic_moment
+
+    def read_magnetic_moment(self):
+        magmom = None
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            lines = open(INFO_file).readlines()
+            for line in lines:
+                if line.rfind('total moment                :') > -1:
+                    magmom = float(line.split(':')[1].strip()) # last iter
+        return magmom
+
+    def get_magnetic_moments(self, atoms):
+        # not implemented yet, so
+        # so set the total magnetic moment on the atom no. 0 and fill with 0.0
+        self.update(atoms)
+        magmoms = [0.0 for a in range(len(atoms))]
+        magmoms[0] = self.get_magnetic_moment(atoms)
+        return np.array(magmoms)
+
+    def get_fermi_level(self):
+        return self.read_fermi()
+
+    def get_number_of_bands(self):
+        return self.nbands
+
+    def read_number_of_bands(self):
+        nbands = None
+        EIGVAL_file = '%s/EIGVAL.OUT' % self.dir
+        if os.path.isfile(EIGVAL_file) or os.path.islink(EIGVAL_file):
+            lines = open(EIGVAL_file).readlines()
+            for line in lines:
+                if line.rfind(': nstsv') > -1:
+                    nbands = int(line.split(':')[0].strip())
+                    break
+        if self.get_spin_polarized():
+            nbands = nbands / 2
+        return nbands
+
+    def get_number_of_iterations(self):
+        return self.niter
+
+    def read_number_of_iterations(self):
+        niter = None
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            lines = open(INFO_file).readlines()
+            for line in lines:
+                if line.rfind(' Loop number : ') > -1:
+                    niter = int(line.split(':')[1].split()[0].strip()) # last iter
+        return niter
+
+    def get_electronic_temperature(self):
+        return self.swidth
+
+    def read_electronic_temperature(self):
+        swidth = None
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            text = open(INFO_file).read().lower()
+            assert 'convergence targets achieved' in text
+            assert not 'reached self-consistent loops maximum' in text
+            for line in iter(text.split('\n')):
+                if line.rfind('smearing width :') > -1:
+                    swidth = float(line.split(':')[1].strip())
+                    break
+        return Hartree*swidth
+
+    def get_number_of_electrons(self):
+        return self.nelect
+
+    def read_number_of_electrons(self):
+        nelec = None
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            text = open(INFO_file).read().lower()
+            assert 'convergence targets achieved' in text
+            assert not 'reached self-consistent loops maximum' in text
+            # Total electronic charge
+            for line in iter(text.split('\n')):
+                if line.rfind('total electronic charge :') > -1:
+                    nelec = float(line.split(':')[1].strip())
+                    break
+        return nelec
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        return self.read_eigenvalues(kpt, spin, 'eigenvalues')
+
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        return self.read_eigenvalues(kpt, spin, 'occupations')
+
+    def get_fermi_level(self):
+        return self.fermi
+
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        self.initialize(atoms)
+
+        assert os.system('cd %s&& %s ' % (self.dir, self.exe)) == 0
+        self.read()
+
+        self.converged = True
+
+    def write(self, atoms):
+        if not os.path.isdir(self.dir):
+            os.mkdir(self.dir)
+        fd = open('%s/elk.in' % self.dir, 'w')
+        for key, value in self.parameters.items():
+            fd.write('%s\n' % key)
+            if isinstance(value, bool):
+                fd.write('.%s.\n\n' % ('false', 'true')[value])
+            elif isinstance(value, (int, float)):
+                fd.write('%s\n\n' % value)
+            else:
+                fd.write('%s\n\n' % ' '.join([str(x) for x in value]))
+
+        fd.write('avec\n')
+        for vec in atoms.cell:
+            fd.write('%.14f %.14f %.14f\n' % tuple(vec / Bohr))
+        fd.write('\n')
+
+        species = {}
+        symbols = []
+        for a, symbol in enumerate(atoms.get_chemical_symbols()):
+            if symbol in species:
+                species[symbol].append(a)
+            else:
+                species[symbol] = [a]
+                symbols.append(symbol)
+        fd.write('atoms\n%d\n' % len(species))
+        scaled = atoms.get_scaled_positions()
+        for symbol in symbols:
+            fd.write("'%s.in'\n" % symbol)
+            fd.write('%d\n' % len(species[symbol]))
+            for a in species[symbol]:
+                fd.write('%.14f %.14f %.14f 0.0 0.0 0.0\n' % tuple(scaled[a]))
+
+        customspecies = self.rmt
+        if customspecies:
+            # custom species definitions
+            fd.write("\n")
+            sfile = os.path.join(os.environ['ELK_SPECIES_PATH'], 'elk.in')
+            assert os.path.exists(sfile)
+            slines = open(sfile, 'r').readlines()
+            # remove unused species
+            for s in customspecies.keys():
+                if s not in species.keys():
+                    customspecies.pop(s)
+            # add undefined species with defaults
+            for s in species.keys():
+                if s not in customspecies.keys():
+                    # use default rmt for undefined species
+                    customspecies.update({s: 0.0})
+            # write custom species into elk.in
+            skeys = list(set(customspecies.keys())) # unique
+            skeys.sort()
+            for s in skeys:
+                found = False
+                for n, line in enumerate(slines):
+                    if line.find("'" + s + "'") > -1:
+                        begline = n - 1
+                for n, line in enumerate(slines[begline:]):
+                    if not line.strip(): # first empty line
+                        endline = n
+                        found = True
+                        break
+                assert found
+                fd.write("species\n")
+                # set rmt on third line
+                rmt = customspecies[s]
+                assert isinstance(rmt, (float,int))
+                if rmt <= 0.0: # relative
+                    # split needed because H is defined with comments
+                    newrmt = float(slines[begline + 3].split()[0].strip()) + rmt
+                else:
+                    newrmt = rmt
+                slines[begline + 3] = '%6s\n' % str(newrmt)
+                for l in slines[begline: begline + endline]:
+                    fd.write('%s' % l)
+                fd.write("\n")
+        else:
+            # use default species
+            # if sppath is present in elk.in it overwrites species blocks!
+            fd.write("sppath\n'%s'\n\n" % os.environ['ELK_SPECIES_PATH'])
+
+
+    def read_fermi(self):
+        """Method that reads Fermi energy in Hartree from the output file
+        and returns it in eV"""
+        E_f=None
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            text = open(INFO_file).read().lower()
+            assert 'convergence targets achieved' in text
+            assert not 'reached self-consistent loops maximum' in text
+            for line in iter(text.split('\n')):
+                if line.rfind('fermi                       :') > -1:
+                    E_f = float(line.split(':')[1].strip())
+            E_f = E_f*Hartree
+        return E_f
+
+    def read_eigenvalues(self, kpt=0, spin=0, mode='eigenvalues'):
+        """ Returns list of last eigenvalues, occupations
+        for given kpt and spin.  """
+        values = []
+        assert mode in ['eigenvalues' , 'occupations'], 'mode not in [\'eigenvalues\' , \'occupations\']'
+        EIGVAL_file = '%s/EIGVAL.OUT' % self.dir
+        if os.path.isfile(EIGVAL_file) or os.path.islink(EIGVAL_file):
+            lines = open(EIGVAL_file).readlines()
+            nstsv = None
+            for line in lines:
+                if line.rfind(': nstsv') > -1:
+                    nstsv = int(line.split(':')[0].strip())
+                    break
+            assert not nstsv is None
+            kpts = None
+            for line in lines:
+                if line.rfind(': nkpt') > -1:
+                    kpts = int(line.split(':')[0].strip())
+                    break
+            assert not kpts is None
+            text = lines[3:] # remove first 3 lines
+            # find the requested k-point
+            beg = 2 + (nstsv + 4) * kpt
+            end = beg + nstsv
+            if self.get_spin_polarized():
+                # elk prints spin-up and spin-down together
+                if spin == 0:
+                    beg = beg
+                    end = beg + nstsv / 2
+                else:
+                    beg = beg - nstsv / 2 - 3
+                    end = end
+            values = []
+            for line in text[beg:end]:
+                b = [float(c.strip()) for c in line.split()[1:]]
+                values.append(b)
+            if mode == 'eigenvalues':
+                values = [Hartree*v[0] for v in values]
+            else:
+                values = [v[1] for v in values]
+            if len(values) == 0:
+                values = None
+            return np.array(values)
+
+    def read(self):
+        fd = open('%s/TOTENERGY.OUT' % self.dir, 'r')
+        self.energy = float(fd.readlines()[-1]) * Hartree
+        # Forces:
+        INFO_file = '%s/INFO.OUT' % self.dir
+        if os.path.isfile(INFO_file) or os.path.islink(INFO_file):
+            text = open(INFO_file).read().lower()
+            assert 'convergence targets achieved' in text
+            assert not 'reached self-consistent loops maximum' in text
+            lines = iter(text.split('\n'))
+            forces = []
+            atomnum = 0
+            for line in lines:
+                if line.rfind('total force') > -1:
+                    forces.append(np.array([float(f) for f in line.split(':')[1].split()]))
+                    atomnum =+ 1
+            self.forces = np.array(forces)
+        else:
+            raise RuntimeError
+        # Stress
+        self.stress = np.empty((3, 3))
+        self.nbands = self.read_number_of_bands()
+        self.nelect = self.read_number_of_electrons()
+        self.swidth = self.read_electronic_temperature()
+        self.niter = self.read_number_of_iterations()
+        self.magnetic_moment = self.read_magnetic_moment()
diff --git a/ase/calculators/emt.py b/ase/calculators/emt.py
new file mode 100644
index 0000000..fd6d217
--- /dev/null
+++ b/ase/calculators/emt.py
@@ -0,0 +1,273 @@
+"""Effective medium theory potential."""
+
+from math import sqrt, exp, log, pi
+
+import numpy as np
+import sys
+
+from ase.data import chemical_symbols
+from ase.units import Bohr
+from ase.calculators.neighborlist import NeighborList
+
+
+parameters = {
+    #      E0     s0    V0     eta2    kappa   lambda  n0
+    #      eV     bohr  eV     bohr^-1 bohr^-1 bohr^-1 bohr^-3
+    'Al': (-3.28, 3.00, 1.493, 1.240,  2.000,  1.169,  0.00700),
+    'Cu': (-3.51, 2.67, 2.476, 1.652,  2.740,  1.906,  0.00910),
+    'Ag': (-2.96, 3.01, 2.132, 1.652,  2.790,  1.892,  0.00547),
+    'Au': (-3.80, 3.00, 2.321, 1.674,  2.873,  2.182,  0.00703),
+    'Ni': (-4.44, 2.60, 3.673, 1.669,  2.757,  1.948,  0.01030),
+    'Pd': (-3.90, 2.87, 2.773, 1.818,  3.107,  2.155,  0.00688),
+    'Pt': (-5.85, 2.90, 4.067, 1.812,  3.145,  2.192,  0.00802),
+    # extra parameters - just for fun ...
+    'H':  (-3.21, 1.31, 0.132, 2.652,  2.790,  3.892,  0.00547),
+    'C':  (-3.50, 1.81, 0.332, 1.652,  2.790,  1.892,  0.01322),
+    'N':  (-5.10, 1.88, 0.132, 1.652,  2.790,  1.892,  0.01222),
+    'O':  (-4.60, 1.95, 0.332, 1.652,  2.790,  1.892,  0.00850)}
+
+beta = 1.809     # (16 * pi / 3)**(1.0 / 3) / 2**0.5,
+                 # but preserve historical rounding
+
+
+class EMT:
+    disabled = False  # Set to True to disable (asap does this).
+    
+    def __init__(self, fakestress=False):
+        self.energy = None
+        self.name = 'EMT'
+        self.version = '1.0'
+        # fakestress is needed to fake some stress value for the testsuite
+        # in order to test the filter functionality.
+        self.fakestress = fakestress
+        if self.disabled:
+            print >> sys.stderr, """
+            ase.EMT has been disabled by Asap.  Most likely, you
+            intended to use Asap's EMT calculator, but accidentally
+            imported ase's EMT calculator after Asap's.  This could
+            happen if your script contains the lines
+
+              from asap3 import *
+              from ase.calculators.emt import EMT
+            Swap the two lines to solve the problem.
+
+            (or 'from ase import *' in older versions of ASE.) Swap 
+            the two lines to solve the problem.
+
+            In the UNLIKELY event that you actually wanted to use
+            ase.calculators.emt.EMT although asap3 is loaded into memory,
+            please reactivate it with the command
+              ase.calculators.emt.EMT.disabled = False
+            """
+            raise RuntimeError('ase.EMT has been disabled.  ' +
+                               'See message printed above.')
+        
+    def get_name(self):
+        return self.name
+
+    def get_version(self):
+        return self.version
+
+    def get_spin_polarized(self):
+        return False
+    
+    def initialize(self, atoms):
+        self.par = {}
+        self.rc = 0.0
+        self.numbers = atoms.get_atomic_numbers()
+        maxseq = max(par[1] for par in parameters.values()) * Bohr
+        rc = self.rc = beta * maxseq * 0.5 * (sqrt(3) + sqrt(4))
+        rr = rc * 2 * sqrt(4) / (sqrt(3) + sqrt(4))
+        self.acut = np.log(9999.0) / (rr - rc)
+        for Z in self.numbers:
+            if Z not in self.par:
+                p = parameters[chemical_symbols[Z]]
+                s0 = p[1] * Bohr
+                eta2 = p[3] / Bohr
+                kappa = p[4] / Bohr
+                x = eta2 * beta * s0
+                gamma1 = 0.0
+                gamma2 = 0.0
+                for i, n in enumerate([12, 6, 24]):
+                    r = s0 * beta * sqrt(i + 1)
+                    x = n / (12 * (1.0 + exp(self.acut * (r - rc))))
+                    gamma1 += x * exp(-eta2 * (r - beta * s0))
+                    gamma2 += x * exp(-kappa / beta * (r - beta * s0))
+
+                self.par[Z] = {'E0': p[0],
+                               's0': s0,
+                               'V0': p[2],
+                               'eta2': eta2,
+                               'kappa': kappa,
+                               'lambda': p[5] / Bohr,
+                               'n0': p[6] / Bohr**3,
+                               'rc': rc,
+                               'gamma1': gamma1,
+                               'gamma2': gamma2}
+                #if rc + 0.5 > self.rc:
+                #    self.rc = rc + 0.5
+
+        self.ksi = {}
+        for s1, p1 in self.par.items():
+            self.ksi[s1] = {}
+            for s2, p2 in self.par.items():
+                #self.ksi[s1][s2] = (p2['n0'] / p1['n0'] *
+                #                    exp(eta1 * (p1['s0'] - p2['s0'])))
+                self.ksi[s1][s2] = p2['n0'] / p1['n0']
+                
+        self.forces = np.empty((len(atoms), 3))
+        self.sigma1 = np.empty(len(atoms))
+        self.deds = np.empty(len(atoms))
+                    
+        self.nl = NeighborList([0.5 * self.rc + 0.25] * len(atoms),
+                               self_interaction=False)
+
+    def update(self, atoms):
+        if (self.energy is None or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def calculation_required(self, atoms, quantities):
+        if len(quantities) == 0:
+            return False
+
+        return (self.energy is None or
+                len(self.numbers) != len(atoms) or
+                (self.numbers != atoms.get_atomic_numbers()).any() or
+                (self.positions != atoms.get_positions()).any() or
+                (self.pbc != atoms.get_pbc()).any() or
+                (self.cell != atoms.get_cell()).any())
+                
+    def get_number_of_iterations(self):
+        return 0
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.energy
+
+    def get_numeric_forces(self, atoms):
+        self.update(atoms)
+        p = atoms.positions
+        p0 = p.copy()
+        forces = np.empty_like(p)
+        eps = 0.0001
+        for a in range(len(p)):
+            for c in range(3):
+                p[a, c] += eps
+                self.calculate(atoms)
+                de = self.energy
+                p[a, c] -= 2 * eps
+                self.calculate(atoms)
+                de -= self.energy
+                p[a, c] += eps
+                forces[a, c] = -de / (2 * eps)
+        p[:] = p0
+        return forces
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+    
+    def get_stress(self, atoms):
+        if self.fakestress:
+            return np.zeros((6))
+        else:
+            raise NotImplementedError
+    
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+        
+        self.nl.update(atoms)
+        
+        self.energy = 0.0
+        self.sigma1[:] = 0.0
+        self.forces[:] = 0.0
+        
+        natoms = len(atoms)
+
+        for a1 in range(natoms):
+            Z1 = self.numbers[a1]
+            p1 = self.par[Z1]
+            ksi = self.ksi[Z1]
+            neighbors, offsets = self.nl.get_neighbors(a1)
+            offsets = np.dot(offsets, atoms.cell)
+            for a2, offset in zip(neighbors, offsets):
+                d = self.positions[a2] + offset - self.positions[a1]
+                r = sqrt(np.dot(d, d))
+                if r < self.rc + 0.5:
+                    Z2 = self.numbers[a2]
+                    p2 = self.par[Z2]
+                    self.interact1(a1, a2, d, r, p1, p2, ksi[Z2])
+                                
+        for a in range(natoms):
+            Z = self.numbers[a]
+            p = self.par[Z]
+            try:
+                ds = -log(self.sigma1[a] / 12) / (beta * p['eta2'])
+            except (OverflowError, ValueError):
+                self.deds[a] = 0.0
+                self.energy -= p['E0']
+                continue
+            x = p['lambda'] * ds
+            y = exp(-x)
+            z = 6 * p['V0'] * exp(-p['kappa'] * ds)
+            self.deds[a] = ((x * y * p['E0'] * p['lambda'] + p['kappa'] * z) /
+                            (self.sigma1[a] * beta * p['eta2']))
+            e = p['E0'] * ((1 + x) * y - 1) + z
+            self.energy += p['E0'] * ((1 + x) * y - 1) + z
+
+        for a1 in range(natoms):
+            Z1 = self.numbers[a1]
+            p1 = self.par[Z1]
+            ksi = self.ksi[Z1]
+            neighbors, offsets = self.nl.get_neighbors(a1)
+            offsets = np.dot(offsets, atoms.cell)
+            for a2, offset in zip(neighbors, offsets):
+                d = self.positions[a2] + offset - self.positions[a1]
+                r = sqrt(np.dot(d, d))
+                if r < self.rc + 0.5:
+                    Z2 = self.numbers[a2]
+                    p2 = self.par[Z2]
+                    self.interact2(a1, a2, d, r, p1, p2, ksi[Z2])
+
+    def interact1(self, a1, a2, d, r, p1, p2, ksi):
+        x = exp(self.acut * (r - self.rc))
+        theta = 1.0 / (1.0 + x)
+        y1 = (0.5 * p1['V0'] * exp(-p2['kappa'] * (r / beta - p2['s0'])) *
+              ksi / p1['gamma2'] * theta)
+        y2 = (0.5 * p2['V0'] * exp(-p1['kappa'] * (r / beta - p1['s0'])) /
+              ksi / p2['gamma2'] * theta)
+        self.energy -= y1 + y2
+        f = ((y1 * p2['kappa'] + y2 * p1['kappa']) / beta +
+             (y1 + y2) * self.acut * theta * x) * d / r
+        self.forces[a1] += f
+        self.forces[a2] -= f
+        self.sigma1[a1] += (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
+                            ksi * theta / p1['gamma1'])
+        self.sigma1[a2] += (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
+                            ksi * theta / p2['gamma1'])
+
+    def interact2(self, a1, a2, d, r, p1, p2, ksi):
+        x = exp(self.acut * (r - self.rc))
+        theta = 1.0 / (1.0 + x)
+        y1 = (exp(-p2['eta2'] * (r - beta * p2['s0'])) *
+              ksi / p1['gamma1'] * theta * self.deds[a1])
+        y2 = (exp(-p1['eta2'] * (r - beta * p1['s0'])) /
+              ksi / p2['gamma1'] * theta * self.deds[a2])
+        f = ((y1 * p2['eta2'] + y2 * p1['eta2']) +
+             (y1 + y2) * self.acut * theta * x) * d / r
+        self.forces[a1] -= f
+        self.forces[a2] += f
+
+    def set_atoms(self,*args,**kwargs):
+        'empty function for compatibility with other calculators and tests'
+        pass
+
diff --git a/ase/calculators/exciting.py b/ase/calculators/exciting.py
new file mode 100644
index 0000000..1231043
--- /dev/null
+++ b/ase/calculators/exciting.py
@@ -0,0 +1,137 @@
+import os
+import numpy as np
+from ase.units import Bohr, Hartree
+
+
+class Exciting:
+    """exciting calculator object."""
+   
+    def __init__(self, dir='calc', template=None, 
+                 speciespath='http://xml.exciting-code.org/species/',
+                 bin='excitingser', kpts=(1, 1, 1), **kwargs):
+        """Exciting calculator object constructor
+        
+        Parameters
+        ----------
+        dir: string
+            directory in which to execute exciting
+        template: string
+            Path to XSLT template if it should be used
+            default: none
+        speciespath: string
+            Directory or URL to look up species files
+        bin: string
+            Path or executable name of exciting 
+            default: ``excitingser`` 
+        kpts: integer list length 3
+            Number of k-points
+        kwargs: dictionary like
+            list of key value pairs to be converted into groundstate attributes
+        
+        """
+        self.dir = dir
+        self.energy = None
+        self.template = template
+     
+        self.speciespath = speciespath
+        self.converged = False
+        self.excitingbinary = bin
+        self.groundstate_attributes = kwargs
+        if not 'ngridk' in kwargs.keys():
+            self.groundstate_attributes['ngridk'] = ' '.join(map(str, kpts))
+
+    def update(self, atoms):
+        if (not self.converged or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        self.numbers = atoms.get_atomic_numbers().copy()
+        self.write(atoms)
+
+    def get_potential_energy(self, atoms):
+        """
+        returns potential Energy
+        """
+        if self.energy is None:
+            self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+
+#    def get_stress(self, atoms):
+#        self.update(atoms)
+#        return self.stress.copy()
+
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        self.initialize(atoms)
+        syscall = ('cd %(dir)s; %(bin)s;' %
+                   {'dir': self.dir, 'bin': self.excitingbinary})
+        print syscall
+        assert os.system(syscall ) == 0
+        self.read()
+
+    def write(self, atoms):
+        from lxml import etree as ET
+        from ase.io.exciting import  atoms2etree
+        if not os.path.isdir(self.dir):
+            os.mkdir(self.dir)
+        root = atoms2etree(atoms)
+        root.find('structure').attrib['speciespath'] = self.speciespath
+        root.find('structure').attrib['autormt'] = 'false'
+        groundstate = ET.SubElement(root, 'groundstate', tforce='true')
+        for key, value in self.groundstate_attributes.items():
+            groundstate.attrib[key] = str(value)
+        if self.template:
+            xslf = open(self.template, 'r')
+            xslt_doc = ET.parse(xslf)
+            transform = ET.XSLT(xslt_doc)
+            result = transform(root)
+            fd = open('%s/input.xml' % self.dir, 'w')
+            fd.write(ET.tostring(result, method='xml', pretty_print=True,
+                                 xml_declaration=True, encoding='UTF-8'))
+            fd.close()
+        else:
+            fd = open('%s/input.xml' % self.dir, 'w')
+            fd.write(ET.tostring(root, method='xml', pretty_print=True,
+                                 xml_declaration=True, encoding='UTF-8'))
+            fd.close()
+        
+    def read(self):
+        """ reads Total energy and forces from info.xml
+        """
+        from lxml import etree as ET
+        INFO_file = '%s/info.xml' % self.dir
+   
+        try:
+            fd = open(INFO_file)
+        except IOError:
+            print "file doesn't exist"
+        info = ET.parse(fd)
+        self.energy = float(info.xpath('//@totalEnergy')[-1]) * Hartree
+        forces = []
+        forcesnodes = info.xpath(
+            '//structure[last()]/species/atom/forces/totalforce/@*')
+        for force in forcesnodes:
+            forces.append(np.array(float(force)))
+        self.forces = np.reshape(forces, (-1, 3))
+        
+        if str(info.xpath('//groundstate/@status')[0]) == 'finished':
+            self.converged = True
+        else:
+            raise RuntimeError('calculation did not finish correctly')
+  
+        # Stress
+        # self.stress = np.empty((3, 3))
diff --git a/ase/calculators/fleur.py b/ase/calculators/fleur.py
new file mode 100644
index 0000000..3fb316b
--- /dev/null
+++ b/ase/calculators/fleur.py
@@ -0,0 +1,514 @@
+"""This module defines an ASE interface to FLAPW code FLEUR.
+
+http://www.flapw.de
+"""
+
+import os
+try:
+    from subprocess import Popen, PIPE
+except ImportError:
+    from os import popen3
+else:
+    def popen3(cmd):
+        p = Popen(cmd, shell=True, close_fds=True,
+                  stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        return p.stdin, p.stdout, p.stderr
+import re
+
+import numpy as np
+
+from ase.units import Hartree, Bohr
+
+class FLEUR:
+    """Class for doing FLEUR calculations.
+
+    In order to use fleur one has to define the following environment
+    variables:
+
+    FLEUR_INPGEN path to the input generator (inpgen.x) of fleur
+
+    FLEUR path to the fleur executable. Note that fleur uses different
+    executable for real and complex cases (systems with/without inversion
+    symmetry), so FLEUR must point to the correct executable.
+
+    The initialize_density step can be performed in parallel
+    only if run on one compute node. FLEUR_SERIAL is used for this step.
+
+    It is probable that user needs to tune manually the input file before
+    the actual calculation, so in addition to the standard
+    get_potential_energy function this class defines the following utility
+    functions:
+
+    write_inp
+        generate the input file `inp`
+    initialize_density
+        creates the initial density after possible manual edits of `inp`
+    calculate
+        convergence the total energy. With fleur, one specifies always
+        only the number of SCF-iterations so this function launches
+        the executable several times and monitors the convergence.
+    relax
+        Uses fleur's internal algorithm for structure
+        optimization. Requires that the proper optimization parameters
+        (atoms to optimize etc.) are specified by hand in `inp`
+
+    """
+    def __init__(self, xc='LDA', kpts=None, nbands=None, convergence=None,
+                 width=None, kmax=None, mixer=None, maxiter=None,
+                 maxrelax=20, workdir=None, equivatoms=True):
+
+        """Construct FLEUR-calculator object.
+
+        Parameters
+        ==========
+        xc: str
+            Exchange-correlation functional. Must be one of LDA, PBE,
+            RPBE.
+        kpts: list of three int
+            Monkhost-Pack sampling.
+        nbands: int
+            Number of bands. (not used at the moment)
+        convergence: dictionary
+            Convergence parameters (currently only energy in eV)
+            {'energy' : float}
+        width: float
+            Fermi-distribution width in eV.
+        kmax: float
+            Plane wave cutoff in a.u. If kmax is set then:
+            gmax = 3.0 * kmax
+            gmaxxc = int(2.5 * kmax * 10)/10. (from set_inp.f)
+        mixer: dictionary
+            Mixing parameters imix, alpha, spinf
+            {'imix' : int, 'alpha' : float, 'spinf' : float}
+        maxiter: int
+            Maximum number of SCF iterations (name in the code: itmax)
+        maxrelax: int
+            Maximum number of relaxation steps
+        workdir: str
+            Working directory for the calculation
+        equivatoms: bool
+            If False: generate inequivalent atoms (default is True).
+            Setting to False allows one for example to calculate spin-polarized dimers.
+            Ssee http://www.flapw.de/pm/index.php?n=User-Documentation.InputFileForTheInputGenerator.
+        """
+
+        self.xc = xc
+        self.kpts = kpts
+        self.nbands = nbands
+        self.width = width
+        self.kmax = kmax
+        self.itmax_step_default = 9 # SCF steps per run (default)
+        self.itmax_step = 5 # SCF steps per run
+        assert self.itmax_step_default <= 9
+        assert self.itmax_step <= self.itmax_step_default
+        self.itmax_default = 40
+        if maxiter is None:
+            self.itmax = self.itmax_default
+        else:
+            self.itmax = maxiter
+        self.maxrelax = maxrelax
+        self.mixer = mixer
+
+        if convergence:
+            self.convergence = convergence 
+            self.convergence['energy'] /= Hartree 
+        else:
+            self.convergence = {'energy' : 0.0001}
+
+        self.start_dir = None
+        self.workdir = workdir
+        if self.workdir:
+            self.start_dir = os.getcwd()
+            if not os.path.isdir(workdir):
+                os.mkdir(workdir)
+        else:
+            self.workdir = '.'
+            self.start_dir = '.'
+
+        self.equivatoms = equivatoms
+
+        self.converged = False
+
+    def run_executable(self, mode='fleur', executable='FLEUR'):
+
+        assert executable in ['FLEUR', 'FLEUR_SERIAL']
+
+        executable_use = executable
+        if executable == 'FLEUR_SERIAL' and not os.environ.get(executable, ''):
+            executable_use = 'FLEUR' # use FLEUR if FLEUR_SERIAL not set
+        try:
+            code_exe = os.environ[executable_use]
+        except KeyError:
+            raise RuntimeError('Please set ' + executable_use)
+        p = Popen(code_exe, shell=True, stdin=PIPE, stdout=PIPE,
+                  stderr=PIPE)
+        stat = p.wait()
+        out = p.stdout.read()
+        err = p.stderr.read()
+        print mode, ': stat= ', stat, ' out= ', out, ' err=', err
+        # special handling of exit status from density generation and regular fleur.x
+        if mode in ['density']:
+            if '!' in err:
+                raise RuntimeError(executable_use + ' exited with a code %s' % err)
+        else:
+            if stat != 0:
+                raise RuntimeError(executable_use + ' exited with a code %d' % stat)
+
+
+    def update(self, atoms):
+        """Update a FLEUR calculation."""
+
+        if (not self.converged or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.converged = False
+            self.initialize(atoms)
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        """Create an input file inp and generate starting density."""
+
+        self.converged = False
+        self.initialize_inp(atoms)
+        self.initialize_density(atoms)
+
+    def initialize_inp(self, atoms):
+        """Create a inp file"""
+        os.chdir(self.workdir)
+
+        self.numbers = atoms.get_atomic_numbers().copy()
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        # create the input
+        self.write_inp(atoms)
+
+        os.chdir(self.start_dir)
+
+    def initialize_density(self, atoms):
+        """Creates a new starting density."""
+
+        os.chdir(self.workdir)
+        # remove possible conflicting files
+        files2remove = ['cdn1', 'fl7para', 'stars', 'wkf2', 'enpara',
+                        'kpts', 'broyd', 'broyd.7', 'tmat', 'tmas']
+        if 0:
+            # avoid STOP bzone3 error by keeping the kpts file
+            files2remove.remove('kpts')
+
+        for f in files2remove:
+            if os.path.isfile(f):
+                os.remove(f)
+
+        # generate the starting density
+        os.system("sed -i -e 's/strho=./strho=T/' inp")
+        self.run_executable(mode='density', executable='FLEUR_SERIAL')
+        os.system("sed -i -e 's/strho=./strho=F/' inp")
+
+        os.chdir(self.start_dir)
+        # generate spin-polarized density
+        # http://www.flapw.de/pm/index.php?n=User-Documentation.Magnetism
+        if atoms.get_initial_magnetic_moments().sum() > 0.0:
+            os.chdir(self.workdir)
+            # generate cdnc file (1 SCF step: swsp=F - non-magnetic)
+            os.system("sed -i -e 's/itmax=.*,maxiter/itmax= 1,maxiter/' inp")
+            self.run_executable(mode='cdnc', executable='FLEUR')
+            sedline = "'s/itmax=.*,maxiter/itmax= '"
+            sedline += str(self.itmax_step_default) + "',maxiter/'"
+            os.system("sed -i -e " + sedline + " inp")
+            # generate spin polarized density (swsp=T)
+            os.system("sed -i -e 's/swsp=./swsp=T/' inp")
+            self.run_executable(mode='swsp', executable='FLEUR_SERIAL')
+            # restore swsp=F
+            os.system("sed -i -e 's/swsp=./swsp=F/' inp")
+            os.chdir(self.start_dir)
+
+    def get_potential_energy(self, atoms, force_consistent=False):
+        self.update(atoms)
+
+        if force_consistent:
+            return self.efree * Hartree
+        else:
+            # Energy extrapolated to zero Kelvin:
+            return  (self.etotal + self.efree) / 2 * Hartree
+
+    def get_number_of_iterations(self, atoms):
+        self.update(atoms)
+        return self.niter
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        # electronic structure is converged, so let's calculate forces:
+        # TODO
+        return np.array((0.0, 0.0, 0.0))
+
+    def get_stress(self, atoms):
+        raise NotImplementedError
+
+    def get_dipole_moment(self, atoms):
+        """Returns total dipole moment of the system."""
+        raise NotImplementedError
+
+    def calculate(self, atoms):
+        """Converge a FLEUR calculation to self-consistency.
+
+           Input files should be generated before calling this function
+           FLEUR performs always fixed number of SCF steps. This function
+           reduces the number of iterations gradually, however, a minimum
+           of five SCF steps is always performed.
+        """
+
+        os.chdir(self.workdir)
+
+        self.niter = 0
+        out = ''
+        err = ''
+        while not self.converged:
+            if self.niter > self.itmax:
+                raise RuntimeError('FLEUR failed to convergence in %d iterations' % self.itmax)
+
+            self.run_executable(mode='fleur', executable='FLEUR')
+
+            # catenate new output with the old one
+            os.system('cat out >> out.old')
+            self.read()
+            self.check_convergence()
+
+        if os.path.exists('out.old'): os.rename('out.old', 'out')
+        # After convergence clean up broyd* files
+        os.system('rm -f broyd*')
+        os.chdir(self.start_dir)
+        return out, err
+
+    def relax(self, atoms):
+        """Currently, user has to manually define relaxation parameters
+           (atoms to relax, relaxation directions, etc.) in inp file
+           before calling this function."""
+
+        nrelax = 0
+        relaxed = False
+        while not relaxed:
+            # Calculate electronic structure
+            self.calculate(atoms)
+            # Calculate the Pulay forces
+            os.system("sed -i -e 's/l_f=./l_f=T/' inp")
+            while True:
+                self.converged = False
+                out, err = self.calculate(atoms)
+                if 'GEO new' in err:
+                    os.chdir(self.workdir)
+                    os.rename('inp_new', 'inp')
+                    os.chdir(self.start_dir)
+                    break
+            if 'GEO: Des woas' in err:
+                relaxed = True
+                break
+            nrelax += 1
+            # save the out and cdn1 files
+            os.system('cp out out_%d' % nrelax)
+            os.system('cp cdn1 cdn1_%d' % nrelax)
+            if nrelax > self.maxrelax:
+                raise RuntimeError('Failed to relax in %d iterations' % self.maxrelax)
+            self.converged = False
+
+
+    def write_inp(self, atoms):
+        """Write the `inp` input file of FLEUR.
+
+        First, the information from Atoms is written to the simple input
+        file and the actual input file `inp` is then generated with the
+        FLEUR input generator. The location of input generator is specified
+        in the environment variable FLEUR_INPGEN.
+
+        Finally, the `inp` file is modified according to the arguments of
+        the FLEUR calculator object.
+        """
+
+        fh = open('inp_simple', 'w')
+        fh.write('FLEUR input generated with ASE\n')
+        fh.write('\n')
+
+        if atoms.pbc[2]:
+            film = 'f'
+        else:
+            film = 't'
+        fh.write('&input film=%s /' % film)
+        fh.write('\n')
+
+        for vec in atoms.get_cell():
+            fh.write(' ')
+            for el in vec:
+                fh.write(' %21.16f' % (el/Bohr))
+            fh.write('\n')
+        fh.write(' %21.16f\n' % 1.0)
+        fh.write(' %21.16f %21.16f %21.16f\n' % (1.0, 1.0, 1.0))
+        fh.write('\n')
+
+        natoms = len(atoms)
+        fh.write(' %6d\n' % natoms)
+        positions = atoms.get_scaled_positions()
+        if not atoms.pbc[2]:
+            # in film calculations z position has to be in absolute
+            # coordinates and symmetrical
+            cart_pos = atoms.get_positions()
+            cart_pos[:, 2] -= atoms.get_cell()[2, 2]/2.0
+            positions[:, 2] = cart_pos[:, 2] / Bohr
+        atomic_numbers = atoms.get_atomic_numbers()
+        for n, (Z, pos) in enumerate(zip(atomic_numbers, positions)):
+            if self.equivatoms:
+                fh.write('%3d' % Z)
+            else:
+                # generate inequivalent atoms, by using non-integer Z
+                # (only the integer part will be used as Z of the atom)
+                # see http://www.flapw.de/pm/index.php?n=User-Documentation.InputFileForTheInputGenerator
+                fh.write('%3d.%04d' % (Z, n)) # MDTMP don't think one can calculate more that 10**4 atoms
+            for el in pos:
+                fh.write(' %21.16f' % el)
+            fh.write('\n')
+
+        # avoid "STOP read_record: ERROR reading input"
+        fh.write('&end /')
+
+        fh.close()
+        try:
+            inpgen = os.environ['FLEUR_INPGEN']
+        except KeyError:
+            raise RuntimeError('Please set FLEUR_INPGEN')
+
+        # rename the previous inp if it exists
+        if os.path.isfile('inp'):
+            os.rename('inp', 'inp.bak')
+        os.system('%s < inp_simple' % inpgen)
+
+        # read the whole inp-file for possible modifications
+        fh = open('inp', 'r')
+        lines = fh.readlines()
+        fh.close()
+
+
+        window_ln = -1
+        for ln, line in enumerate(lines):
+            # XC potential
+            if line.startswith('pbe'):
+                if self.xc == 'PBE':
+                    pass
+                elif self.xc == 'RPBE':
+                    lines[ln] = 'rpbe   non-relativi\n'
+                elif self.xc == 'LDA':
+                    lines[ln] = 'mjw    non-relativic\n'
+                    del lines[ln+1]
+                else:
+                    raise RuntimeError('XC-functional %s is not supported' % self.xc)
+            if line.startswith('Window'):
+                # few things are set around this line
+                window_ln = ln
+            # kmax
+            if self.kmax and ln == window_ln:
+                line = '%10.5f\n' % self.kmax
+                lines[ln+2] = line
+            # gmax   cutoff for PW-expansion of potential & density  ( > 2*kmax)
+            # gmaxxc cutoff for PW-expansion of XC-potential ( > 2*kmax, < gmax)
+            if self.kmax and line.startswith('vchk'):
+                gmax = 3. * self.kmax
+                line = ' %10.6f %10.6f\n' % (gmax, int(2.5 * self.kmax * 10)/10.)
+                lines[ln-1] = line
+            # Fermi width
+            if self.width and line.startswith('gauss'):
+                line = 'gauss=F   %7.5ftria=F\n' % (self.width / Hartree)
+                lines[ln] = line
+            # kpts
+            if self.kpts and line.startswith('nkpt'):
+                line = 'nkpt=      nx=%2d,ny=%2d,nz=%2d\n' % (self.kpts[0],
+                                                              self.kpts[1],
+                                                              self.kpts[2])
+                lines[ln] = line
+            # itmax
+            if self.itmax < self.itmax_step_default and line.startswith('itmax'):
+                # decrease number of SCF steps; increasing is done by 'while not self.converged:'
+                lsplit = line.split(',')
+                if lsplit[0].find('itmax') != -1:
+                    lsplit[0] = 'itmax=' + ('%2d' % self.itmax)
+                    lines[ln] = ",".join(lsplit)
+            # Mixing
+            if self.mixer and line.startswith('itmax'):
+                imix = self.mixer['imix']
+                alpha = self.mixer['alpha']
+                spinf = self.mixer['spinf']
+                line_end = 'imix=%2d,alpha=%6.2f,spinf=%6.2f\n' % (imix,
+                                                                   alpha,
+                                                                   spinf)
+                line = line[:21] + line_end
+                lines[ln] = line
+            # jspins and swsp
+            if atoms.get_initial_magnetic_moments().sum() > 0.0:
+                assert not self.equivatoms, 'equivatoms currently not allowed in magnetic systems'
+                if line.find('jspins=1') != -1:
+                    lines[ln] = line.replace('jspins=1', 'jspins=2')
+                if line.startswith('swsp=F'):
+                    # setting initial magnetic moments for all atom types
+                    lines[ln] = 'swsp=F'
+                    for m in atoms.get_initial_magnetic_moments():
+                        lines[ln] += (' %5.2f' % m)
+                    lines[ln] += '\n'
+            # inpgen produces incorrect symbol 'J' for Iodine
+            if line.startswith(' J  53'):
+                lines[ln] = lines[ln].replace(' J  53', ' I  53')
+
+        # write everything back to inp
+        fh = open('inp', 'w')
+        for line in lines:
+            fh.write(line)
+        fh.close()
+
+    def read(self):
+        """Read results from FLEUR's text-output file `out`."""
+
+        lines = open('out', 'r').readlines()
+
+        # total energies
+        self.total_energies = []
+        pat = re.compile('(.*total energy=)(\s)*([-0-9.]*)')
+        for line in lines:
+            m = pat.match(line)
+            if m:
+                self.total_energies.append(float(m.group(3)))
+        self.etotal = self.total_energies[-1]
+
+        # free_energies
+        self.free_energies = []
+        pat = re.compile('(.*free energy=)(\s)*([-0-9.]*)')
+        for line in lines:
+            m = pat.match(line)
+            if m:
+                self.free_energies.append(float(m.group(3)))
+        self.efree = self.free_energies[-1]
+
+        # TODO forces, charge density difference...
+
+    def check_convergence(self):
+        """Check the convergence of calculation"""
+        energy_error = np.ptp(self.total_energies[-3:])
+        self.converged = energy_error < self.convergence['energy']
+
+        # TODO check charge convergence
+
+        # reduce the itmax in inp
+        lines = open('inp', 'r').readlines()
+        pat = re.compile('(itmax=)([ 0-9]*)')
+        fh = open('inp', 'w')
+        for line in lines:
+            m = pat.match(line)
+            if m:
+                itmax = int(m.group(2))
+                self.niter += itmax
+                itmax_new = itmax / 2
+                itmax = max(self.itmax_step, itmax_new)
+                line = 'itmax=%2d' % itmax + line[8:]
+            fh.write(line)
+        fh.close()
diff --git a/ase/calculators/general.py b/ase/calculators/general.py
new file mode 100644
index 0000000..3dd6b9d
--- /dev/null
+++ b/ase/calculators/general.py
@@ -0,0 +1,59 @@
+
+class Calculator:
+    def __init__(self):
+        return
+
+    def set_atoms(self, atoms):
+        self.atoms = atoms.copy()
+
+    def get_atoms(self):
+        atoms = self.atoms.copy()
+        atoms.set_calculator(self)
+        return atoms
+
+    def get_name(self):
+        """Return the name of the calculator (string).  """
+        raise NotImplementedError
+
+    def get_version(self):
+        """Return the version of the calculator (string).  """
+        raise NotImplementedError
+
+    def get_potential_energy(self, atoms, force_consistent=False):
+        self.update(atoms)
+        if force_consistent:
+            return self.energy_free
+        else:
+            return self.energy_zero
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress
+
+    def initialize(self, atoms):
+        """Prepare the input files required to
+        start the program (calculator).  """
+        raise NotImplementedError
+
+    def read(self, atoms):
+        self.positions = atoms.get_positions()
+        self.energy_free, self.energy_zero = self.read_energy()
+        self.forces = self.read_forces(atoms)
+        self.dipole = self.read_dipole()
+        self.fermi = self.read_fermi()
+        self.atoms = atoms.copy()
+        try:
+            self.nbands = self.read_nbands()
+        except NotImplementedError:
+            do_nothing = True
+        except AttributeError:
+            do_nothing = True
+        try:
+            self.stress = self.read_stress()
+        except NotImplementedError:
+            do_nothing = True
+        return
diff --git a/ase/calculators/interface.py b/ase/calculators/interface.py
new file mode 100644
index 0000000..e2b9dc4
--- /dev/null
+++ b/ase/calculators/interface.py
@@ -0,0 +1,147 @@
+import numpy as np
+
+
+class Calculator:
+    """Class for demonstrating the ASE-calculator interface.
+
+    A good implementation of a calculator should store a copy of the
+    atoms object used for the last calculation.  When one of the
+    *get_potential_energy*, *get_forces*, or *get_stress* methods is
+    called, the calculator should check if anything has changed since
+    the last calculation and only do the calculation if it's really
+    needed.  The Atoms class implements the methods *__eq__* and
+    *__ne__* that can be used for checking identity (using *==* and
+    *!=*): Two sets of atoms are considered identical if they have the
+    same positions, atomic numbers, unit cell and periodic boundary
+    conditions."""
+
+    def get_potential_energy(self, atoms=None, force_consistent=False):
+        """Return total energy.
+        
+        Both the energy extrapolated to zero Kelvin and the energy
+        consistent with the forces (the free energy) can be
+        returned."""
+        return 0.0
+        
+    def get_forces(self, atoms):
+        """Return the forces."""
+        return np.zeros((len(atoms), 3))
+                        
+    def get_stress(self, atoms):
+        """Return the stress."""
+        return np.zeros((3, 3))
+
+    def calculation_required(self, atoms, quantities):
+        """Check if a calculation is required.
+
+        Check if the quantities in the *quantities* list have already
+        been calculated for the atomic configuration *atoms*.  The
+        quantities can be one or more of: 'energy', 'forces', 'stress',
+        and 'magmoms'."""
+        return False
+
+    def set_atoms(self, atoms):
+        """Let the calculator know the atoms.
+
+        This method is optional.  If it exists, it will be called when
+        the *Atoms.set_calculator()* method is called.
+
+        *Don't* store a reference to *atoms* - that will create a
+         cyclic reference loop!  Store a copy instead."""
+        self.atoms = atoms.copy()
+
+
+class DFTCalculator(Calculator):
+    """Class for demonstrating the ASE interface to DFT-calculators."""
+    
+    def get_number_of_bands(self):
+        """Return the number of bands."""
+        return 42
+  
+    def get_xc_functional(self):
+        """Return the XC-functional identifier.
+        
+        'LDA', 'PBE', ..."""
+        return 'LDA'
+ 
+    def get_bz_k_points(self):
+        """Return all the k-points in the 1. Brillouin zone.
+
+        The coordinates are relative to reciprocal latice vectors."""
+        return np.zeros((1, 3))
+ 
+    def get_number_of_spins(self):
+        """Return the number of spins in the calculation.
+
+        Spin-paired calculations: 1, spin-polarized calculation: 2."""
+        return 1
+
+    def get_spin_polarized(self):
+        """Is it a spin-polarized calculation?"""
+        return False
+    
+    def get_ibz_k_points(self):
+        """Return k-points in the irreducible part of the Brillouin zone.
+
+        The coordinates are relative to reciprocal latice vectors."""
+        return np.zeros((1, 3))
+
+    def get_k_point_weights(self):
+        """Weights of the k-points. 
+        
+        The sum of all weights is one."""
+        return np.ones(1)
+
+    def get_pseudo_density(self, spin=None, pad=True):
+        """Return pseudo-density array.
+        
+        If *spin* is not given, then the total density is returned.
+        Otherwise, the spin up or down density is returned (spin=0 or
+        1)."""
+        return np.zeros((40, 40, 40))
+
+    def get_effective_potential(self, spin=0, pad=True):
+        """Return pseudo-effective-potential array."""
+        return np.zeros((40, 40, 40))
+
+    def get_pseudo_wave_function(self, band=0, kpt=0, spin=0, broadcast=True,
+                                 pad=True):
+        """Return pseudo-wave-function array."""
+        return np.zeros((40, 40, 40))
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        """Return eigenvalue array."""
+        return np.arange(42, float)
+    
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        """Return occupation number array."""
+        return np.ones(42)
+        
+    def get_fermi_level(self):
+        """Return the Fermi level."""
+        return 0.0
+
+    def initial_wannier(self, initialwannier, kpointgrid, fixedstates,
+                        edf, spin, nbands):
+        """Initial guess for the shape of wannier functions.
+
+        Use initial guess for wannier orbitals to determine rotation
+        matrices U and C.
+        """
+        raise NotImplementedError
+
+        return c, U
+
+    def get_wannier_localization_matrix(self, nbands, dirG, kpoint,
+                                        nextkpoint, G_I, spin):
+        """Calculate integrals for maximally localized Wannier functions."""
+
+    def get_magnetic_moment(self, atoms=None):
+        """Return the total magnetic moment."""
+        return self.occupation.magmom
+
+    def get_number_of_grid_points(self):
+        """Return the shape of arrays."""
+        return self.gd.N_c
+
+
diff --git a/ase/calculators/jacapo/ChangeLog b/ase/calculators/jacapo/ChangeLog
new file mode 100644
index 0000000..c8ede57
--- /dev/null
+++ b/ase/calculators/jacapo/ChangeLog
@@ -0,0 +1,81 @@
+2010-07-08  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.get_psp): added a line to return None when sym and z is None, that probably means nothing is being asked for, but that must be caught.
+
+2010-06-16  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.set_nbands): added code to delete teh number_of)bands nc dimension. if you change the number of bands in a calculator, you need to delete this so that the new dimension can be fixed.
+	(valid_kpts): changed ot (str(x.dtype)[0:5] to make sure the string comparison works.
+	(valid_kpts): fixed to [0:7]
+
+2010-06-02  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (ados_changed): fixed error in the comparison. it used to return True if ados was None and x is not None. I added cases for each possibility.
+	(calculate_stress_changed): added.
+	(xc_changed): added.
+
+2009-12-21  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (get_dipole_moment): fixed code to actually calculate the dipole moment.
+
+2009-12-08  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py: fixed get_forces for when atoms is None, e.g. when the calculator itself tries to get the forces, not the atoms.
+	(Jacapo.get_pseudopotentials): added this function.
+	(Jacapo.get_ncoutput): added this function.
+	(Jacapo.get_debug): added this function.
+	(_set_electronic_minimization): added all the _set_kw methods
+
+2009-04-14  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.get_spin_polarized): added variable existence check so that __str__ works if spin-polarization or nbands has not been defined.
+	(read_only_atoms): added reading the constraints from the netcdf file
+	(Jacapo.set_atoms): added support for saving a pickled string of the constraints to the netcdf file.
+
+2009-04-02  Lars C Grabow <grabow at fysik.dtu.dk>
+
+        * jacapo.py (Jacapo.initial_wannier): uses wannier.py instead of legacyASE2.py 
+          (Jacapo.get_wannier_localization_matrix): uses wannier.py instead of legacyASE2.py
+          (Jacapo.get_pseudo_wave_function): new implementation without legacyASE2.py
+          (Jacapo.get_wave_function): new implementation without legacyASE2.py
+          (Jacapo.get_fftgrid): closed open nc file.
+          (Jacapo.set_atoms): fixed the frame pointer bug
+          (Jacapo.calculate): fixed the frame pointer bug
+          (Jacapo.__init__): fixed the frame pointer bug
+          (Jacapo.get_effective_potential): using get_fftgrid() to get hardgrid
+          (Jacapo.get_electrostatic_potential): using get_fftgrid() to get hardgrid
+          (Jacapo.get_charge_density): using get_fftgrid() to get hardgrid
+         * utils/wannier.py: added.
+         * legacyASE2.py: removed.
+         * version.py: changed version number to 0.6.7
+
+2009-03-24  Lars C Grabow <grabow at fysik.dtu.dk>
+
+        * jacapo.py (__init__, __del__, _set_frame_number, _increment_frame, calculate, execute_external_dynamics, write_nc):
+          stay_alive support was added using a new variable self._frame.
+        (Jacapo.get_potential_energy): Now raises a runtime error if fortran executable didn't finish correctly.
+
+2009-02-05  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.get_charge_density): I had not divided the density array by the volume before. this is fixed now.
+
+2009-02-04  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.get_nbands): v.NumberOfBands was returning a list for some reason. I changed it to return a number
+	(Jacapo.set_fftgrid): fixed error in delete_ncattdimvar. ncfile arg was missing.
+
+2009-02-03  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.read_only_atoms): new ase suggests use set_initial_magnetic moments instead. 
+
+2009-02-02  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.set_fixmagmom): fixed spelling error in FixedMagneticMoment attribute
+	(Jacapo.set_kpts): added capability to specify a list of kpts for band structure calculations.
+
+	* 0.6.4 fixed error in placement of set_psp_database.
+
+2009-02-01  John Tester  <jkitchin at andrew.cmu.edu>
+
+	* jacapo.py (Jacapo.get_magnetic_moment): added.
+
diff --git a/ase/calculators/jacapo/__init__.py b/ase/calculators/jacapo/__init__.py
new file mode 100644
index 0000000..535d40d
--- /dev/null
+++ b/ase/calculators/jacapo/__init__.py
@@ -0,0 +1,17 @@
+import Scientific
+import string
+
+from ase.test import NotAvailable
+try:
+    version = string.split(Scientific.__version__,".")
+    if map(int,version) < [2,8]:
+        print 'your ScientifPython version is: ',Scientific.__version__ 
+        print 'ScientificPython 2.8 or greater required for numpy support in NetCDF'
+        raise NotAvailable('ScientificPython version 2.8 or greater is required')
+except AttributeError:
+    print 'It appears your ScientificPython has no version.'
+    print 'That probably means it is not 2.8, which is required'
+    raise Exception
+
+from jacapo import *
+
diff --git a/ase/calculators/jacapo/changed.py b/ase/calculators/jacapo/changed.py
new file mode 100644
index 0000000..3b5752a
--- /dev/null
+++ b/ase/calculators/jacapo/changed.py
@@ -0,0 +1,238 @@
+import numpy as np
+
+import logging
+log = logging.getLogger('Jacapo')
+
+'''
+provides functions to determine if an input parameter has changed.
+'''
+
+#######################################################################
+#### changed functions
+
+def kpts_changed(calc, x):
+    '''
+    check if kpt grid has changed.
+
+    we have to take care to generate the right k-points from x if
+    needed. if a user provides (4,4,4) we need to generate the MP
+    grid, etc...
+
+    Since i changed the MP code in set_kpts, there is some
+    incompatibility with old jacapo calculations and their MP
+    grids.
+    '''
+    #chadi-cohen
+    if isinstance(x, str):
+        exec('from ase.dft.kpoints import %s' % x)
+        listofkpts = eval(x)
+    #monkhorst-pack grid
+    elif np.array(x).shape == (3,):
+        from ase.dft.kpoints import monkhorst_pack
+        N1, N2, N3 = x
+        listofkpts = monkhorst_pack((N1, N2, N3))
+    #user-defined list is provided
+    elif len(np.array(x).shape) == 2:
+        listofkpts = np.array(x)
+    else:
+        raise Exception, 'apparent invalid setting for kpts'
+
+    grid = calc.get_kpts()
+    
+    if grid.shape != listofkpts.shape:
+        return True
+
+    if (abs(listofkpts - grid) < 1e-6).all():
+        return False
+    else:
+        return True
+
+def electronic_minimization_changed(calc, x):
+    myx = calc.get_electronic_minimization()
+
+    for key in myx:
+        if myx[key] != x[key]:
+            print key, myx[key], ' changed to ', x[key]
+            return True
+    return False
+
+def spinpol_changed(calc, x):
+    if x != calc.get_spinpol():
+        return True
+    else:
+        return False
+
+def symmetry_changed(calc, x):
+    if x != calc.get_symmetry():
+        return True
+    else:
+        return False
+
+def xc_changed(calc, x):
+    if x != calc.get_xc():
+        return True
+    return False
+
+def calculate_stress_changed(calc, x):
+    if x != calc.get_calculate_stress():
+        return True
+    return False
+
+def ados_changed(calc, x):
+    ados = calc.get_ados()
+
+    #ados may not be defined, and then None is returned
+    if ados is None and x is None:
+        return False
+    elif ados is None and x is not None:
+        return True
+    elif ados is not None and x is None:
+        return True
+
+    #getting here means ados and x are not none so we compare them
+    for key in x:
+        try:
+            if x[key] != ados[key]:
+                return True
+        except ValueError:
+            if (x[key] != ados[key]).all():
+                return True
+    return False
+
+def convergence_changed(calc, x):
+    conv = calc.get_convergence()
+    for key in x:
+        if x[key] != conv[key]:
+            return True
+    return False
+
+def charge_mixing_changed(calc, x):
+    cm = calc.get_charge_mixing()
+    if x is None and cm is None:
+        return False
+    else:
+        return True
+        
+    for key in x:
+        if x[key] != cm[key]:
+            return True
+    return False
+
+def decoupling_changed(calc, x):
+    pars = calc.get_decoupling()
+    for key in x:
+        if x[key] != pars[key]:
+            return True
+    return False
+
+def dipole_changed(calc, x):
+
+    pars = calc.get_dipole() #pars stored in calculator
+
+    # pars = False if no dipole variables exist
+    if (pars is False and x is False):
+        return False #no change
+    elif (pars is False and x is not False):
+        return True
+
+    # both x and pars is a dictionary
+    if (type(pars) == type(dict) and
+        type(pars) == type(x)):
+        for key in x:
+            if key == 'position':    # dipole layer position is never writen to the nc file
+                print 'need to do something special'
+                continue
+            if x[key] != pars[key]:
+                return True
+
+    #nothing seems to have changed.
+    return False
+
+def extpot_changed(calc, x):
+    extpot = calc.get_extpot()
+    if (x == extpot).all():
+        return False
+    return True
+
+def fftgrid_changed(calc, x):
+    validkeys = ['soft', 'hard']
+
+    myx = calc.get_fftgrid()
+    if (myx['soft'] == x['soft'] and myx['hard'] == x['hard']):
+        return False
+    else:
+        return True
+
+
+def nbands_changed(calc, x):
+    if calc.get_nbands() == x:
+        return False
+    else:
+        return True
+
+def occupationstatistics_changed(calc, x):
+    if calc.get_occupationstatistics() == x:
+        return False
+    else:
+        return True
+
+def pw_changed(calc, x):
+    if calc.get_pw() == x:
+        return False
+    else:
+        return True
+
+def dw_changed(calc, x):
+    if calc.get_dw() == x:
+        return False
+    else:
+        return True
+
+def ft_changed(calc, x):
+    if calc.get_ft() == x:
+        return False
+    else:
+        return True
+    
+def mdos_changed(calc,x):
+
+    myx = calc.get_mdos()
+
+    log.debug('myx = %s' % str(myx))
+    log.debug('x = %s' % str(x))
+
+    if x is None and myx is None:
+        return False
+    elif ((x is None and myx is not None)
+        or (x is not None and myx is None)):
+        return True
+    else:
+        for key in x:
+            if x[key] != myx[key]:
+                return True
+    return False
+
+def pseudopotentials_changed(calc,x):
+
+    mypsp = calc.get_pseudopotentials()
+
+    if len(mypsp) != len(x):
+        return True
+
+    for key in x:
+        if key not in mypsp:
+            return True
+        if mypsp[key] != x[key]:
+            return True
+
+    for key in mypsp:
+        if key not in x:
+            return True
+        if mypsp[key] != x[key]:
+            return True
+    return False
+
+def status_changed(calc,x):
+    if calc.get_status() != x:
+        return True
+    return False
diff --git a/ase/calculators/jacapo/jacapo.py b/ase/calculators/jacapo/jacapo.py
new file mode 100644
index 0000000..15acb43
--- /dev/null
+++ b/ase/calculators/jacapo/jacapo.py
@@ -0,0 +1,4452 @@
+'''
+python module for ASE2-free and Numeric-free dacapo
+
+U{John Kitchin<mailto:jkitchin at andrew.cmu.edu>} December 25, 2008
+
+This module supports numpy directly.
+
+* ScientificPython2.8 is required
+
+ - this is the first version to use numpy by default.
+
+see https://wiki.fysik.dtu.dk/stuff/nc/ for dacapo netcdf variable
+documentation
+'''
+
+__docformat__ = 'restructuredtext'
+
+import sys
+import exceptions, glob, os, pickle, string
+from Scientific.IO.NetCDF import NetCDFFile as netCDF
+import numpy as np
+import subprocess as sp
+
+import validate
+import changed 
+
+try:
+    from uuid import uuid1
+except ImportError: #probably an old python before 2.5
+    import random, time
+    def uuid1():
+        t = time.asctime()
+        host = os.environ['HOSTNAME']
+        random.seed(host + str(t))
+        s = host + '-' + t + '-'+str(random.random())
+        return s.replace(' ','-')
+    
+import logging
+log = logging.getLogger('Jacapo')
+
+handler = logging.StreamHandler()
+if sys.version_info < (2,5): # no funcName in python 2.4
+    formatstring = ('%(levelname)-10s '
+                    'lineno: %(lineno)-4d %(message)s')
+else:
+    formatstring = ('%(levelname)-10s function: %(funcName)s '
+                    'lineno: %(lineno)-4d %(message)s')
+formatter = logging.Formatter(formatstring)
+handler.setFormatter(formatter)
+log.addHandler(handler)
+
+class DacapoRunning(exceptions.Exception):
+    """Raised when ncfile.status = 'running'"""
+    pass
+
+class DacapoAborted(exceptions.Exception):
+    """Raised when ncfile.status = 'aborted'"""
+    pass
+
+class DacapoInput(exceptions.Exception):
+    ''' raised for bad input variables'''
+    pass
+
+class DacapoAbnormalTermination(exceptions.Exception):
+    """Raised when text file does not end correctly"""
+    pass
+
+class DacapoDryrun(exceptions.Exception):
+    """Raised when text file does not end correctly"""
+    pass
+
+
+
+def read(ncfile):
+    '''return atoms and calculator from ncfile
+
+    >>> atoms, calc = read('co.nc')
+    '''
+    calc = Jacapo(ncfile)
+    atoms = calc.get_atoms() #this returns a copy
+    return (atoms, calc)
+
+class Jacapo:
+    '''
+    Python interface to the Fortran DACAPO code
+    '''
+    
+    __name__ = 'Jacapo'
+    __version__ = '0.4'
+    
+    #dictionary of valid input variables and default settings
+    default_input = {'atoms':None,
+                     'pw':350,
+                     'dw':350,
+                     'xc':'PW91',
+                     'nbands':None,
+                     'ft':0.1,
+                     'kpts':(1,1,1),
+                     'spinpol':False,
+                     'fixmagmom':None,
+                     'symmetry':False,
+                     'calculate_stress':False,
+                     'dipole':{'status':False,
+                               'mixpar':0.2,
+                               'initval':0.0,
+                               'adddipfield':0.0,
+                               'position':None},                         
+                     'status':'new',
+                     'pseudopotentials':None,
+                     'extracharge':None,
+                     'extpot':None,
+                     'fftgrid':None,
+                     'ascii_debug':'Off',
+                     'ncoutput':{'wf':'Yes',
+                                 'cd':'Yes',
+                                 'efp':'Yes',
+                                 'esp':'Yes'},
+                     'ados':None,
+                     'decoupling':None,
+                     'external_dipole':None,
+                     'convergence':{'energy':0.00001,
+                                    'density':0.0001,
+                                    'occupation':0.001,
+                                    'maxsteps':None,
+                                    'maxtime':None},
+                     'charge_mixing':{'method':'Pulay',
+                                      'mixinghistory':10,
+                                      'mixingcoeff':0.1,
+                                      'precondition':'No',
+                                      'updatecharge':'Yes'},
+                     'electronic_minimization':{'method':'eigsolve',
+                                                'diagsperband':2},
+                     'occupationstatistics':'FermiDirac',
+                     'fftgrid':{'soft':None,
+                                'hard':None},
+                     'mdos':None,
+                     'psp':None
+                   }
+    
+    def __init__(self,
+                 nc='out.nc',
+                 outnc=None,
+                 debug=logging.WARN,
+                 stay_alive=False,
+                 **kwargs):
+        '''
+        Initialize the Jacapo calculator
+
+        :Parameters:
+
+          nc : string
+           output netcdf file, or input file if nc already exists
+
+          outnc : string
+           output file. by default equal to nc
+
+          debug : integer
+           logging debug level.
+           
+        Valid kwargs:
+
+          atoms : ASE.Atoms instance
+            atoms is an ase.Atoms object that will be attached
+            to this calculator.
+        
+          pw : integer
+            sets planewave cutoff
+
+          dw : integer
+            sets density cutoff
+
+          kpts : iterable
+            set chadi-cohen, monkhorst-pack kpt grid,
+            e.g. kpts = (2,2,1) or explicit list of kpts
+            
+          spinpol : Boolean
+            sets whether spin-polarization is used or not.
+
+          fixmagmom : float
+            set the magnetic moment of the unit cell. only used
+            in spin polarize calculations
+
+          ft : float
+            set the Fermi temperature used in occupation smearing
+
+          xc : string
+            set the exchange-correlation functional.
+            one of ['PZ','VWN','PW91','PBE','RPBE','revPBE'],
+
+          dipole
+            boolean
+            turn the dipole correction on (True) or off (False)
+
+            or:
+            dictionary of parameters to fine-tune behavior
+            {'status':False,
+            'mixpar':0.2,
+            'initval':0.0,
+            'adddipfield':0.0,
+            'position':None}
+            
+          nbands : integer
+            set the number of bands
+
+          symmetry : Boolean
+            Turn symmetry reduction on (True) or off (False)
+
+          stress : Boolean
+            Turn stress calculation on (True) or off (False)
+
+          debug : level for logging
+            could be something like
+            logging.DEBUG or an integer 0-50. The higher the integer,
+            the less information you see set debug level (0 = off, 10 =
+            extreme)
+        
+        Modification of the nc file only occurs at calculate time if needed
+
+        >>> calc = Jacapo('CO.nc')
+        
+        reads the calculator from CO.nc if it exists or
+        minimally initializes CO.nc with dimensions if it does not exist. 
+
+        >>> calc = Jacapo('CO.nc', pw=300)
+
+        reads the calculator from CO.nc or initializes it if
+        it does not exist and changes the planewave cutoff energy to
+        300eV
+
+         >>> atoms = Jacapo.read_atoms('CO.nc')
+
+        returns the atoms in the netcdffile CO.nc, with the calculator
+        attached to it.
+
+        >>> atoms, calc = read('CO.nc')
+        
+        '''
+        self.debug = debug
+        log.setLevel(debug)
+
+        self.pars = Jacapo.default_input.copy()
+        self.pars_uptodate = {}
+
+        log.debug(self.pars)
+        
+        for key in self.pars:
+            self.pars_uptodate[key] = False
+            
+        self.kwargs = kwargs
+        self.set_psp_database()
+
+        self.set_nc(nc)
+        #assume not ready at init, rely on code later to change this 
+        self.ready = False  
+
+        # need to set a default value for stay_alive
+        self.stay_alive = stay_alive
+
+        # for correct updating, we need to set the correct frame number
+        # before setting atoms or calculator
+        self._set_frame_number()
+        
+        if os.path.exists(nc):
+
+            self.atoms = self.read_only_atoms(nc)
+
+            #if atoms object is passed to
+            #__init__ we assume the user wants the atoms object
+            # updated to the current state in the file.
+            if 'atoms' in kwargs:
+                log.debug('Updating the atoms in kwargs')
+
+                atoms = kwargs['atoms']
+                atoms.set_cell(self.atoms.get_cell())
+                atoms.set_positions(self.atoms.get_positions())
+                atoms.calc = self
+                
+            #update the parameter list from the ncfile
+            self.update_input_parameters()
+    
+            self.ready = True
+                   
+        #change output file if needed
+        if outnc:
+            self.set_nc(outnc)          
+            
+        if len(kwargs) > 0:
+
+            if 'stress' in kwargs:
+                raise DacapoInput, '''\
+                stress keyword is deprecated.
+                you must use calculate_stress instead'''
+            
+            #make sure to set calculator on atoms if it was in kwargs
+            #and do this first, since some parameters need info from atoms
+            if 'atoms' in kwargs:
+                #we need to set_atoms here so the atoms are written to
+                #the ncfile
+                self.set_atoms(kwargs['atoms'])
+                kwargs['atoms'].calc = self
+                del kwargs['atoms'] #so we don't call it in the next
+                                    #line. we don't want to do that
+                                    #because it will update the _frame
+                                    #counter, and that should not be
+                                    #done here.
+                                
+            self.set(**kwargs) #if nothing changes, nothing will be done
+                    
+    def set(self, **kwargs):
+        '''set a parameter
+
+        parameter is stored in dictionary that is processed later if a
+        calculation is need.
+        '''
+
+        if 'DACAPO_NOSET' in os.environ:
+            #it is probably a bug that this is detected so we raise an exception
+            raise Exception, 'DACAPO_NOSET detected, nothing is being set'
+            
+        
+        for key in kwargs:
+            if key not in self.default_input:
+                raise DacapoInput, '%s is not valid input' % key
+
+            if kwargs[key] is None:
+                continue
+            
+            #now check for valid input
+            validf = 'validate.valid_%s' % key
+            valid = eval('%s(kwargs[key])' % validf)
+            if not valid:
+                s = 'Warning invalid input detected for key "%s" %s'
+                log.warn(s % (key,
+                              kwargs[key]))
+                raise DacapoInput, s % (key, kwargs[key])
+                                       
+            #now see if key has changed
+            if key in self.pars:
+                changef = 'changed.%s_changed' % key
+                if os.path.exists(self.get_nc()):
+                    notchanged = not eval('%s(self,kwargs[key])' % changef)
+                else:
+                    notchanged = False
+                log.debug('%s notchanged = %s' % (key, notchanged))
+
+                if notchanged:
+                    continue
+
+            log.debug('setting: %s. self.ready = False ' % key)
+
+            # psp's are not stored in self.pars, everything else is
+            if key == 'psp':
+                self.psp[kwargs[key]['sym']] = kwargs[key]['psp']
+            else:
+                self.pars[key] = kwargs[key]
+            self.pars_uptodate[key] = False
+            self.ready = False
+            log.debug('exiting set function')
+
+    def write_input(self):
+        '''write out input parameters as needed
+
+        you must define a self._set_keyword function that does all the
+        actual writing.
+        '''
+        
+        log.debug('Writing input variables out')
+        log.debug(self.pars)
+        
+        if 'DACAPO_READONLY' in os.environ:
+            raise Exception, 'DACAPO_READONLY set and you tried to write!'
+        
+        if self.ready:
+            log.debug('self.ready = %s' % self.ready)
+            log.debug('detected everything is ready, not writing input out')
+            return
+
+        # Only write out changed parameters. this function does not do
+        # the writing, that is done for each variable in private
+        # functions.
+        for key in self.pars:
+            if self.pars_uptodate[key] is False:
+                setf = 'set_%s' % key
+
+                if self.pars[key] is None:
+                    continue
+                
+                log.debug('trying to call: %s' % setf)
+                log.debug('self.%s(self.pars[key])' % setf)
+                log.debug('key = %s' % str(self.pars[key]))
+
+                if isinstance(self.pars[key], dict):
+                    eval('self.%s(**self.pars[key])' % setf)
+                else:
+                    eval('self.%s(self.pars[key])' % setf)
+                
+                self.pars_uptodate[key] = True #update the changed flag
+
+                log.debug('wrote %s: %s' % (key, str(self.pars[key])))
+
+        #set Jacapo version
+        ncf = netCDF(self.get_nc(), 'a')
+        ncf.Jacapo_version = Jacapo.__version__
+        ncf.sync()
+        ncf.close()
+
+    def update_input_parameters(self):
+        '''read in all the input parameters from the netcdfile'''
+
+        log.debug('Updating parameters')
+        
+        for key in self.default_input:
+            getf = 'self.get_%s()' % key
+            log.debug('getting key: %s' % key)
+            self.pars[key] = eval(getf)
+            self.pars_uptodate[key] = True
+        return self.pars
+
+    def write(self, new=False):
+        '''write out everything to the ncfile : self.get_nc()
+
+        new determines whether to delete any existing ncfile, and rewrite it.
+        '''
+        nc = self.get_nc()
+
+        if new:
+            if os.path.exists(nc):
+                os.unlink(nc)
+            self.ready = False
+            for key in self.pars_uptodate:
+                self.pars_uptodate[key] = False
+
+        if not os.path.exists(nc):
+            self.initnc()
+
+        self.write_input()
+        self.write_nc()
+        
+    
+    def initnc(self, ncfile=None):
+        '''create an ncfile with minimal dimensions in it
+
+        this makes sure the dimensions needed for other set functions
+        exist when needed.'''
+        
+        if ncfile is None:
+            ncfile = self.get_nc()
+        else:
+            self.set_nc(ncfile)
+            
+        log.debug('initializing %s' % ncfile)
+                
+        base = os.path.split(ncfile)[0]
+        if base is not '' and not os.path.isdir(base):
+            os.makedirs(base)
+        
+        ncf = netCDF(ncfile, 'w')
+        #first, we define some dimensions we always need
+        #unlimited
+        ncf.createDimension('number_ionic_steps', None)
+        ncf.createDimension('dim1', 1)
+        ncf.createDimension('dim2', 2)
+        ncf.createDimension('dim3', 3)
+        ncf.createDimension('dim4', 4)
+        ncf.createDimension('dim5', 5)
+        ncf.createDimension('dim6', 6)
+        ncf.createDimension('dim7', 7)
+        ncf.createDimension('dim20', 20) #for longer strings
+        ncf.status  = 'new'
+        ncf.history = 'Dacapo'
+        ncf.uuid = str(uuid1())
+        ncf.Jacapo_version = Jacapo.__version__
+        ncf.close()
+        
+        self.ready = False
+        self._frame = 0
+        
+    def __del__(self):
+        '''If calculator is deleted try to stop dacapo program
+        '''
+        
+        if hasattr(self, '_dacapo'):
+            if self._dacapo.poll()==None:
+                self.execute_external_dynamics(stopprogram=True)
+        #and clean up after Dacapo
+        if os.path.exists('stop'):
+            os.remove('stop')
+        #remove slave files
+        txt = self.get_txt()
+        if txt is not None:
+            slv = txt + '.slave*'
+            for slvf in glob.glob(slv):
+                os.remove(slvf)
+        
+    def __str__(self):
+        '''
+        pretty-print the calculator and atoms.
+
+        we read everything directly from the ncfile to prevent
+        triggering any calculations
+        '''
+        s = []
+        if self.nc is None:
+            return 'No netcdf file attached to this calculator'
+        if not os.path.exists(self.nc):
+            return 'ncfile (%s) does not exist yet' % self.nc
+        
+        nc = netCDF(self.nc, 'r')
+        s.append('  ---------------------------------')
+        s.append('  Dacapo calculation from %s' % self.nc)
+        if hasattr(nc, 'uuid'):
+            s.append('  uuid = %s' % nc.uuid)
+        if hasattr(nc, 'status'):
+            s.append('  status = %s' % nc.status)
+        if hasattr(nc, 'version'):
+            s.append('  version = %s' % nc.version)
+        if hasattr(nc, 'Jacapo_version'):
+            s.append('  Jacapo version = %s' % nc.Jacapo_version[0])
+        
+        energy = nc.variables.get('TotalEnergy', None)
+        
+        if energy and energy[:][-1] < 1E36: # missing values get
+                                              # returned at 9.3E36
+            s.append('  Energy = %1.6f eV' % energy[:][-1])
+        else:
+            s.append('  Energy = None')
+            
+        s.append('')
+        
+        atoms = self.get_atoms()
+        
+        if atoms is None:
+            s.append('  no atoms defined')
+        else:
+            uc = atoms.get_cell()
+            #a, b, c = uc
+            s.append("  Unit Cell vectors (angstroms)")
+            s.append("         x        y       z   length")
+
+            for i, v in enumerate(uc):
+                L = (np.sum(v**2))**0.5 #vector length
+                s.append("  a%i [% 1.4f % 1.4f % 1.4f] %1.2f" % (i,
+                                                                 v[0],
+                                                                 v[1],
+                                                                 v[2],
+                                                                 L))
+                                                                 
+            stress = nc.variables.get('TotalStress', None)
+            if stress is not None:
+                stress = np.take(stress[:].ravel(), [0, 4, 8, 5, 2, 1])
+                s.append('  Stress: xx,   yy,    zz,    yz,    xz,    xy')
+                s1 = '       % 1.3f % 1.3f % 1.3f % 1.3f % 1.3f % 1.3f'
+                s.append(s1 % tuple(stress))
+            else:
+                s.append('  No stress calculated.')
+            s.append('  Volume = %1.2f A^3' % atoms.get_volume())
+            s.append('')
+
+            z = "  Atom,  sym, position (in x,y,z),     tag, rmsForce and psp"
+            s.append(z)
+
+            #this is just the ncvariable
+            forces = nc.variables.get('DynamicAtomForces', None)
+           
+            for i, atom in enumerate(atoms):
+                sym = atom.symbol
+                pos = atom.position
+                tag = atom.tag
+                if forces is not None and (forces[:][-1][i] < 1E36).all():
+                    f = forces[:][-1][i]
+                    # Lars Grabow: this seems to work right for some
+                    # reason, but I would expect this to be the right
+                    # index order f=forces[-1][i][:]
+                    # frame,atom,direction
+                    rmsforce = (np.sum(f**2))**0.5
+                else:
+                    rmsforce = None
+                    
+                st = "  %2i   %3.12s  " % (i, sym)
+                st += "[% 7.3f%7.3f% 7.3f] " % tuple(pos)
+                st += " %2s  " % tag
+                if rmsforce is not None:
+                    st += " %4.3f " % rmsforce
+                else:
+                    st += ' None '
+                st += " %s"  % (self.get_psp(sym))        
+                s.append(st)
+                
+        s.append('')
+        s.append('  Details:')
+        xc = self.get_xc()
+        if xc is not None:
+            s.append('  XCfunctional        = %s' % self.get_xc())
+        else:
+            s.append('  XCfunctional        = Not defined')
+
+        pw = self.get_pw()
+        if pw is None:
+            pw = 'default (350eV)'
+            
+        s.append('  Planewavecutoff     = %s eV' % pw)
+        dw = self.get_dw()
+        if dw:
+            s.append('  Densitywavecutoff   = %i eV' % int(self.get_dw()))
+        else:
+            s.append('  Densitywavecutoff   = None')
+        ft = self.get_ft()
+        if ft is not None:
+            s.append('  FermiTemperature    = %f kT' % ft)
+        else:
+            s.append('  FermiTemperature    = not defined')
+        try:
+            nelectrons = self.get_valence()
+        except:
+            nelectrons = None
+        if nelectrons is not None:
+            s.append('  Number of electrons = %1.1f'  % nelectrons)
+        else:
+            s.append('  Number of electrons = N/A')
+        s.append('  Number of bands     = %s'  % self.get_nbands())
+        s.append('  Kpoint grid         = %s' % str(self.get_kpts_type()))
+        s.append('  Spin-polarized      = %s' % self.get_spin_polarized())
+#        if self.get_spin_polarized():
+#           s.append('    Unit cell magnetic moment = %1.2f bohr-magnetons' % \
+#                  self.get_magnetic_moment())
+        s.append('  Dipole correction   = %s' % self.get_dipole())
+        s.append('  Symmetry            = %s' % self.get_symmetry())
+        s.append('  Constraints         = %s' % str(atoms._get_constraints()))
+        s.append('  ---------------------------------')
+        nc.close()
+        return string.join(s, '\n')            
+
+    #todo figure out other xc psp databases
+    def set_psp_database(self, xc=None):
+        '''
+        get the xc-dependent psp database
+
+        :Parameters:
+
+         xc : string
+           one of 'PW91', 'PBE', 'revPBE', 'RPBE', 'PZ'
+
+        
+        not all the databases are complete, and that means
+        some psp do not exist.
+
+        note: this function is not supported fully. only pw91 is
+        imported now. Changing the xc at this point results in loading
+        a nearly empty database, and I have not thought about how to
+        resolve that
+        '''
+        
+        if xc == 'PW91' or xc is None:
+            from pw91_psp import defaultpseudopotentials
+        else:
+            log.warn('PW91 pseudopotentials are being used!')
+            #todo build other xc psp databases
+            from pw91_psp import defaultpseudopotentials
+
+        self.psp = defaultpseudopotentials
+
+    def _set_frame_number(self, frame=None):
+        '''set framenumber in the netcdf file
+
+        this is equal to the number of ionic steps dimension'''
+        
+        if frame is None:
+            if os.path.exists(self.nc):
+                nc = netCDF(self.nc, 'r')
+                # nc.dimensions['number_ionic_steps'] is None
+                if 'TotalEnergy' in nc.variables:
+                    number_ionic_steps = nc.variables['TotalEnergy'].shape[0]
+                else:
+                    number_ionic_steps = nc.variables['DynamicAtomPositions'].shape[0]
+                    
+                frame = number_ionic_steps - 1
+                nc.close()
+            else:
+                if hasattr(self,'atoms'):
+                    frame = 1
+                else:
+                    #when atoms are set, the frame will be incremented
+                    frame = 0
+    
+##            if 'TotalEnergy' in nc.variables:
+##                frame = nc.variables['TotalEnergy'].shape[0]
+##                # make sure the last energy is reasonable. Sometime
+##                # the field is empty if the calculation ran out of
+##                # walltime for example. Empty values get returned as
+##                # 9.6E36.  Dacapos energies should always be negative,
+##                # so if the energy is > 1E36, there is definitely
+##                # something wrong and a restart is required.
+##                if nc.variables.get('TotalEnergy', None)[-1] > 1E36:
+##                    log.warn("Total energy > 1E36. NC file is incomplete. \
+##                    calc.restart may be required")
+##                    #self.restart()
+            
+        log.info("Current frame number is: %i" % (frame - 1))
+        self._frame = frame - 1  #netCDF starts counting with 1
+
+    def _increment_frame(self):
+        'increment the framenumber'
+        
+        log.debug('incrementing frame')
+        self._frame += 1
+
+    def set_pw(self, pw):
+        '''set the planewave cutoff.
+
+        :Parameters:
+
+         pw : integer
+           the planewave cutoff in eV
+           
+        this function checks to make sure the density wave cutoff is
+        greater than or equal to the planewave cutoff.'''
+        
+        nc = netCDF(self.nc, 'a')
+        if 'PlaneWaveCutoff' in nc.variables:
+            vpw = nc.variables['PlaneWaveCutoff']
+            vpw.assignValue(pw)
+        else:
+            vpw = nc.createVariable('PlaneWaveCutoff', 'd', ('dim1',))
+            vpw.assignValue(pw)
+
+        if 'Density_WaveCutoff' in nc.variables:
+            vdw = nc.variables['Density_WaveCutoff']
+            dw = vdw.getValue()
+            if pw > dw:
+                vdw.assignValue(pw) #make them equal
+        else:
+            vdw = nc.createVariable('Density_WaveCutoff', 'd', ('dim1',))
+            vdw.assignValue(pw) 
+        nc.close()
+        self.restart() #nc dimension change for number_plane_Wave dimension
+        self.set_status('new')
+        self.ready = False
+
+    def set_dw(self, dw):
+        '''set the density wave cutoff energy.
+
+        :Parameters:
+
+          dw : integer
+            the density wave cutoff
+
+        The function checks to make sure it is not less than the
+        planewave cutoff.
+
+        Density_WaveCutoff describes the kinetic energy neccesary to
+        represent a wavefunction associated with the total density,
+        i.e. G-vectors for which $\vert G\vert^2$ $<$
+        4*Density_WaveCutoff will be used to describe the total
+        density (including augmentation charge and partial core
+        density). If Density_WaveCutoff is equal to PlaneWaveCutoff
+        this implies that the total density is as soft as the
+        wavefunctions described by the kinetic energy cutoff
+        PlaneWaveCutoff. If a value of Density_WaveCutoff is specified
+        (must be larger than or equal to PlaneWaveCutoff) the program
+        will run using two grids, one for representing the
+        wavefunction density (softgrid_dim) and one representing the
+        total density (hardgrid_dim). If the density can be
+        reprensented on the same grid as the wavefunction density
+        Density_WaveCutoff can be chosen equal to PlaneWaveCutoff
+        (default).
+        '''
+
+        pw = self.get_pw()
+        if pw > dw:
+            log.warn('Planewave cutoff %i is greater \
+than density cutoff %i' % (pw, dw))
+        
+        ncf = netCDF(self.nc, 'a')
+        if 'Density_WaveCutoff' in ncf.variables:
+            vdw = ncf.variables['Density_WaveCutoff']
+            vdw.assignValue(dw)
+        else:
+            vdw = ncf.createVariable('Density_WaveCutoff', 'i', ('dim1',))
+            vdw.assignValue(dw)
+        ncf.close()
+        self.restart() #nc dimension change
+        self.set_status('new')
+        self.ready = False
+
+    def set_xc(self, xc):
+        '''Set the self-consistent exchange-correlation functional
+
+        :Parameters:
+
+         xc : string
+           Must be one of 'PZ', 'VWN', 'PW91', 'PBE', 'revPBE', 'RPBE'
+
+        Selects which density functional to use for
+        exchange-correlation when performing electronic minimization
+        (the electronic energy is minimized with respect to this
+        selected functional) Notice that the electronic energy is also
+        evaluated non-selfconsistently by DACAPO for other
+        exchange-correlation functionals Recognized options :
+
+        * "PZ" (Perdew Zunger LDA-parametrization)
+        * "VWN" (Vosko Wilk Nusair LDA-parametrization)
+        * "PW91" (Perdew Wang 91 GGA-parametrization)
+        * "PBE" (Perdew Burke Ernzerhof GGA-parametrization)
+        * "revPBE" (revised PBE/1 GGA-parametrization)
+        * "RPBE" (revised PBE/2 GGA-parametrization)
+
+        option "PZ" is not allowed for spin polarized
+        calculation; use "VWN" instead.
+        '''
+        nc = netCDF(self.nc, 'a')
+        v = 'ExcFunctional'
+        if v in nc.variables:
+            nc.variables[v][:] = np.array('%7s' % xc, 'c') 
+        else:
+            vxc = nc.createVariable('ExcFunctional', 'c', ('dim7',))
+            vxc[:] = np.array('%7s' % xc, 'c')
+        nc.close()
+        self.set_status('new')
+        self.ready = False    
+
+    def set_nbands(self, nbands=None):
+        '''Set the number of bands. a few unoccupied bands are
+        recommended.
+
+        :Parameters:
+
+          nbands : integer
+            the number of bands.
+            
+        if nbands = None the function returns with nothing done. At
+        calculate time, if there are still no bands, they will be set
+        by:
+
+        the number of bands is calculated as
+        $nbands=nvalence*0.65 + 4$
+        '''
+        if nbands is None:
+            return
+
+        self.delete_ncattdimvar(self.nc,
+                                ncdims=['number_of_bands'],
+                                ncvars=[])
+                       
+        nc = netCDF(self.nc, 'a')
+        v = 'ElectronicBands'
+        if v in nc.variables:
+            vnb = nc.variables[v]
+        else:
+            vnb = nc.createVariable('ElectronicBands', 'c', ('dim1',))
+            
+        vnb.NumberOfBands = nbands
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_kpts(self, kpts):
+        '''
+        set the kpt grid.
+
+        Parameters:
+
+        kpts: (n1,n2,n3) or [k1,k2,k3,...] or one of these
+        chadi-cohen sets:
+         
+        * cc6_1x1
+        * cc12_2x3
+        * cc18_sq3xsq3
+        * cc18_1x1
+        * cc54_sq3xsq3
+        * cc54_1x1
+        * cc162_sq3xsq3
+        * cc162_1x1
+        
+        (n1,n2,n3) creates an n1 x n2 x n3 monkhorst-pack grid,
+        [k1,k2,k3,...] creates a kpt-grid based on the kpoints
+        defined in k1,k2,k3,...
+        
+        There is also a possibility to have Dacapo (fortran) create
+        the Kpoints in chadi-cohen or monkhorst-pack form. To do this
+        you need to set the KpointSetup.gridtype attribute, and
+        KpointSetup.
+
+        KpointSetup = [3,0,0]
+        KpointSetup.gridtype = 'ChadiCohen'
+
+        KpointSetup(1) 	Chadi-Cohen k-point set
+        1 	6 k-points 1x1
+        2 	18-kpoints sqrt(3)*sqrt(3)
+        3 	18-kpoints 1x1
+        4 	54-kpoints sqrt(3)*sqrt(3)
+        5 	54-kpoints 1x1
+        6 	162-kpoints 1x1
+        7 	12-kpoints 2x3
+        8 	162-kpoints 3xsqrt 3
+
+        or
+        KpointSetup = [4,4,4]
+        KpointSetup.gridtype = 'MonkhorstPack'
+        we do not use this functionality.
+        '''
+
+        #chadi-cohen
+        if isinstance(kpts, str):
+            exec('from ase.dft.kpoints import %s' % kpts)
+            listofkpts = eval(kpts)
+            gridtype = kpts #stored in ncfile
+            #uc = self.get_atoms().get_cell()
+            #listofkpts = np.dot(ccgrid,np.linalg.inv(uc.T))
+
+        #monkhorst-pack grid
+        if np.array(kpts).shape == (3,):
+            from ase.dft.kpoints import monkhorst_pack
+            N1, N2, N3 = kpts
+            listofkpts = monkhorst_pack((N1, N2, N3))
+            gridtype = 'Monkhorst-Pack %s' % str(tuple(kpts))
+
+        #user-defined list is provided
+        if len(np.array(kpts).shape) == 2:
+            listofkpts = kpts
+            gridtype = 'user_defined_%i_kpts' % len(kpts)  #stored in ncfile
+                    
+        nbzkpts = len(listofkpts)
+
+        #we need to get dimensions stored temporarily so
+        #we can delete all dimensions and variables associated with
+        #kpoints before we save them back out.
+        nc2 = netCDF(self.nc, 'r')
+        ncdims = nc2.dimensions
+        nc2.close()
+
+        if 'number_BZ_kpoints' in ncdims:
+            self.delete_ncattdimvar(self.nc,
+                                    ncdims=['number_plane_waves',
+                                            'number_BZ_kpoints',
+                                            'number_IBZ_kpoints'])
+
+        # now define dim and var
+        nc = netCDF(self.nc, 'a')
+        nc.createDimension('number_BZ_kpoints', nbzkpts)
+        bv = nc.createVariable('BZKpoints', 'd', ('number_BZ_kpoints',
+                                                  'dim3'))
+
+        bv[:] = listofkpts
+        bv.gridtype = gridtype
+        nc.sync()
+        nc.close()
+
+        log.debug('kpts = %s' % str(self.get_kpts()))
+        
+        self.set_status('new')
+        self.ready = False
+
+    def atoms_are_equal(self, atoms):
+        '''
+        comparison of atoms to self.atoms using tolerances to account
+        for float/double differences and float math.
+        '''
+        
+        TOL = 1.0e-6 #angstroms
+
+        a = self.atoms.arrays
+        b = atoms.arrays
+
+        #match number of atoms in cell
+        lenmatch = len(atoms) == len(self.atoms)
+        if lenmatch is not True:
+            return False #the next two comparisons fail in this case.
+        
+        #match positions in cell
+        posmatch = (abs(a['positions'] - b['positions']) <= TOL).all()
+        #match cell
+        cellmatch = (abs(self.atoms.get_cell()
+                         - atoms.get_cell()) <= TOL).all()
+        
+        if lenmatch and posmatch and cellmatch:
+            return True
+        else:
+            return False
+                
+    def set_atoms(self, atoms):
+        '''attach an atoms to the calculator and update the ncfile
+
+        :Parameters:
+
+          atoms
+            ASE.Atoms instance
+          
+        '''
+
+        log.debug('setting atoms to: %s' % str(atoms))
+        
+        if hasattr(self, 'atoms') and self.atoms is not None:
+            #return if the atoms are the same. no change needs to be made
+            if self.atoms_are_equal(atoms):
+                log.debug('No change to atoms in set_atoms, returning')
+                return
+            
+            # some atoms already exist. Test if new atoms are
+            # different from old atoms.
+            # this is redundant
+            if atoms != self.atoms:
+                # the new atoms are different from the old ones. Start
+                # a new frame.
+                log.debug('atoms != self.atoms, incrementing')
+                self._increment_frame()
+                
+        self.atoms = atoms.copy()
+        self.ready = False
+        log.debug('self.atoms = %s' % str(self.atoms))
+
+    def set_ft(self, ft):
+        '''set the Fermi temperature for occupation smearing
+
+        :Parameters:
+
+          ft : float
+            Fermi temperature in kT (eV)
+
+        Electronic temperature, corresponding to gaussian occupation
+        statistics. Device used to stabilize the convergence towards
+        the electronic ground state. Higher values stabilizes the
+        convergence. Values in the range 0.1-1.0 eV are recommended,
+        depending on the complexity of the Fermi surface (low values
+        for d-metals and narrow gap semiconducters, higher for free
+        electron-like metals).
+        '''
+        
+        nc = netCDF(self.nc, 'a')
+        v = 'ElectronicBands'
+        if v in nc.variables:
+            vnb = nc.variables[v]
+        else:
+            vnb = nc.createVariable('ElectronicBands', 'c', ('dim1',))
+
+        vnb.OccupationStatistics_FermiTemperature = ft
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_status(self, status):
+        '''set the status flag in the netcdf file
+
+        :Parameters:
+
+          status : string
+            status flag, e.g. 'new', 'finished'
+        '''
+        
+        nc = netCDF(self.nc, 'a')
+        nc.status = status
+        nc.sync()
+        nc.close()
+        log.debug('set status to %s' % status)
+
+    def get_spinpol(self):
+        'Returns the spin polarization setting, either True or False'
+        
+        nc = netCDF(self.nc, 'r')
+        v = 'ElectronicBands'
+        if v in nc.variables:
+            vnb = nc.variables[v]
+            if hasattr(vnb, 'SpinPolarization'):
+                spinpol = vnb.SpinPolarization
+            else:
+                spinpol = 1
+        else:
+            spinpol = 1
+
+        nc.close()
+        if spinpol == 1:
+            return False
+        else:
+            return True
+
+    def set_spinpol(self, spinpol=False):
+        '''set Spin polarization.
+
+        :Parameters:
+
+         spinpol : Boolean
+           set_spinpol(True)  spin-polarized.
+           set_spinpol(False) no spin polarization, default
+
+        Specify whether to perform a spin polarized or unpolarized
+        calculation.
+        '''
+        
+        nc = netCDF(self.nc, 'a')
+        v = 'ElectronicBands'
+        if v in nc.variables:
+            vnb = nc.variables[v]
+        else:
+            vnb = nc.createVariable('ElectronicBands', 'c', ('dim1',))
+
+        if spinpol is True:
+            vnb.SpinPolarization = 2
+        else:
+            vnb.SpinPolarization = 1
+
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_fixmagmom(self, fixmagmom=None):
+        '''set a fixed magnetic moment for a spin polarized calculation
+
+        :Parameters:
+
+          fixmagmom : float
+            the magnetic moment of the cell in Bohr magnetons
+        '''
+        
+        if fixmagmom is None:
+            return
+        
+        nc = netCDF(self.nc,'a')
+        v = 'ElectronicBands'
+        if v in nc.variables:
+            vnb = nc.variables[v]
+        else:
+            vnb = nc.createVariable('ElectronicBands', 'c', ('dim1',))
+
+        vnb.SpinPolarization = 2 #You must want spin-polarized
+        vnb.FixedMagneticMoment = fixmagmom
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def get_fixmagmom(self):
+        'returns the value of FixedMagneticMoment'
+        
+        nc = netCDF(self.nc,'r')
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            if hasattr(v,'FixedMagneticMoment'):
+                fixmagmom = v.FixedMagneticMoment
+            else:
+                fixmagmom = None
+        else:
+            fixmagmom = None
+        nc.close()
+        return fixmagmom
+            
+    def set_calculate_stress(self, stress=True):
+        '''Turn on stress calculation
+
+        :Parameters:
+
+          stress : boolean
+            set_calculate_stress(True) calculates stress
+            set_calculate_stress(False) do not calculate stress
+        '''
+        
+        nc = netCDF(self.get_nc(),'a')
+        vs = 'NetCDFOutputControl'
+        if vs in nc.variables:
+            v = nc.variables[vs]
+        else:
+            v = nc.createVariable('NetCDFOutputControl', 'c', ('dim1',))
+
+        if stress is True:
+            v.PrintTotalStress = 'Yes'
+        else:
+            v.PrintTotalStress = 'No'
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_nc(self, nc='out.nc'):
+        '''
+        set filename for the netcdf and text output for this calculation
+
+        :Parameters:
+
+          nc : string
+            filename for netcdf file
+                
+        if the ncfile attached to the calculator is changing, the old
+        file will be copied to the new file if it doesn not exist so
+        that all the calculator details are preserved. Otherwise, the 
+
+        if the ncfile does not exist, it will get initialized.
+
+        the text file will have the same basename as the ncfile, but
+        with a .txt extension.
+        '''
+        
+        #the first time this is called, there may be no self.nc defined
+        if not hasattr(self, 'nc'):
+            self.nc = nc    
+            
+        #check if the name is changing and if so, copy the old ncfile
+        #to the new one.  This is necessary to ensure all the
+        #calculator details are copied over. if the file already
+        #exists we use the contents of the existing file
+        if nc != self.nc and not os.path.exists(nc):
+            log.debug('copying %s to %s' % (self.nc, nc))
+            #import shutil
+            #shutil.copy(self.nc,nc)
+            base = os.path.split(nc)[0]
+            if not os.path.isdir(base) and base is not '':
+                os.makedirs(base)
+            status = os.system("cp '%s' '%s'" % (self.nc, nc))
+            if status != 0:
+                raise Exception, 'Copying ncfile failed.'
+            self.nc = nc
+        
+        elif os.path.exists(nc):
+            self._set_frame_number()
+            self.set_psp_database()
+            self.atoms = self.read_only_atoms(nc)
+            self.nc = nc
+            self.update_input_parameters()
+                
+        #I always want the text file set based on the ncfile
+        #and I never want to set this myself.
+        base = os.path.splitext(self.nc)[0]
+        self.txt = "%s.txt" % base
+
+    def set_pseudopotentials(self, pspdict):
+        '''Set all the pseudopotentials from a dictionary.
+
+        The dictionary should have this form::
+
+            {symbol1: path1,
+             symbol2: path2}
+        '''
+        for key in pspdict:
+            self.set_psp(sym=key,
+                         psp=pspdict[key])
+            
+    def set_psp(self,
+                sym=None,
+                z=None,
+                psp=None):
+        '''
+        set the pseudopotential file for a species or an atomic number.
+
+        :Parameters:
+
+         sym : string
+           chemical symbol of the species
+
+          z : integer
+            the atomic number of the species
+
+          psp : string
+            filename of the pseudopotential
+
+        
+        you can only set sym or z.
+
+        examples::
+        
+          set_psp('N',psp='pspfile')
+          set_psp(z=6,psp='pspfile')
+        '''
+        log.debug(str([sym, z, psp]))
+        if (sym, z, psp) == (None, None, None):
+            return
+        
+        if (sym is None and z is not None):
+            from ase.data import chemical_symbols
+            sym = chemical_symbols[z]
+        elif (sym is not None and z is None):
+            pass
+        else:
+            raise Exception, 'You can only specify Z or sym!'
+
+        if not hasattr(self, 'psp'):
+            self.set_psp_database()
+
+        #only make change if needed
+        if sym not in self.psp:
+            self.psp[sym] = psp
+            self.ready = False
+            self.set_status('new')
+        elif self.psp[sym] != psp:
+            self.psp[sym] = psp
+            self.ready = False
+            self.set_status('new')
+
+        if not self.ready:
+            #now we update the netcdf file
+            ncf = netCDF(self.nc, 'a')
+            vn = 'AtomProperty_%s' % sym
+            if vn not in ncf.variables:
+                if 'dim20' not in ncf.dimensions:
+                    ncf.createDimension('dim20', 20)
+                p = ncf.createVariable(vn, 'c', ('dim20',))
+            else:
+                p = ncf.variables[vn]
+
+            ppath = self.get_psp(sym=sym)
+            p.PspotFile = ppath
+            ncf.close()
+
+    def get_pseudopotentials(self):
+        'get pseudopotentials set for atoms attached to calculator'
+        
+        if self.atoms is None:
+            return None
+        
+        psp = {}
+        for atom in self.atoms:
+            psp[atom.symbol] = self.psp[atom.symbol]
+        return {'pspdict':psp}
+
+    def get_symmetry(self):
+        '''return the type of symmetry used'''
+        
+        nc = netCDF(self.nc, 'r')
+        if 'UseSymmetry' in nc.variables:
+            sym = string.join(nc.variables['UseSymmetry'][:],'').strip()
+        else:
+            sym = None      
+        nc.close()
+        if sym in ['Off', None]:
+            return False
+        elif sym == 'Maximum':
+            return True
+        else:
+            raise Exception, 'Type of symmetry not recognized: %s' % sym
+       
+    def set_symmetry(self, val=False):
+        '''set how symmetry is used to reduce k-points
+
+        :Parameters:
+
+         val : Boolean
+           set_sym(True) Maximum symmetry is used
+           set_sym(False) No symmetry is used
+
+        This variable controls the if and how DACAPO should attempt
+        using symmetry in the calculation. Imposing symmetry generally
+        speeds up the calculation and reduces numerical noise to some
+        extent. Symmetry should always be applied to the maximum
+        extent, when ions are not moved. When relaxing ions, however,
+        the symmetry of the equilibrium state may be lower than the
+        initial state. Such an equilibrium state with lower symmetry
+        is missed, if symmetry is imposed. Molecular dynamics-like
+        algorithms for ionic propagation will generally not break the
+        symmetry of the initial state, but some algorithms, like the
+        BFGS may break the symmetry of the initial state. Recognized
+        options:
+
+        "Off": No symmetry will be imposed, apart from time inversion
+        symmetry in recipical space. This is utilized to reduce the
+        k-point sampling set for Brillouin zone integration and has no
+        influence on the ionic forces/motion.
+
+        "Maximum": DACAPO will look for symmetry in the supplied
+        atomic structure and extract the highest possible symmetry
+        group. During the calculation, DACAPO will impose the found
+        spatial symmetry on ionic forces and electronic structure,
+        i.e. the symmetry will be conserved during the calculation.
+        '''
+        
+        if val:
+            symval = 'Maximum'
+        else:
+            symval = 'Off'
+        
+        ncf = netCDF(self.get_nc(), 'a')
+        if 'UseSymmetry' not in ncf.variables:
+            sym = ncf.createVariable('UseSymmetry', 'c', ('dim7',))
+        else:
+            sym = ncf.variables['UseSymmetry']
+            
+        sym[:] = np.array('%7s' % symval, 'c')
+        ncf.sync()
+        ncf.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_extracharge(self, val):
+        '''add extra charge to unit cell
+
+        :Parameters:
+
+          val : float
+            extra electrons to add or subtract from the unit cell
+
+        Fixed extra charge in the unit cell (i.e. deviation from
+        charge neutrality). This assumes a compensating, positive
+        constant backgound charge (jellium) to forge overall charge
+        neutrality.
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        if 'ExtraCharge' in nc.variables:
+            v = nc.variables['ExtraCharge']
+        else:
+            v = nc.createVariable('ExtraCharge', 'd', ('dim1',))
+
+        v.assignValue(val)
+        nc.sync()
+        nc.close()
+
+    def get_extracharge(self):
+        'Return the extra charge set in teh calculator'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'ExtraCharge' in nc.variables:
+            v = nc.variables['ExtraCharge']
+            exchg = v.getValue()
+        else:
+            exchg = None
+        nc.close()
+        return exchg
+
+    def get_extpot(self):
+        'return the external potential set in teh calculator'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'ExternalPotential' in nc.variables:
+            v = nc.variables['ExternalPotential']
+            extpot = v[:]
+        else:
+            extpot = None
+
+        nc.close()
+        return extpot
+    
+    def set_extpot(self, potgrid):
+        '''add external potential of value
+
+        see this link before using this
+        https://listserv.fysik.dtu.dk/pipermail/campos/2003-August/000657.html
+        
+        :Parameters:
+
+          potgrid : np.array with shape (nx,ny,nz)
+            the shape must be the same as the fft soft grid
+            the value of the potential to add
+
+        
+        you have to know both of the fft grid dimensions ahead of time!
+        if you know what you are doing, you can set the fft_grid you want
+        before hand with:
+        calc.set_fftgrid((n1,n2,n3))
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        if 'ExternalPotential' in nc.variables:
+            v = nc.variables['ExternalPotential']
+        else:
+            # I assume here you have the dimensions of potgrid correct
+            # and that the soft and hard grids are the same. 
+            # if softgrid is defined, Dacapo requires hardgrid to be
+            # defined too.
+            s1, s2, s3 = potgrid.shape
+            if 'softgrid_dim1' not in nc.dimensions:
+                nc.createDimension('softgrid_dim1', s1)
+                nc.createDimension('softgrid_dim2', s2)
+                nc.createDimension('softgrid_dim3', s3)
+                nc.createDimension('hardgrid_dim1', s1)
+                nc.createDimension('hardgrid_dim2', s2)
+                nc.createDimension('hardgrid_dim3', s3)
+                
+            v = nc.createVariable('ExternalPotential',
+                                  'd',
+                                  ('softgrid_dim1',
+                                   'softgrid_dim2',
+                                   'softgrid_dim3',))
+        v[:] = potgrid
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+        
+    def set_fftgrid(self, soft=None, hard=None):
+        '''
+        sets the dimensions of the FFT grid to be used
+
+        :Parameters:
+
+          soft : (n1,n2,n3) integers
+            make a n1 x n2 x n3 grid
+
+          hard : (n1,n2,n3) integers
+            make a n1 x n2 x n3 grid
+
+        
+        >>> calc.set_fftgrid(soft=[42,44,46])
+        sets the soft and hard grid dimensions to 42,44,46
+
+        >>> calc.set_fftgrid(soft=[42,44,46],hard=[80,84,88])
+        sets the soft grid dimensions to 42,44,46 and the hard
+        grid dimensions to 80,84,88
+        
+        These are the fast FFt grid numbers listed in fftdimensions.F
+        
+        data list_of_fft /2,  4,  6,  8, 10, 12, 14, 16, 18, 20, &
+        22,24, 28, 30,32, 36, 40, 42, 44, 48, &
+        56,60, 64, 66, 70, 72, 80, 84, 88, 90, &
+        96,108,110,112,120,126,128,132,140,144,154, &
+        160,168,176,180,192,198,200, &
+        216,240,264,270,280,288,324,352,360,378,384,400,432, &
+        450,480,540,576,640/
+        
+        otherwise you will get some errors from mis-dimensioned variables.
+        
+        this is usually automatically set by Dacapo.
+        '''
+        
+        if soft is not None:
+            self.delete_ncattdimvar(self.nc,
+                                    ncdims=['softgrid_dim1',
+                                            'softgrid_dim2',
+                                            'softgrid_dim3'
+                                            ],
+                                    ncvars=[])
+        
+            
+            nc = netCDF(self.get_nc(), 'a')
+            nc.createDimension('softgrid_dim1', soft[0])
+            nc.createDimension('softgrid_dim2', soft[1])
+            nc.createDimension('softgrid_dim3', soft[2])
+            nc.sync()
+            nc.close()
+
+            if hard is None:
+                hard = soft
+
+        if hard is not None:
+            self.delete_ncattdimvar(self.nc,
+                                    ncdims=['hardgrid_dim1',
+                                            'hardgrid_dim2',
+                                            'hardgrid_dim3'
+                                            ],
+                                    ncvars=[])
+            nc = netCDF(self.get_nc(),'a')
+            nc.createDimension('hardgrid_dim1', hard[0])
+            nc.createDimension('hardgrid_dim2', hard[1])
+            nc.createDimension('hardgrid_dim3', hard[2])
+            nc.sync()
+            nc.close()
+
+        self.set_status('new')
+        self.ready = False
+
+    def get_ascii_debug(self):
+        'Return the debug settings in Dacapo'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'PrintDebugInfo' in nc.variables:
+            v = nc.variables['PrintDebugInfo']
+            debug = string.join(v[:], '')
+        else:
+            debug = None
+        nc.close()
+        return debug
+    
+    def set_ascii_debug(self, level):
+        '''set the debug level for Dacapo
+
+        :Parameters:
+        
+          level : string
+            one of 'Off', 'MediumLevel', 'HighLevel'
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        if 'PrintDebugInfo' in nc.variables:
+            v = nc.variables['PrintDebugInfo']
+        else:
+            if 'dim20' not in nc.dimensions:
+                nc.createDimension('dim20', 20)
+            v = nc.createVariable('PrintDebugInfo', 'c', ('dim20',))
+
+        v[:] = np.array('%20s' % level, dtype='c')
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def get_ncoutput(self):
+        'returns the control variables for the ncfile'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'NetCDFOutputControl' in nc.variables:
+            v = nc.variables['NetCDFOutputControl']
+            ncoutput = {}
+            if hasattr(v, 'PrintWaveFunction'):
+                ncoutput['wf'] = v.PrintWaveFunction
+            if hasattr(v, 'PrintChargeDensity'):
+                ncoutput['cd'] = v.PrintChargeDensity
+            if hasattr(v, 'PrintEffPotential'):
+                ncoutput['efp'] = v.PrintEffPotential
+            if hasattr(v, 'PrintElsPotential'):
+                ncoutput['esp'] = v.PrintElsPotential
+        else:
+            ncoutput = None
+        nc.close()
+        return ncoutput
+        
+    def set_ncoutput(self,
+                     wf=None,
+                     cd=None,
+                     efp=None,
+                     esp=None):
+        '''set the output of large variables in the netcdf output file
+
+        :Parameters:
+
+          wf : string
+            controls output of wavefunction. values can
+            be 'Yes' or 'No'
+
+          cd : string
+            controls output of charge density. values can
+            be 'Yes' or 'No'
+
+          efp : string
+            controls output of effective potential. values can
+            be 'Yes' or 'No'
+
+          esp : string
+            controls output of electrostatic potential. values can
+            be 'Yes' or 'No'
+        '''
+        nc = netCDF(self.get_nc(), 'a')
+        if 'NetCDFOutputControl' in nc.variables:
+            v = nc.variables['NetCDFOutputControl']
+        else:
+            v = nc.createVariable('NetCDFOutputControl', 'c', ())
+
+        if wf is not None:
+            v.PrintWaveFunction = wf
+        if cd is not None:
+            v.PrintChargeDensity = cd
+        if efp is not None:
+            v.PrintEffPotential = efp
+        if esp is not None:
+            v.PrintElsPotential = esp
+
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def get_ados(self, **kwargs):
+        '''
+        attempt at maintaining backward compatibility with get_ados
+        returning data
+
+        Now when we call calc.get_ados() it will return settings,
+
+        and calc.get_ados(atoms=[],...) should return data
+
+        '''
+        
+        if len(kwargs) != 0:
+            return self.get_ados_data(**kwargs)
+        
+        nc = netCDF(self.get_nc(),'r')
+        if 'PrintAtomProjectedDOS' in nc.variables:
+            v = nc.variables['PrintAtomProjectedDOS']
+            ados = {}
+            if hasattr(v, 'EnergyWindow'):
+                ados['energywindow'] = v.EnergyWindow
+            if hasattr(v, 'EnergyWidth'):
+                ados['energywidth'] = v.EnergyWidth[0]
+            if hasattr(v, 'NumberEnergyPoints'):
+                ados['npoints'] = v.NumberEnergyPoints[0]
+            if hasattr(v, 'CutoffRadius'):
+                ados['cutoff'] = v.CutoffRadius[0]
+        else:
+            ados = None
+
+        nc.close()
+        return ados
+        
+    def set_ados(self,
+                 energywindow=(-15,5),
+                 energywidth=0.2,
+                 npoints=250,
+                 cutoff=1.0):
+        '''
+        setup calculation of atom-projected density of states
+
+        :Parameters:
+
+          energywindow : (float, float)
+            sets (emin,emax) in eV referenced to the Fermi level
+
+          energywidth : float
+            the gaussian used in smearing
+
+          npoints : integer
+            the number of points to sample the DOS at
+
+          cutoff : float
+            the cutoff radius in angstroms for the integration.
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        if 'PrintAtomProjectedDOS' in nc.variables:
+            v = nc.variables['PrintAtomProjectedDOS']
+        else:
+            v = nc.createVariable('PrintAtomProjectedDOS', 'c', ())
+
+        v.EnergyWindow = energywindow
+        v.EnergyWidth  = energywidth
+        v.NumberEnergyPoints = npoints
+        v.CutoffRadius = cutoff
+
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def get_mdos(self):
+        'return multicentered projected dos parameters'
+        nc = netCDF(self.get_nc(),'r')
+
+        mdos = {}
+        
+        if 'MultiCenterProjectedDOS' in nc.variables:
+            v = nc.variables['MultiCenterProjectedDOS']
+            mdos['energywindow'] = v.EnergyWindow
+            mdos['energywidth'] = v.EnergyWidth
+            mdos['numberenergypoints'] = v.NumberEnergyPoints
+            mdos['cutoffradius'] = v.CutoffRadius
+            mdos['mcenters'] = eval(v.mcenters)
+                
+        nc.close()
+
+        return mdos
+
+    def get_mdos_data(self,
+                      spin=0,
+                      cutoffradius='infinite'):
+        '''returns data from multicentered projection
+
+
+        returns (mdos, rotmat)
+
+        the rotation matrices are retrieved from the text file. I am
+        not sure what you would do with these, but there was a note
+        about them in the old documentation so I put the code to
+        retrieve them here. the syntax for the return value is:
+        rotmat[atom#][label] returns the rotation matrix for the
+        center on the atom# for label. I do not not know what the
+        label refers to.
+        '''
+        
+        if self.calculation_required():
+            self.calculate()
+        
+        nc = netCDF(self.get_nc(),'r')
+        icut = 1 #short
+        if cutoffradius == "infinite":
+            icut = 0
+            
+        #var = nc.variables['MultiCenterProjectedDOS']
+        integrated = nc.variables['MultiCenterProjectedDOS_IntegratedDOS'][:]
+        tz = 'MultiCenterProjectedDOS_EnergyResolvedDOS'
+        energyresolved = nc.variables[tz][:]
+        energygrid = nc.variables['MultiCenterProjectedDOS_EnergyGrid'][:]
+
+        number_of_multicenters  = integrated.shape[0]
+        #number_of_cutoff = integrated.shape[1]
+        #number_of_spin = integrated.shape[2]
+
+        multicenterprojections = []
+        for multicenter in range(number_of_multicenters): 
+            #orbitals = var[multicenter]
+            energyresolveddata = energyresolved[multicenter, icut, spin, :]
+            #integrateddata     = integrated[multicenter, icut, spin]
+            multicenterprojections.append([energygrid, energyresolveddata])
+
+        log.info('Found %d multicenters' % len(multicenterprojections))
+        nc.close()
+
+        #now parse the text file for the rotation matrices
+        rot_mat_lines = []
+        txt = self.get_txt()
+        if os.path.exists(txt):
+            f = open(txt,'r')
+            for line in f:
+                if 'MUL: Rmatrix' in line:
+                    rot_mat_lines.append(line)
+            f.close()
+
+            rotmat = []
+            for line in rot_mat_lines:
+                fields = line.split()
+                novl = int(fields[2])
+                ncen = int(fields[3])
+                row = [float(x) for x in fields[4:]]
+
+                try:
+                    rotmat[novl-1][ncen-1].append(row)
+                except IndexError:
+                    try:
+                        rotmat[novl-1].append([])
+                        rotmat[novl-1][ncen-1].append(row)
+                    except IndexError:
+                        rotmat.append([])
+                        rotmat[novl-1].append([])
+                    rotmat[novl-1][ncen-1].append(row)
+        else:
+            rotmat = None
+                    
+        return (multicenterprojections, rotmat)
+            
+    def set_mdos(self,
+                 mcenters=None,
+                 energywindow=(-15,5),
+                 energywidth=0.2,
+                 numberenergypoints=250,
+                 cutoffradius=1.0):
+        '''Setup multicentered projected DOS.
+
+        mcenters
+           a list of tuples containing (atom#,l,m,weight)
+           (0,0,0,1.0) specifies (atom 0, l=0, m=0, weight=1.0) an s orbital
+           on atom 0
+           
+           (1,1,1,1.0) specifies (atom 1, l=1, m=1, weight=1.0) a p orbital
+           with m = +1 on atom 0
+
+           l=0 s-orbital
+           l=1 p-orbital
+           l=2 d-orbital
+
+           m in range of ( -l ... 0 ... +l )
+
+           The direction cosines for which the spherical harmonics are
+           set up are using the next different atom in the list
+           (cyclic) as direction pointer, so the z-direction is chosen
+           along the direction to this next atom. At the moment the
+           rotation matrices is only given in the text file, you can
+           use grep'MUL: Rmatrix' out_o2.txt to get this information.
+           
+        adapated from old MultiCenterProjectedDOS.py
+        '''
+        if mcenters is None:
+            return
+        
+        nc = netCDF(self.get_nc(), 'a')
+        
+        _listofmcenters_ = mcenters
+        
+        # get number of multi centers
+        ncenters = len(_listofmcenters_)
+        # get max number of orbitals any center 
+        max_orbitals = max(map(len, _listofmcenters_))
+
+        mmatrix = np.zeros([ncenters, max_orbitals, 4], np.float)
+        ncenter = 0
+        for multicenter in _listofmcenters_: 
+            norbital = 0
+            for orbital in multicenter: 
+                mmatrix[ncenter, norbital] = orbital 
+                norbital = norbital + 1 
+
+            # signal that this multicenter contains less than
+            # max_orbital orbitals
+            if len(multicenter) < max_orbitals: 
+                mmatrix[ncenter, len(multicenter):max_orbitals] = (-1.0, 0,
+                                                                   0, 0)
+
+            ncenter = ncenter + 1
+
+        nc.createDimension('max_orbitals', max_orbitals)
+        nc.createDimension('number_of_multicenters', ncenters)
+
+        if 'MultiCenterProjectedDOS' in nc.variables:
+            v = nc.variables['MultiCenterProjectedDOS']
+        else:
+            v = nc.createVariable('MultiCenterProjectedDOS',
+                                  'd',
+                                  ('number_of_multicenters',
+                                   'max_orbitals',
+                                   'dim4'))
+
+        v.EnergyWindow = energywindow
+        v.EnergyWidth = energywidth
+        v.NumberEnergyPoints = numberenergypoints
+        v.CutoffRadius = cutoffradius
+
+        #this is kind of hacky, but it is needed for get_mdos so you
+        #can tell if the input is changed.
+        v.mcenters = str(mcenters)
+
+        v[:] = mmatrix
+
+        nc.sync()
+        nc.close()        
+
+    def set_debug(self, debug):
+        '''
+        set debug level for python logging
+
+        debug should be an integer from 0-100 or one of the logging
+        constants like logging.DEBUG, logging.WARN, etc...
+
+        '''
+        
+        self.debug = debug
+        log.setLevel(debug)
+
+    def get_debug(self):
+        'Return the python logging level'
+        
+        return self.debug
+
+    def get_decoupling(self):
+        'return the electrostatic decoupling parameters'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'Decoupling' in nc.variables:
+            v = nc.variables['Decoupling']
+            decoupling = {}
+            if hasattr(v,'NumberOfGaussians'):
+                decoupling['ngaussians'] = v.NumberOfGaussians
+            if hasattr(v,'ECutoff'):
+                decoupling['ecutoff'] = v.ECutoff
+            if hasattr(v,'WidthOfGaussian'):
+                decoupling['gausswidth'] = v.WidthOfGaussian
+        else:
+            decoupling = None
+        nc.close()
+        return decoupling
+        
+    def set_decoupling(self,
+                       ngaussians=3,
+                       ecutoff=100,
+                       gausswidth=0.35):
+        '''
+        Decoupling activates the three dimensional electrostatic
+        decoupling. Based on paper by Peter E. Bloechl: JCP 103
+        page7422 (1995).
+
+        :Parameters:
+
+          ngaussians : int
+            The number of gaussian functions per atom
+            used for constructing the model charge of the system
+
+          ecutoff : int
+            The cut off energy (eV) of system charge density in
+            g-space used when mapping constructing the model change of
+            the system, i.e. only charge density components below
+            ECutoff enters when constructing the model change.
+
+          gausswidth : float
+            The width of the Gaussians defined by
+            $widthofgaussian*1.5^(n-1)$  $n$=(1 to numberofgaussians)
+            
+        '''
+
+        nc = netCDF(self.get_nc(), 'a')
+        if 'Decoupling' in nc.variables:
+            v = nc.variables['Decoupling']
+        else:
+            v = nc.createVariable('Decoupling', 'c', ())
+
+        v.NumberOfGaussians = ngaussians
+        v.ECutoff = ecutoff
+        v.WidthOfGaussian = gausswidth
+
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def set_external_dipole(self,
+                            value,
+                            position=None):
+        '''
+        Externally imposed dipole potential. This option overwrites
+        DipoleCorrection if set. 
+
+        :Parameters:
+
+          value : float
+            units of volts
+
+          position : float
+            scaled coordinates along third unit cell direction.
+            if None, the compensation dipole layer plane in the
+            vacuum position farthest from any other atoms on both
+            sides of the slab. Do not set to 0.0.
+        '''
+        
+        var = 'ExternalDipolePotential'
+        nc = netCDF(self.get_nc(), 'a')
+        if var in nc.variables:
+            v = nc.variables[var]
+        else:
+            v = nc.createVariable('ExternalDipolePotential', 'd', ())
+        
+        v.assignValue(value)
+        if position is not None:
+            v.DipoleLayerPosition = position
+
+        nc.sync()
+        nc.close()
+        self.set_status('new')
+        self.ready = False
+
+    def get_external_dipole(self):
+        'return the External dipole settings'
+
+        var = 'ExternalDipolePotential'
+        nc = netCDF(self.get_nc(),'r')
+        if var in nc.variables:
+            v = nc.variables[var]
+            value = v.getValue()
+            if hasattr(v, 'DipoleLayerPosition'):
+                position = v.DipoleLayerPosition
+            else:
+                position = None
+
+            ed = {'value':value, 'position':position}
+        else:
+            ed = None
+        nc.close()
+        return ed
+            
+    def set_dipole(self,
+                   status=True,
+                   mixpar=0.2,
+                   initval=0.0,
+                   adddipfield=0.0,
+                   position=None):
+        '''turn on and set dipole correction scheme
+
+        :Parameters:
+
+          status : Boolean
+            True turns dipole on. False turns Dipole off
+
+          mixpar : float
+            Mixing Parameter for the the dipole correction field
+            during the electronic minimization process. If instabilities
+            occur during electronic minimization, this value may be
+            decreased.
+
+          initval : float
+            initial value to start at
+
+          adddipfield : float
+            additional dipole field to add
+            units : V/ang
+            External additive, constant electrostatic field along
+            third unit cell vector, corresponding to an external
+            dipole layer. The field discontinuity follows the position
+            of the dynamical dipole correction, i.e. if
+            DipoleCorrection:DipoleLayerPosition is set, the field
+            discontinuity is at this value, otherwise it is at the
+            vacuum position farthest from any other atoms on both
+            sides of the slab.
+
+          position : float
+            scaled coordinates along third unit cell direction.
+            If this attribute is set, DACAPO will position the
+            compensation dipole layer plane in at the provided value.
+            If this attribute is not set, DACAPO will put the compensation
+            dipole layer plane in the vacuum position farthest from any
+            other atoms on both sides of the slab. Do not set this to
+            0.0
+
+        
+        calling set_dipole() sets all default values.
+            
+        '''
+        if status == False:
+            self.delete_ncattdimvar(self.nc, ncvars=['DipoleCorrection'])
+            return
+        
+        ncf = netCDF(self.get_nc(), 'a')
+        if 'DipoleCorrection' not in ncf.variables:
+            dip = ncf.createVariable('DipoleCorrection', 'c', ())
+        else:
+            dip = ncf.variables['DipoleCorrection']
+        dip.MixingParameter = mixpar
+        dip.InitialValue = initval
+        dip.AdditiveDipoleField = adddipfield
+
+        if position is not None:
+            dip.DipoleLayerPosition = position
+            
+        ncf.sync()
+        ncf.close()
+        self.set_status('new')
+        self.ready = False
+       
+    def set_stay_alive(self, value):
+        'set the stay alive setting'
+        
+        self.delete_ncattdimvar(self.nc,
+                                ncvars=['Dynamics'])
+
+        if (hasattr(self,'parent') or hasattr(self,'children')) and value == True:
+            log.debug("This is a parent/child calculator and stay_alive must be false.")
+            value = False
+
+        if value in [True, False]:
+            self.stay_alive = value
+            #self._dacapo_is_running = False
+        else:
+            log.debug("stay_alive must be boolean. Value was not changed.")
+
+    def get_stay_alive(self):
+        'return the stay alive settings'
+        
+        return self.stay_alive
+
+    def get_fftgrid(self):
+        'return soft and hard fft grids'
+        
+        nc = netCDF(self.nc, 'r')
+        soft = []
+        hard = []
+        for d in [1, 2, 3]:
+            sd = 'softgrid_dim%i' % d
+            hd = 'hardgrid_dim%i' % d
+            if sd in nc.dimensions:
+                soft.append(nc.dimensions[sd])
+                hard.append(nc.dimensions[hd])
+        nc.close()
+        if soft == []:
+            soft = None
+        if hard == []:
+            hard = None
+        return ({'soft':soft,
+                 'hard':hard})
+
+    def get_kpts_type(self):
+        'return the kpt grid type'
+        
+        nc = netCDF(self.nc, 'r')
+
+        if 'BZKpoints' in nc.variables:
+            bv = nc.variables['BZKpoints']
+            if hasattr(bv, 'gridtype'):
+                kpts_type = bv.gridtype #string saved in jacapo
+            else:
+                #no grid attribute, this ncfile was created pre-jacapo
+                kpts_type = '%i kpts' % len(bv[:])
+        else:
+            kpts_type = 'BZKpoints not defined. [[0,0,0]] used by default.'
+
+        nc.close()
+        return kpts_type
+    
+    def get_kpts(self):
+        'return the BZ kpts'
+        nc = netCDF(self.nc, 'r')
+
+        if 'BZKpoints' in nc.variables:
+            bv = nc.variables['BZKpoints']
+            kpts = bv[:]
+        else:
+            kpts = np.array(([0, 0, 0])) #default Gamma point used in Dacapo when
+                             #BZKpoints not defined
+
+        nc.close()
+        return kpts
+        
+    def get_nbands(self):
+        'return the number of bands used in the calculation'
+        nc = netCDF(self.nc, 'r')
+
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            if hasattr(v, 'NumberOfBands'):
+                nbands = int(v.NumberOfBands[0])
+            else:
+                nbands = None
+        else:
+            nbands = None
+            
+        nc.close()
+        return nbands
+    
+    def get_ft(self):
+        'return the FermiTemperature used in the calculation'
+        nc = netCDF(self.nc, 'r')
+
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            if hasattr(v, 'OccupationStatistics_FermiTemperature'):
+                ft = v.OccupationStatistics_FermiTemperature
+            else:
+                ft = None
+        else:
+            ft = None
+        nc.close()
+        return ft
+    
+    def get_dipole(self):
+        'return dictionary of parameters if the DipoleCorrection was used'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        pars = {}
+        if 'DipoleCorrection' in nc.variables:
+            v = nc.variables['DipoleCorrection']
+            pars['status'] = True
+            if hasattr(v, 'MixingParameter'):
+                pars['mixpar'] = v.MixingParameter
+            if hasattr(v, 'InitialValue'):
+                pars['initval'] = v.InitialValue
+            if hasattr(v, 'AdditiveDipoleField'):
+                pars['adddipfield'] = v.AdditiveDipoleField
+            if hasattr(v, 'DipoleLayerPosition'):
+                pars['position'] = v.DipoleLayerPosition
+            
+        else:
+            pars = False
+        nc.close()
+        return pars
+        
+    def get_pw(self):
+        'return the planewave cutoff used'
+        
+        ncf = netCDF(self.nc, 'r')
+        if 'PlaneWaveCutoff' in ncf.variables:
+            pw = ncf.variables['PlaneWaveCutoff'].getValue()
+        else:
+            pw = None
+        ncf.close()
+        
+        if (isinstance(pw, int)
+            or isinstance(pw, float)
+            or isinstance(pw,np.int32)):
+            return pw
+        elif pw is None:
+            return None
+        else:
+            return pw[0]
+
+    def get_dw(self):
+        'return the density wave cutoff'
+        
+        ncf = netCDF(self.nc, 'r')
+        if 'Density_WaveCutoff' in ncf.variables:
+            dw = ncf.variables['Density_WaveCutoff'].getValue()
+        else:
+            dw = None
+        ncf.close()
+
+        #some old calculations apparently store ints, while newer ones
+        #are lists
+        if (isinstance(dw, int)
+            or isinstance(dw, float)
+            or isinstance(dw, np.int32)):
+            return dw
+        else:
+            if dw is None:
+                return None
+            else:
+                return dw[0]
+    
+    def get_xc(self):
+        '''return the self-consistent exchange-correlation functional used
+
+        returns a string'''
+        
+        nc = netCDF(self.nc, 'r')
+        v = 'ExcFunctional'
+        if v in nc.variables:
+            xc = nc.variables[v][:].tostring().strip()
+        else:
+            xc = None
+
+        nc.close()
+        return xc
+
+    def get_number_of_iterations(self):
+
+        niter = None
+
+        if self.calculation_required():
+            self.calculate()
+
+        txt = self.get_txt()
+        if os.path.exists(txt):
+            f = open(txt,'r')
+            for line in f:
+                if 'Number of iterations =' in line:
+                    niter = int(line.split('=')[1])
+                    break
+            f.close()
+                    
+        return niter
+
+    def get_potential_energy(self,
+                             atoms=None,
+                             force_consistent=False):
+        '''
+        return the potential energy.
+        '''
+        
+        if self.calculation_required(atoms):
+            log.debug('calculation required for energy')
+            self.calculate()
+        else:
+            log.debug('no calculation required for energy')
+                        
+        nc = netCDF(self.get_nc(), 'r')
+        try:
+            if force_consistent:
+                e = nc.variables['TotalFreeEnergy'][-1]
+            else:
+                e = nc.variables['TotalEnergy'][-1]
+            nc.close()
+            return e 
+        except (TypeError, KeyError):
+            raise RuntimeError('Error in calculating the total energy\n'
+                               + 'check %s for error messages'
+                               % self.get_txt())
+
+    def get_forces(self, atoms=None):
+        """Calculate atomic forces"""
+        
+        if atoms is None:
+            atoms = self.atoms
+        if self.calculation_required(atoms):
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        forces = nc.variables['DynamicAtomForces'][-1]
+        nc.close()
+        return forces
+
+    def get_atoms(self):
+        'return the atoms attached to a calculator()'
+        
+        if hasattr(self, 'atoms'):
+            if self.atoms is None:
+                return None
+            atoms = self.atoms.copy()
+            #it is not obvious the copy of atoms should have teh same
+            #calculator
+            atoms.set_calculator(self)
+        else:
+            atoms = None
+        return atoms
+
+    def get_nc(self):
+        'return the ncfile used for output'
+        
+        return self.nc
+
+    def get_txt(self):
+        'return the txt file used for output'
+        
+        if hasattr(self,'txt'):
+            return self.txt
+        else:
+            return None
+        
+    def get_psp(self, sym=None, z=None):
+        '''get the pseudopotential filename from the psp database
+
+        :Parameters:
+
+          sym : string
+            the chemical symbol of the species
+
+          z : integer
+            the atomic number of the species
+
+        
+        you can only specify sym or z. Returns the pseudopotential
+        filename, not the full path.
+        '''
+        if sym is None and z is None:
+            return None
+        
+        if (sym is None and z is not None):
+            from ase.data import chemical_symbols
+            sym = chemical_symbols[z]
+        elif (sym is not None and z is None):
+            pass
+        else:
+            raise Exception, 'You can only specify Z or sym!'
+        psp = self.psp[sym]
+        return psp
+    
+    def get_spin_polarized(self):
+        'Return True if calculate is spin-polarized or False if not'
+        
+        #self.calculate() #causes recursion error with get_magnetic_moments
+        nc = netCDF(self.nc, 'r')
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            if hasattr(v, 'SpinPolarization'):
+                if v.SpinPolarization == 1:
+                    spinpol = False
+                elif v.SpinPolarization == 2:
+                    spinpol = True
+            else:
+                spinpol = False
+        else:
+            spinpol = 'Not defined'
+
+        nc.close()
+        return spinpol
+
+    def get_magnetic_moments(self, atoms=None):
+        '''return magnetic moments on each atom after the calculation is
+        run'''
+
+        if self.calculation_required(atoms):
+            self.calculate()
+        nc = netCDF(self.nc, 'r')
+        if 'InitialAtomicMagneticMoment' in nc.variables:
+            mom = nc.variables['InitialAtomicMagneticMoment'][:]
+        else:
+            mom = [0.0]*len(self.atoms)
+
+        nc.close()
+        return mom
+
+    def get_status(self):
+        '''get status of calculation from ncfile. usually one of:
+        'new',
+        'aborted'
+        'running'
+        'finished'
+        None
+        '''
+        
+        nc = netCDF(self.nc, 'r')
+        if hasattr(nc, 'status'):
+            status = nc.status
+        else:
+            status = None
+        nc.close()
+        return status
+
+    def get_calculate_stress(self):
+        'return whether stress is calculated or not'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'TotalStress' in nc.variables:
+            calcstress = True
+        else:
+            calcstress = False
+        nc.close()
+        return calcstress
+        
+    def get_stress(self, atoms=None):
+        '''get stress on the atoms.
+
+        you should have set up the calculation
+        to calculate stress first.
+
+        returns [sxx, syy, szz, syz, sxz, sxy]'''
+        
+        if self.calculation_required(atoms):
+            self.calculate()
+
+        nc = netCDF(self.get_nc(), 'r')
+        if 'TotalStress' in nc.variables:
+            stress = nc.variables['TotalStress'][:]
+            #ase expects the 6-element form
+            stress = np.take(stress.ravel(), [0, 4, 8, 5, 2, 1])
+        else:
+            #stress will not be here if you did not set it up by
+            #calling set_stress() or in the __init__
+            stress = None
+        
+        nc.close()
+        
+        return stress
+
+    def get_psp_valence(self, psp):
+        '''
+        get the psp valence charge on an atom from the pspfile.
+        '''
+        
+        from struct import unpack
+        dacapopath = os.environ.get('DACAPOPATH', '')
+
+        if os.path.exists(psp):
+            #the pspfile may be in the current directory
+            #or defined by an absolute path
+            fullpsp = psp
+        else:
+            #or, it is in the default psp path
+            fullpsp = os.path.join(dacapopath, psp)
+
+        if os.path.exists(fullpsp.strip()):
+            f = open(fullpsp)
+            # read past version numbers and text information
+            buf = f.read(64)
+            # read number valence electrons
+            buf = f.read(8)
+            fmt = ">d"
+            nvalence = unpack(fmt, buf)[0]
+            f.close()
+
+        else:
+            raise Exception, "%s does not exist" % fullpsp
+
+        return nvalence
+
+    def get_psp_nuclear_charge(self, psp):
+        '''
+        get the nuclear charge of the atom from teh psp-file.
+
+        This is not the same as the atomic number, nor is it
+        necessarily the negative of the number of valence electrons,
+        since a psp may be an ion. this function is needed to compute
+        centers of ion charge for the dipole moment calculation.
+
+        We read in the valence ion configuration from the psp file and
+        add up the charges in each shell.
+        '''
+        
+        from struct import unpack
+        dacapopath = os.environ.get('DACAPOPATH')
+
+        if os.path.exists(psp):
+            #the pspfile may be in the current directory
+            #or defined by an absolute path
+            fullpsp = psp
+
+        else:
+            #or, it is in the default psp path
+            fullpsp = os.path.join(dacapopath, psp)
+
+        if os.path.exists(fullpsp.strip()):
+            f = open(fullpsp)
+            unpack('>i', f.read(4))[0]
+            for i in range(3):
+                f.read(4)
+            for i in range(3):
+                f.read(4)
+            f.read(8)
+            f.read(20)
+            f.read(8)
+            f.read(8)
+            f.read(8)
+            nvalps = unpack('>i', f.read(4))[0]
+            f.read(4)
+            f.read(8)
+            f.read(8)
+            wwnlps = []
+            for i in range(nvalps):
+                f.read(4)
+                wwnlps.append(unpack('>d', f.read(8))[0])
+                f.read(8)
+            f.close()
+
+        else:
+            raise Exception, "%s does not exist" % fullpsp
+
+        return np.array(wwnlps).sum()
+       
+    def get_valence(self, atoms=None):
+        '''return the total number of valence electrons for the
+        atoms. valence electrons are read directly from the
+        pseudopotentials.
+
+        the psp filenames are stored in the ncfile. They may be just
+        the name of the file, in which case the psp may exist in the
+        same directory as the ncfile, or in $DACAPOPATH, or the psp
+        may be defined by an absolute or relative path. This function
+        deals with all these possibilities.
+        '''
+        
+        from struct import unpack
+        
+        #do not use get_atoms() or recursion occurs
+        if atoms is None:
+            if hasattr(self, 'atoms'):
+                atoms = self.atoms
+            else:
+                return None
+
+        dacapopath = os.environ.get('DACAPOPATH')
+        totval = 0.0
+        for sym in atoms.get_chemical_symbols():
+            psp = self.get_psp(sym)
+            
+            if os.path.exists(psp):
+                #the pspfile may be in the current directory
+                #or defined by an absolute path
+                fullpsp = psp
+
+            #let's also see if we can construct an absolute path to a
+            #local or relative path psp.
+            abs_path_to_nc = os.path.abspath(self.get_nc())
+            base = os.path.split(abs_path_to_nc)[0]
+            possible_path_to_psp = os.path.join(base, psp)
+            if os.path.exists(possible_path_to_psp):
+                fullpsp = possible_path_to_psp
+            else:
+                #or, it is in the default psp path
+                fullpsp = os.path.join(dacapopath, psp)
+            if os.path.exists(fullpsp.strip()):
+                f = open(fullpsp)
+                # read past version numbers and text information
+                buf = f.read(64)
+                # read number valence electrons
+                buf = f.read(8)
+                fmt = ">d"
+                nvalence = unpack(fmt, buf)[0]
+                f.close()
+                totval += float(nvalence)
+            else:
+                print "%s does not exist" % fullpsp
+                totval = None
+            
+        return totval 
+
+    def calculation_required(self, atoms=None, quantities=None):
+        '''
+        determines if a calculation is needed.
+
+        return True if a calculation is needed to get up to date data.
+        return False if no calculation is needed.
+
+        quantities is here because of the ase interface.
+        '''
+        
+        # first, compare if the atoms is the same as the stored atoms
+        # if anything has changed, we need to run a calculation
+        log.debug('running calculation_required')
+
+        if self.nc is None:
+            raise Exception, 'No output ncfile specified!'
+                
+        if atoms is not None:
+            if not self.atoms_are_equal(atoms):
+                log.debug('found that atoms != self.atoms')
+                tol = 1.0e-6 #tolerance that the unit cell is the same
+                new = atoms.get_cell()
+                old = self.atoms.get_cell()
+                #float comparison of equality
+                if not np.all(abs(old-new) < tol): 
+                    #this often changes the number of planewaves
+                    #which requires a complete restart
+                    log.debug('restart required! because cell changed')
+                    self.restart()
+                else:
+                    log.debug('Unitcells apparently the same')
+                    
+                self.set_atoms(atoms) #we have to update the atoms in any case
+                return True
+            
+        #if we make it past the atoms check, we look in the
+        #nc file. if parameters have been changed the status
+        #will tell us if a calculation is needed
+
+        #past this point, atoms was None or equal, so there is nothing to
+        #update in the calculator
+
+        log.debug('atoms tested equal')
+        if os.path.exists(self.nc):
+            nc = netCDF(self.nc, 'r')
+            if hasattr(nc, 'status'):
+                if nc.status == 'finished' and self.ready:
+                    nc.close()
+                    return False
+                elif nc.status == 'running':
+                    nc.close()
+                    raise DacapoRunning('Dacapo is Running')
+                elif nc.status == 'aborted':
+                    nc.close()
+                    raise DacapoAborted('Dacapo aborted. see txt file!')
+                else:
+                    log.debug('ncfile exists, but is not ready')
+                    nc.close()
+                    return True
+            else:
+                #legacy calculations do not have a status flag in them.
+                #let us guess that if the TotalEnergy is there
+                #no calculation needs to be run?
+                if 'TotalEnergy' in nc.variables:
+                    runflag = False
+                else:
+                    runflag = True
+                nc.close()
+                log.debug('Legacy calculation')
+                return runflag #if no status run calculation
+            nc.close()
+            
+        #default, a calculation is required
+        return True
+
+    def get_scratch(self):
+        '''finds an appropriate scratch directory for the calculation'''
+        
+        import getpass
+        username = getpass.getuser()
+
+        scratch_dirs = []
+        if os.environ.has_key('SCRATCH'):
+            scratch_dirs.append(os.environ['SCRATCH'])
+        if os.environ.has_key('SCR'):
+            scratch_dirs.append(os.environ['SCR'])
+        scratch_dirs.append('/scratch/'+username)
+        scratch_dirs.append('/scratch/')
+        scratch_dirs.append(os.curdir)
+        for scratch_dir in scratch_dirs:
+            if os.access(scratch_dir, os.W_OK):
+                return scratch_dir
+        raise IOError, "No suitable scratch directory and no write access \
+        to current dir."
+
+    def set_parent(self,parent):
+        if hasattr(self,'children'):
+            raise RuntimeError,"Cannot create grandparents."
+        self.parent = parent
+
+    def attach_child(self,child):
+        if hasattr(self,'parent'):
+            raise RuntimeError,"Cannot create grandchildren!"
+        if not hasattr(self,'children'):
+            self.children = []
+        self.children.append(child)
+        child.set_parent(self)
+
+    def calculate(self):
+        '''run a calculation.
+
+        you have to be a little careful with code in here. Use the
+        calculation_required function to tell if a calculation is
+        required. It is assumed here that if you call this, you mean
+        it.'''
+
+        #provide a way to make no calculation get run
+        if os.environ.get('DACAPO_DRYRUN', None) is not None:
+            raise DacapoDryrun, '$DACAPO_DRYRUN detected, and a calculation \
+            attempted'
+
+        if hasattr(self,'children'):
+                # We are a parent and call execute_parent_calculation
+                self.execute_parent_calculation()
+                return
+
+        if hasattr(self,'parent'):  # we're a child and call the parent
+                log.debug("I'm a child. Calling parent instead.")
+                self.parent.calculate()   # call the parent process to calculate all images
+                return
+    
+        if not self.ready:
+            log.debug('Calculator is not ready.')
+            if not os.path.exists(self.get_nc()):
+                self.initnc()
+
+            log.debug('writing atoms out')
+            log.debug(self.atoms)
+            self.write_nc() #write atoms to ncfile
+
+            log.debug('writing input out')
+            self.write_input() #make sure input is uptodate
+   
+            #check that the bands get set
+            if self.get_nbands() is None:  
+                nelectrons = self.get_valence()
+                nbands = int(nelectrons * 0.65 + 4)
+                self.set_nbands(nbands) 
+
+        log.debug('running a calculation')
+
+        nc = self.get_nc()
+        txt = self.get_txt()
+        scratch = self.get_scratch()
+
+        if self.stay_alive:
+            self.execute_external_dynamics(nc, txt)
+            self.ready = True
+            self.set_status('finished')
+        else:
+            # if Dynamics:ExternalIonMotion_script is set in the .nc file from a previous run
+            # and stay_alive is false for the continuation run, the Fortran executable continues
+            # taking steps of size 0 and ends in an infinite loop.
+            # Solution: remove the Dynamics variable if present when not running with stay_alive
+            # 
+            self.delete_ncattdimvar(self.nc,ncvars=['Dynamics'])
+	    cmd = "dacapo.run '%(innc)s' -out '%(txt)s' -scratch %(scratch)s"
+	    cmd = cmd % {'innc':nc,
+			 'txt':txt,
+			 'scratch':scratch}
+
+	    log.debug(cmd)
+	    # using subprocess instead of commands subprocess is more
+	    # flexible and works better for stay_alive
+	    self._dacapo = sp.Popen(cmd,
+				stdout=sp.PIPE,
+				stderr=sp.PIPE,
+				shell=True)
+	    status = self._dacapo.wait()
+	    [stdout, stderr] = self._dacapo.communicate()
+	    output = stdout+stderr
+    
+	    if status is 0: #that means it ended fine!
+		self.ready = True
+		self.set_status('finished')
+	    else:
+		log.debug('Status was not 0')
+		log.debug(output)
+		self.ready = False
+	    # directory cleanup has been moved to self.__del__()
+	    del self._dacapo
+
+	    #Sometimes dacapo dies or is killed abnormally, and in this
+	    #case an exception should be raised to prevent a geometry
+	    #optimization from continuing for example. The best way to
+	    #detect this right now is actually to check the end of the
+	    #text file to make sure it ends with the right line. The
+	    #line differs if the job was run in parallel or in serial.
+	    f = open(txt, 'r')
+	    lines = f.readlines()
+	    f.close()
+
+	    if 'PAR: msexit halting Master' in lines[-1]:
+		pass #standard parallel end
+	    elif ('TIM' in lines[-2]
+		  and 'clexit: exiting the program' in lines[-1]):
+		pass #standard serial end
+	    else:
+		# text file does not end as expected, print the last
+		# 10 lines and raise exception
+		log.debug(string.join(lines[-10:-1], ''))
+		s = 'Dacapo output txtfile (%s) did not end normally.\n'
+		s += ''.join(lines[-10:-1])
+		raise DacapoAbnormalTermination(s % txt)
+
+    def execute_parent_calculation(self):
+        '''
+        Implementation of an extra level of parallelization, where one jacapo calculator spawns several
+        dacapo.run processes. This is used for NEBs parallelized over images.
+        '''                
+        nchildren = len(self.children)
+        log.debug("I'm a parent and start a calculation for ",nchildren," children.")
+        self._dacapo = nchildren*[None]
+        # export the number of children to the environment
+        env = os.environ
+        env['JACAPO_NIMAGES'] = str(nchildren)
+        
+        # start a dacapo.run instance for each child
+        for i,child in enumerate(self.children):
+
+            nc = child.get_nc()
+            txt= child.get_txt()
+            scratch = child.get_scratch()
+
+            if not os.path.exists(nc):
+                child.initnc()
+            child.write_nc() #write atoms to ncfile
+            child.write_input() #make sure input is uptodate
+
+            #check that the bands get set
+            if child.get_nbands() is None:
+                nelectrons = child.get_valence()
+                nbands = int(nelectrons * 0.65 + 4)
+                child.set_nbands(nbands)
+
+            env['JACAPO_IMAGE'] = str(i)
+            cmd = "dacapo.run  '%(innc)s' -out '%(txt)s' -scratch %(scratch)s"
+            cmd = cmd % {'innc':nc,
+                         'txt':txt,
+                         'scratch':scratch}
+
+            log.debug(cmd)
+            self._dacapo[i] = sp.Popen(cmd,stdout=sp.PIPE,stderr=sp.PIPE,shell=True,env=env)
+        
+        print 'now waiting for all children to finish'
+        # now wait for all processes to finish
+        for i,child in enumerate(self.children):
+            status = self._dacapo[i].wait()
+            [stdout,stderr] = self._dacapo[i].communicate()
+            output = stdout+stderr
+            if status is 0: #that means it ended fine!
+                child.ready = True
+                child.set_status('finished')
+            else:
+                log.debug('Status was not 0')
+                log.debug(output)
+                child.ready = False
+
+        # could also check the end of the output .txt file to make sure everything was fine.
+        
+        del self._dacapo
+
+    def execute_external_dynamics(self,
+                                  nc=None,
+                                  txt=None,
+                                  stoppfile='stop',
+                                  stopprogram=None):
+        '''
+        Implementation of the stay alive functionality with socket
+        communication between dacapo and python.  Known limitations:
+        It is not possible to start 2 independent Dacapo calculators
+        from the same python process, since the python PID is used as
+        identifier for the script[PID].py file.
+        '''
+        
+        from socket import socket, AF_INET, SOCK_STREAM, timeout
+        import tempfile
+        
+        if hasattr(self, "_dacapo"):
+            msg = "Starting External Dynamics while Dacapo is runnning: %s"
+            msg = msg % str(self._dacapo.poll())
+            log.debug(msg)
+        else:
+            log.debug("No dacapo instance has been started yet")
+        log.debug("Stopprogram: %s" % stopprogram)
+
+        if not nc:
+            nc = self.get_nc()
+        if not txt:
+            txt = self.get_txt()
+        tempfile.tempdir = os.curdir
+
+        if stopprogram:
+            # write stop file
+            stfile = open(stoppfile, 'w')
+            stfile.write('1 \n')
+            stfile.close()
+
+            # signal to dacapo that positions are ready
+            # let dacapo continue, it is up to the python mainloop 
+            # to allow dacapo enough time to finish properly.
+            self._client.send('ok too proceed')
+
+            # Wait for dacapo to acknowledge that netcdf file has
+            # been updated, and analysis part of the code has been
+            # terminated. Dacapo sends a signal at the end of call
+            # clexit().
+            log.info("waiting for dacapo to exit...")
+            self.s.settimeout(1200.0) # if dacapo exits with an
+                                       # error, self.s.accept()
+                                       # should time out,
+                                      # but we need to give it
+                                      # enough time to write the
+                                      # wave function to the nc
+                                      # file.
+            try:
+                self._client, self._addr = self.s.accept() # Last
+                                                          # mumble
+                                                          # before
+                                                          # Dacapo
+                                                          # dies.
+                os.system("sleep 5") # 5 seconds of silence
+                                                         # mourning
+                                                         # dacapo.
+            except timeout:
+                print '''Socket connection timed out.'''
+                print '''This usually means Dacapo crashed.'''
+                
+            # close the socket s
+            self.s.close()
+            self._client.close()
+
+            # remove the script???? file
+            ncfile = netCDF(nc, 'r')
+            vdyn = ncfile.variables['Dynamics']
+            os.system("rm -f '"+vdyn.ExternalIonMotion_script+"'")
+            ncfile.close()
+            os.system('rm -f '+stoppfile)
+
+            if self._dacapo.poll()==None:  # dacapo is still not dead!
+                # but this should do it!
+                sp.Popen("kill -9 "+str(self._dacapo.pid), shell=True)
+                #if Dacapo dies for example because of too few
+                #bands, subprocess never returns an exitcode.
+                #very strange, but at least the program is
+                #terminated.  print self._dacapo.returncode
+            del self._dacapo
+            return
+
+        if hasattr(self, '_dacapo') and self._dacapo.poll()==None: 
+            # returns  None if  dacapo is running self._dacapo_is_running:
+
+            # calculation_required already updated the positions in
+            # the nc file
+            self._client.send('ok too proceed')
+
+        else:
+
+            # get process pid that will be used as communication
+            # channel
+            pid = os.getpid()
+
+            # setup communication channel to dacapo
+            from sys    import version
+            from string import split
+            effpid = (pid)%(2**16-1025)+1025 # This translate pid
+                                               # [0;99999] to a number
+                                               # in [1025;65535] (the
+                                               # allowed socket
+                                               # numbers)
+
+            self.s = socket(AF_INET, SOCK_STREAM)
+            foundafreesocket = 0
+            while not foundafreesocket:
+                try:
+                    if split(version)[0] > "2":     # new interface
+                        self.s.bind(("", effpid))
+                    else:                           # old interface
+                        self.s.bind("", effpid)
+                    foundafreesocket = 1
+                except:
+                    effpid = effpid + 1
+
+            # write script file that will be used by dacapo
+            scriptname = 'script%s.py' % str(pid)
+            scriptfile = open(scriptname, 'w')
+            scriptfile.write(
+"""#!/usr/bin/env python
+from socket import *
+from sys    import version
+from string import split  
+s = socket(AF_INET,SOCK_STREAM)
+# tell python that dacapo has finished
+if split(version)[0] > "2":     # new interface 
+     s.connect(("",%(effpid)s))
+else:                           # old interface
+     s.connect("",%(effpid)s)
+# wait for python main loop
+s.recv(14)
+""" % {'effpid':str(effpid)})
+            scriptfile.close()
+            os.system('chmod +x ' + scriptname)
+
+            # setup dynamics as external and set the script name
+            ncfile = netCDF(nc, 'a')
+            if 'Dynamics' not in ncfile.variables:
+                vdyn = ncfile.createVariable('Dynamics', 'c', ())
+            else:
+                vdyn = ncfile.variables['Dynamics']
+            vdyn.Type = "ExternalIonMotion" 
+            vdyn.ExternalIonMotion_script = './'+ scriptname
+            ncfile.close()
+
+            # dacapo is not running start dacapo non blocking
+            scratch_in_nc = tempfile.mktemp()
+            os.system('mv '+nc+' '+scratch_in_nc)
+            os.system('rm -f '+stoppfile)
+            scratch = self.get_scratch()
+            cmd = "dacapo.run"
+            cmd += " '%(innc)s' '%(outnc)s' -out '%(txt)s' -scratch %(scratch)s"
+            cmd = cmd % {'innc':scratch_in_nc,
+                         'outnc':nc,
+                         'txt':txt,
+                         'scratch':scratch}
+
+            log.debug(cmd)
+            self._dacapo = sp.Popen(cmd,
+                                    stdout=sp.PIPE,
+                                    stderr=sp.PIPE,
+                                    shell=True)
+
+            self.s.listen(1)
+
+        # wait for dacapo  
+        self._client, self._addr = self.s.accept()
+
+    def write_nc(self, nc=None, atoms=None):
+        '''
+        write out atoms to a netcdffile.
+
+        This does not write out the calculation parameters!
+
+        :Parameters:
+
+          nc : string
+            ncfilename to write to. this file will get clobbered
+            if it already exists.
+
+          atoms : ASE.Atoms
+            atoms to write. if None use the attached atoms
+            if no atoms are attached only the calculator is
+            written out. 
+
+        the ncfile is always opened in 'a' mode.
+
+        note: it is good practice to use the atoms argument to make
+        sure that the geometry you mean gets written! Otherwise, the
+        atoms in the calculator is used, which may be different than
+        the external copy of the atoms.
+
+        '''
+
+        log.debug('writing atoms to ncfile with write_nc')
+        #no filename was provided to function, use the current ncfile
+        if nc is None: 
+            nc = self.get_nc()
+        
+        if nc != self.nc:
+            #this means we are writing a new file, and we should copy
+            #the old file to it first.  this makes sure the old
+            #calculator settings are preserved
+            new = nc
+            old = self.nc
+            log.debug('Copying old ncfile to new ncfile')
+            log.debug("cp '%s' '%s'" % (old, new))
+            os.system("cp '%s' '%s'" % (old, new))
+              
+        if atoms is None:
+            atoms = self.get_atoms()
+
+        log.debug('self.atoms = %s' % str(self.atoms))
+        log.debug('atoms = %s' % str(atoms))
+
+        if atoms is not None: #there may still be no atoms attached
+            log.debug('about to write to %s' % nc)
+            ncf = netCDF(nc, 'a')
+
+            if 'number_of_dynamic_atoms' not in ncf.dimensions:
+                ncf.createDimension('number_of_dynamic_atoms',
+                                    len(atoms))
+            else:
+                # number of atoms is already a dimension, but we might
+                # be setting new atoms here
+                # check for same atom symbols (implicitly includes
+                # a length check)
+                symbols = np.array(['%2s' % s for s in
+                                    atoms.get_chemical_symbols()], dtype='c')
+                ncsym = ncf.variables['DynamicAtomSpecies'][:]
+                if (symbols.size != ncsym.size) or (np.any(ncsym != symbols)):
+                    # the number of atoms or their order has changed.
+                    # Treat this as a new calculation and reset
+                    # number_of_ionic_steps and
+                    # number_of_dynamic_atoms.
+                    ncf.close() #nc file must be closed for
+                                 #delete_ncattdimvar to work correctly
+                    self.delete_ncattdimvar(nc, ncattrs=[],
+                                            ncdims=['number_of_dynamic_atoms',
+                                                    'number_ionic_steps'])
+                    ncf = netCDF(nc, 'a')
+                    ncf.createDimension('number_of_dynamic_atoms',
+                                                  len(atoms)) 
+                    ncf.createDimension('number_ionic_steps', None)
+                    self._set_frame_number(0)
+                    ncf.close() #nc file must be closed for restart to
+                                #work correctly
+                    self.restart()
+                    ncf = netCDF(nc, 'a')
+
+            #now, create variables
+            if 'DynamicAtomSpecies' not in ncf.variables:
+                sym = ncf.createVariable('DynamicAtomSpecies',
+                                         'c',
+                                         ('number_of_dynamic_atoms',
+                                          'dim2',))
+            else:
+                sym = ncf.variables['DynamicAtomSpecies']
+
+            #note explicit array casting was required here
+            symbols = atoms.get_chemical_symbols()
+            sym[:] = np.array(['%2s' % s for s in symbols], dtype='c')
+
+            if 'DynamicAtomPositions' not in ncf.variables:
+                pos = ncf.createVariable('DynamicAtomPositions',
+                                         'd',
+                                         ('number_ionic_steps',
+                                          'number_of_dynamic_atoms',
+                                          'dim3'))
+            else:
+                pos = ncf.variables['DynamicAtomPositions']
+
+            spos = atoms.get_scaled_positions()
+            if pos.typecode() == 'f':
+                spos = np.array(spos, dtype=np.float32)
+            pos[self._frame, :] = spos
+
+            if 'UnitCell' not in ncf.variables:
+                uc = ncf.createVariable('UnitCell', 'd',
+                                        ('number_ionic_steps',
+                                         'dim3', 'dim3'))
+            else:
+                uc = ncf.variables['UnitCell']
+
+            cell = atoms.get_cell()
+            if uc.typecode() == 'f':
+                cell = np.array(cell, dtype=np.float32)
+                
+            uc[self._frame, :] = cell
+
+            if 'AtomTags' not in ncf.variables:
+                tags = ncf.createVariable('AtomTags', 'i',
+                                          ('number_of_dynamic_atoms',))
+            else:
+                tags = ncf.variables['AtomTags']
+
+            tags[:] = np.array(atoms.get_tags(), np.int32)
+
+            if 'InitialAtomicMagneticMoment' not in ncf.variables:
+                mom = ncf.createVariable('InitialAtomicMagneticMoment',
+                                         'd',
+                                         ('number_of_dynamic_atoms',))
+            else:
+                mom = ncf.variables['InitialAtomicMagneticMoment']
+
+            #explain why we have to use get_initial_magnetic_moments()
+            moms = atoms.get_initial_magnetic_moments()
+            if mom.typecode() == 'f':
+                moms = np.array(moms, dtype=np.float32)
+            mom[:] = moms
+            
+            #finally the atom pseudopotentials
+            for sym in atoms.get_chemical_symbols():
+                vn = 'AtomProperty_%s' % sym
+                if vn not in ncf.variables:
+                    p = ncf.createVariable(vn, 'c', ('dim20',))
+                else:
+                    p = ncf.variables[vn]
+
+                ppath = self.get_psp(sym=sym)
+                p.PspotFile = ppath
+
+            ncf.sync()
+            ncf.close()
+               
+            #store constraints if they exist
+            constraints = atoms._get_constraints()
+            if constraints != []:
+                nc = netCDF(self.get_nc(), 'a')
+                if 'constraints' not in nc.variables:
+                    if 'dim1' not in nc.dimensions:
+                        nc.createDimension('dim1', 1)
+                    c = nc.createVariable('constraints', 'c', ('dim1',))
+                else:
+                    c = nc.variables['constraints']
+                #we store the pickle string as an attribute of a
+                #netcdf variable because that way we do not have to
+                #know how long the string is.  with a character
+                #variable you have to specify the dimension of the
+                #string ahead of time.
+                c.data = pickle.dumps(constraints)
+                nc.close()
+            else:
+                # getting here means there where no constraints on the
+                # atoms just written we should check if there are any
+                # old constraints left in the ncfile
+                # from a previous atoms, and delete them if so
+                delete_constraints = False
+                nc = netCDF(self.get_nc())
+                if 'constraints' in nc.variables:
+                    delete_constraints = True
+                nc.close()
+
+                if delete_constraints:
+                    log.debug('deleting old constraints')
+                    self.delete_ncattdimvar(self.nc,
+                                            ncvars=['constraints'])
+        
+    def read_atoms(filename):
+        '''read atoms and calculator from an existing netcdf file.
+
+        :Parameters:
+
+          filename : string
+            name of file to read from.
+
+        static method
+
+        example::
+        
+          >>> atoms = Jacapo.read_atoms(ncfile)
+          >>> calc = atoms.get_calculator()
+
+        this method is here for legacy purposes. I used to use it alot.
+        '''
+        
+        calc = Jacapo(filename)
+        atoms = calc.get_atoms()
+        return atoms
+    
+    read_atoms = staticmethod(read_atoms)
+
+    def read_only_atoms(self, ncfile):
+        '''read only the atoms from an existing netcdf file. Used to
+        initialize a calculator from a ncfilename.
+
+        :Parameters:
+
+          ncfile : string
+            name of file to read from.
+
+        return ASE.Atoms with no calculator attached or None if no
+        atoms found
+        '''
+        
+        from ase import Atoms
+        
+        nc = netCDF(ncfile, 'r')
+        #some ncfiles do not have atoms in them
+        if 'UnitCell' not in nc.variables:
+            log.debug('no unit cell found in ncfile')
+            nc.close()
+            return None
+        
+        cell = nc.variables['UnitCell'][:][-1]
+        sym = nc.variables['DynamicAtomSpecies'][:]
+        symbols = [x.tostring().strip() for x in sym]
+        spos = nc.variables['DynamicAtomPositions'][:][-1]
+
+        pos = np.dot(spos, cell)
+        
+        atoms = Atoms(symbols=symbols,
+                      positions=pos,
+                      cell=cell)
+
+        if 'AtomTags' in nc.variables:
+            tags = nc.variables['AtomTags'][:]
+            atoms.set_tags(tags)
+
+        if 'InitialAtomicMagneticMoment' in nc.variables:
+            mom = nc.variables['InitialAtomicMagneticMoment'][:]
+            atoms.set_initial_magnetic_moments(mom)
+
+        #update psp database
+        for sym in symbols:
+            vn = 'AtomProperty_%s' % sym
+            if vn in nc.variables:
+                var = nc.variables[vn]
+                pspfile = var.PspotFile
+                self.psp[sym] = pspfile
+
+        #get constraints if they exist
+        c = nc.variables.get('constraints', None)
+        if c is not None:
+            constraints = pickle.loads(c.data)
+            atoms.set_constraint(constraints)
+                    
+        nc.close()
+        
+        return atoms
+        
+    def delete_ncattdimvar(self, ncf, ncattrs=None, ncdims=None, ncvars=None):
+        '''
+        helper function to delete attributes,
+        dimensions and variables in a netcdffile
+
+        this functionality is not implemented for some reason in
+        netcdf, so the only way to do this is to copy all the
+        attributes, dimensions, and variables to a new file, excluding
+        the ones you want to delete and then rename the new file.
+
+        if you delete a dimension, all variables with that dimension
+        are also deleted.
+        '''
+
+        if ncattrs is None:
+            ncattrs = []
+        if ncdims is None:
+            ncdims = []
+        if ncvars is None:
+            ncvars = []
+        
+        log.debug('beginning: going to delete dims: %s' % str(ncdims))
+        log.debug('beginning: going to delete vars: %s' % str(ncvars))
+            
+        oldnc = netCDF(ncf, 'r')
+
+        #h,tempnc = tempfile.mkstemp(dir='.',suffix='.nc')
+        tempnc = ncf+'.temp'
+        
+        newnc = netCDF(tempnc, 'w')
+
+        for attr in dir(oldnc):
+            if attr in ['close', 'createDimension',
+                        'createVariable', 'flush', 'sync']:
+                continue
+            if attr in ncattrs:
+                continue #do not copy this attribute
+            setattr(newnc, attr, getattr(oldnc, attr))
+           
+        #copy dimensions
+        for dim in oldnc.dimensions:
+            if dim in ncdims:
+                log.debug('deleting %s of %s' % (dim, str(ncdims)))
+                continue #do not copy this dimension
+            size = oldnc.dimensions[dim]
+            
+            newnc.createDimension(dim, size)
+
+        # we need to delete all variables that depended on a deleted dimension
+        for v in oldnc.variables:
+            dims1 = oldnc.variables[v].dimensions
+            for dim in ncdims:
+                if dim in dims1:
+                    s = 'deleting "%s" because it depends on dim "%s"'
+                    log.debug(s %(v, dim))
+                    ncvars.append(v)
+
+        #copy variables, except the ones to delete
+        for v in oldnc.variables:
+            if v in ncvars:
+                log.debug('vars to delete: %s ' % ncvars)
+                log.debug('deleting ncvar: %s' % v)
+                continue #we do not copy this v over
+
+            ncvar = oldnc.variables[v]
+            tcode = ncvar.typecode()
+            #char typecodes do not come out right apparently
+            if tcode == " ":
+                tcode = 'c'
+            
+            ncvar2 = newnc.createVariable(v, tcode, ncvar.dimensions)
+            try:
+                ncvar2[:] = ncvar[:]
+            except TypeError:
+                #this exception occurs for scalar variables
+                #use getValue and assignValue instead
+                ncvar2.assignValue(ncvar.getValue())
+
+            #and variable attributes
+            #print dir(ncvar)
+            for att in dir(ncvar):
+                if att in ['assignValue', 'getValue', 'typecode']:
+                    continue
+                setattr(ncvar2, att, getattr(ncvar, att))
+
+        oldnc.close()
+        newnc.close()
+
+        s = 'looking for .nfs files before copying: %s'
+        log.debug(s % glob.glob('.nfs*'))
+        
+        #ack!!! this makes .nfsxxx files!!!
+        #os.close(h) #this avoids the stupid .nfsxxx file
+        #import shutil
+        #shutil.move(tempnc,ncf)
+
+        #this seems to avoid making the .nfs files 
+        os.system("cp '%s' '%s'" % (tempnc, ncf))
+        os.system("rm '%s'" % tempnc)
+
+        s = 'looking for .nfs files after copying: %s'
+        log.debug(s %  glob.glob('.nfs*'))
+        
+    def restart(self):
+        '''
+        Restart the calculator by deleting nc dimensions that will
+        be rewritten on the next calculation. This is sometimes required
+        when certain dimensions change related to unitcell size changes
+        planewave/densitywave cutoffs and kpt changes. These can cause
+        fortran netcdf errors if the data does not match the pre-defined
+        dimension sizes.
+
+        also delete all the output from previous calculation.
+        '''
+        
+        log.debug('restarting!')
+        
+        ncdims = ['number_plane_waves',
+                  'number_IBZ_kpoints',
+                  'softgrid_dim1',
+                  'softgrid_dim2',
+                  'softgrid_dim3',
+                  'hardgrid_dim1',
+                  'hardgrid_dim2',
+                  'hardgrid_dim3',
+                  'max_projectors_per_atom',
+                  'atomdos_energygrid_size',
+                  'atomdos_angular_channels',
+                  'atomdos_radial_orbs']
+
+        ncvars = ['TotalEnergy',
+                  'TotalFreeEnergy',
+                  'EvaluateTotalEnergy',
+                  'DynamicAtomForces',
+                  'FermiLevel',
+                  'EnsembleXCEnergies',
+                  'AtomProjectedDOS_IntegratedDOS',
+                  'AtomProjectedDOS_OrdinalMap',
+                  'NumberPlaneWavesKpoint',
+                  'AtomProjectedDOS_EnergyResolvedDOS',
+                  'AtomProjectedDOS_EnergyGrid',
+                  'EvaluateCorrelationEnergy',
+                  'DynamicAtomVelocities',
+                  'KpointWeight',
+                  'EvaluateExchangeEnergy',
+                  'EffectivePotential',
+                  'TotalStress',
+                  'ChargeDensity',
+                  'WaveFunction',
+                  'WaveFunctionFFTindex',
+                  'NumberOfNLProjectors',
+                  'NLProjectorPsi',
+                  'TypeNLProjector1',
+                  'NumberofNLProjectors',
+                  'PartialCoreDensity',
+                  'ChargeDensity',
+                  'ElectrostaticPotential',
+                  'StructureFactor',
+                  'EigenValues',
+                  'OccupationNumbers']
+                  
+        self.delete_ncattdimvar(self.nc,
+                                ncattrs=[],
+                                ncdims=ncdims,
+                                ncvars=ncvars)
+
+        self.set_status('new')
+        self.ready = False
+
+    def get_convergence(self):
+        'return convergence settings for Dacapo'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        vname = 'ConvergenceControl'
+        if vname in nc.variables:
+            v = nc.variables[vname]
+            convergence = {}
+            if hasattr(v, 'AbsoluteEnergyConvergence'):
+                convergence['energy'] = v.AbsoluteEnergyConvergence[0]
+            if hasattr(v, 'DensityConvergence'):
+                convergence['density'] = v.DensityConvergence[0]
+            if hasattr(v, 'OccupationConvergence'):
+                convergence['occupation'] = v.OccupationConvergence[0]
+            if hasattr(v, 'MaxNumberOfSteps'):
+                convergence['maxsteps'] = v.MaxNumberOfSteps[0]
+            if hasattr(v, 'CPUTimeLimit'):
+                convergence['cputime'] = v.CPUTimeLimit[0]
+        else:
+            convergence = None
+
+        nc.close()
+        return convergence
+
+    def set_convergence(self,
+                        energy=0.00001,
+                        density=0.0001,
+                        occupation=0.001,
+                        maxsteps=None,
+                        maxtime=None
+                        ):
+        '''set convergence criteria for stopping the dacapo calculator.
+
+        :Parameters:
+
+          energy : float
+            set total energy change (eV) required for stopping
+
+          density : float
+            set density change required for stopping
+
+          occupation : float
+            set occupation change required for stopping
+
+          maxsteps : integer
+            specify maximum number of steps to take
+
+          maxtime : integer
+            specify maximum number of hours to run.
+
+        Autopilot not supported here.
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        vname = 'ConvergenceControl'
+        if vname in nc.variables:
+            v = nc.variables[vname]
+        else:
+            v = nc.createVariable(vname, 'c', ('dim1',))
+
+        if energy is not None:
+            v.AbsoluteEnergyConvergence = energy
+        if density is not None:
+            v.DensityConvergence = density
+        if occupation is not None:
+            v.OccupationConvergence = occupation
+        if maxsteps is not None:
+            v.MaxNumberOfSteps = maxsteps
+        if maxtime is not None:
+            v.CPUTimeLimit = maxtime
+
+        nc.sync()
+        nc.close()
+
+    def get_charge_mixing(self):
+        'return charge mixing parameters'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        vname = 'ChargeMixing'
+        if vname in nc.variables:
+            v = nc.variables[vname]
+            charge_mixing = {}
+            if hasattr(v, 'Method'):
+                charge_mixing['method'] = v.Method
+            if hasattr(v, 'UpdateCharge'):
+                charge_mixing['updatecharge'] = v.UpdateCharge
+            if hasattr(v, 'Pulay_MixingHistory'):
+                charge_mixing['mixinghistory'] = v.Pulay_MixingHistory[0]
+            if hasattr(v, 'Pulay_DensityMixingCoeff'):
+                charge_mixing['mixingcoeff'] = v.Pulay_DensityMixingCoeff[0]
+            if hasattr(v, 'Pulay_KerkerPrecondition'):
+                charge_mixing['precondition'] = v.Pulay_KerkerPrecondition
+        else:
+            charge_mixing = None
+
+        nc.close()
+        return charge_mixing
+
+    def set_charge_mixing(self,
+                          method='Pulay',
+                          mixinghistory=10,
+                          mixingcoeff=0.1,
+                          precondition='No',
+                          updatecharge='Yes'):
+        '''set density mixing method and parameters
+
+        :Parameters:
+
+          method : string
+            'Pulay' for Pulay mixing. only one supported now
+
+          mixinghistory : integer
+            number of iterations to mix
+            Number of charge residual vectors stored for generating
+            the Pulay estimate on the self-consistent charge density,
+            see Sec. 4.2 in Kresse/Furthmuller:
+            Comp. Mat. Sci. 6 (1996) p34ff
+
+          mixingcoeff : float
+            Mixing coefficient for Pulay charge mixing, corresponding
+            to A in G$^1$ in Sec. 4.2 in Kresse/Furthmuller:
+            Comp. Mat. Sci. 6 (1996) p34ff
+                        
+          precondition : string
+            'Yes' or 'No'
+            
+            * "Yes" : Kerker preconditiong is used,
+               i.e. q$_0$ is different from zero, see eq. 82
+               in Kresse/Furthmuller: Comp. Mat. Sci. 6 (1996).
+               The value of q$_0$ is fix to give a damping of 20
+               of the lowest q vector.
+            
+            * "No" : q$_0$ is zero and mixing is linear (default).
+
+          updatecharge : string
+            'Yes' or 'No'
+            
+            * "Yes" : Perform charge mixing according to
+               ChargeMixing:Method setting
+              
+            * "No" : Freeze charge to initial value.
+               This setting is useful when evaluating the Harris-Foulkes
+               density functional
+              
+        '''
+        
+        if method == 'Pulay':
+            nc = netCDF(self.get_nc(), 'a')
+            vname = 'ChargeMixing'
+            if vname in nc.variables:
+                v = nc.variables[vname]
+            else:
+                v = nc.createVariable(vname, 'c', ('dim1',))
+
+            v.Method = 'Pulay'
+            v.UpdateCharge = updatecharge
+            v.Pulay_MixingHistory = mixinghistory
+            v.Pulay_DensityMixingCoeff = mixingcoeff
+            v.Pulay_KerkerPrecondition = precondition
+
+            nc.sync()
+            nc.close()
+
+        self.ready = False
+
+    def set_electronic_minimization(self,
+                                    method='eigsolve',
+                                    diagsperband=2):
+        '''set the eigensolver method
+
+        Selector for which subroutine to use for electronic
+        minimization
+
+        Recognized options : "resmin", "eigsolve" and "rmm-diis".
+
+        * "resmin" : Power method (Lennart Bengtson), can only handle
+           k-point parallization.
+
+        * "eigsolve : Block Davidson algorithm
+           (Claus Bendtsen et al).
+
+        * "rmm-diis : Residual minimization
+           method (RMM), using DIIS (direct inversion in the iterate
+           subspace) The implementaion follows closely the algorithm
+           outlined in Kresse and Furthmuller, Comp. Mat. Sci, III.G/III.H
+        
+        :Parameters:
+
+          method : string
+            should be 'resmin', 'eigsolve' or 'rmm-diis'
+
+          diagsperband : int
+            The number of diagonalizations per band for
+            electronic minimization algorithms (maps onto internal
+            variable ndiapb). Applies for both
+            ElectronicMinimization:Method = "resmin" and "eigsolve".
+            default value = 2
+        '''
+        
+        nc = netCDF(self.get_nc(), 'a')
+        
+        vname = 'ElectronicMinimization'
+        if vname in nc.variables:
+            v = nc.variables[vname]
+        else:
+            log.debug('Creating ElectronicMinimization')
+            v = nc.createVariable(vname, 'c', ('dim1',))
+
+        log.debug('setting method for ElectronicMinimization: % s' % method)
+        v.Method = method
+        log.debug('setting DiagonalizationsBand for ElectronicMinimization')
+        if diagsperband is not None:
+            v.DiagonalizationsPerBand = diagsperband
+
+        log.debug('synchronizing ncfile')
+        nc.sync()
+        
+        nc.close()
+
+    def get_electronic_minimization(self):
+        '''get method and diagonalizations per band for electronic
+        minimization algorithms'''
+
+        log.debug('getting electronic minimization parameters')
+        
+        nc = netCDF(self.get_nc(), 'r')
+        vname = 'ElectronicMinimization'
+        if vname in nc.variables:
+            v = nc.variables[vname]
+            method = v.Method
+            if hasattr(v, 'DiagonalizationsPerBand'):
+                diagsperband = v.DiagonalizationsPerBand[0]
+            else:
+                diagsperband = None
+        else:
+            method = None
+            diagsperband = None
+        nc.close()
+        return {'method':method,
+                'diagsperband':diagsperband}
+
+    def get_occupationstatistics(self):
+        'return occupation statistics method'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            if hasattr(v, 'OccupationStatistics'):
+                occstat = v.OccupationStatistics
+            else:
+                occstat = None
+        else:
+            occstat = None
+        nc.close()
+        return occstat
+
+    def set_occupationstatistics(self, method):
+        '''
+        set the method used for smearing the occupations.
+
+        :Parameters:
+
+          method : string
+            one of 'FermiDirac' or 'MethfesselPaxton'
+            Currently, the Methfessel-Paxton scheme (PRB 40, 3616 (1989).)
+            is implemented to 1th order (which is recommemded by most authors).
+            'FermiDirac' is the default
+        '''
+            
+        nc = netCDF(self.get_nc(), 'a')
+        if 'ElectronicBands' in nc.variables:
+            v = nc.variables['ElectronicBands']
+            v.OccupationStatistics = method
+        
+        nc.sync()
+        nc.close()
+
+    def get_fermi_level(self):
+        'return Fermi level'
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        ef = nc.variables['FermiLevel'][-1]
+        nc.close()
+        return ef
+
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        '''return occupancies of eigenstates for a kpt and spin
+
+        :Parameters:
+
+          kpt : integer
+            index of the IBZ kpoint you want the occupation of
+
+          spin : integer
+            0 or 1
+        '''
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        occ = nc.variables['OccupationNumbers'][:][-1][kpt, spin]
+        nc.close()
+        return occ
+
+    def get_xc_energies(self, *functional):
+        """
+        Get energies for different functionals self-consistent and
+        non-self-consistent.
+
+        :Parameters:
+
+          functional : strings
+            some set of 'PZ','VWN','PW91','PBE','revPBE', 'RPBE'
+
+        This function returns the self-consistent energy and/or
+	energies associated with various functionals. 
+        The functionals are currently PZ,VWN,PW91,PBE,revPBE, RPBE.
+        The different energies may be useful for calculating improved
+	adsorption energies as in B. Hammer, L.B. Hansen and
+	J.K. Norskov, Phys. Rev. B 59,7413. 
+        Examples: 
+        get_xcenergies() #returns all the energies
+        get_xcenergies('PBE') # returns the PBE total energy
+        get_xcenergies('PW91','PBE','revPBE') # returns a
+	# list of energies in the order asked for
+        """
+        
+        if self.calculation_required():
+            self.calculate()
+
+        nc = netCDF(self.get_nc(), 'r')
+
+        funcenergies = nc.variables['EvaluateTotalEnergy'][:][-1]
+        xcfuncs = nc.variables['EvalFunctionalOfDensity_XC'][:]
+
+        nc.close()
+        
+        xcfuncs = [xc.tostring().strip() for xc in xcfuncs]
+        edict = dict(zip(xcfuncs, funcenergies))
+
+        if len(functional) == 0:
+            #get all energies by default
+            functional = xcfuncs
+
+        return [edict[xc] for xc in functional]
+
+    # break of compatibility
+    def get_ados_data(self,
+                      atoms,
+                      orbitals,
+                      cutoff,
+                      spin):
+        '''get atom projected data
+
+        :Parameters:
+
+          atoms  
+              list of atom indices (integers)
+
+          orbitals
+              list of strings
+              ['s','p','d'],
+              ['px','py','pz']
+              ['d_zz', 'dxx-yy', 'd_xy', 'd_xz', 'd_yz']
+
+          cutoff : string
+            cutoff radius you want the results for 'short' or 'infinite'
+
+          spin
+            : list of integers
+            spin you want the results for
+            [0] or [1] or [0,1] for both
+
+        returns (egrid, ados)
+        egrid has the fermi level at 0 eV
+        '''
+              
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        omapvar = nc.variables['AtomProjectedDOS_OrdinalMap']
+        omap = omapvar[:] #indices
+        c = omapvar.AngularChannels
+        channels = [x.strip() for x in c.split(',')] #channel names
+        #this has dimensions(nprojections, nspins, npoints)
+        ados = nc.variables['AtomProjectedDOS_EnergyResolvedDOS'][:]
+        #this is the energy grid for all the atoms
+        egrid = nc.variables['AtomProjectedDOS_EnergyGrid'][:]
+        nc.close()
+
+        #it is apparently not necessary to normalize the egrid to
+        #the Fermi level. the data is already for ef = 0.
+
+        #get list of orbitals, replace 'p' and 'd' in needed
+        orbs = []
+        for o in orbitals:
+            if o == 'p':
+                orbs += ['p_x', 'p_y', 'p_z']
+            elif o == 'd':
+                orbs += ['d_zz', 'dxx-yy', 'd_xy', 'd_xz', 'd_yz']
+            else:
+                orbs += [o]
+
+        orbinds = [channels.index(x) for x in orbs]
+
+        cutdict = {'infinite':0,
+                   'short':1}
+
+        icut = cutdict[cutoff]
+
+        ydata = np.zeros(len(egrid), np.float)
+        
+        for atomind in atoms:
+            for oi in orbinds:
+                ind = omap[atomind, icut, oi]
+
+                for si in spin:
+                    ydata += ados[ind, si]
+
+        return (egrid, ydata)
+
+    def get_all_eigenvalues(self, spin=0):
+        '''return all the eigenvalues at all the kpoints for a spin.
+
+        :Parameters:
+
+          spin : integer
+            which spin the eigenvalues are for'''
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        ev = nc.variables['EigenValues'][:][-1][:, spin]
+        nc.close()
+        return ev
+    
+    def get_eigenvalues(self, kpt=0, spin=0):
+        '''return the eigenvalues for a kpt and spin
+
+        :Parameters:
+
+          kpt : integer
+            index of the IBZ kpoint
+
+          spin : integer
+            which spin the eigenvalues are for'''
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        ev = nc.variables['EigenValues'][:][-1][kpt, spin]
+        nc.close()
+        return ev
+    
+    def get_k_point_weights(self):
+        'return the weights on the IBZ kpoints'
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        kw = nc.variables['KpointWeight'][:]
+        nc.close()
+        return kw
+
+    def get_magnetic_moment(self, atoms=None):
+        'calculates the magnetic moment (Bohr-magnetons) of the supercell'
+
+        if not self.get_spin_polarized():
+            return None
+        
+        if self.calculation_required():
+            self.calculate()
+
+        nibzk = len(self.get_ibz_kpoints())
+        ibzkw = self.get_k_point_weights()
+        spinup, spindn = 0.0, 0.0
+
+        for k in range(nibzk):
+
+            spinup += self.get_occupation_numbers(k, 0).sum()*ibzkw[k]
+            spindn += self.get_occupation_numbers(k, 1).sum()*ibzkw[k]
+
+        return (spinup - spindn)
+
+    def get_number_of_spins(self):
+        'if spin-polarized returns 2, if not returns 1'
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        spv = nc.variables['ElectronicBands']
+        nc.close()
+
+        if hasattr(spv, 'SpinPolarization'):
+            return spv.SpinPolarization
+        else:
+            return 1
+
+    def get_ibz_kpoints(self):
+        'return list of kpoints in the irreducible brillouin zone'
+        
+        if self.calculation_required():
+            self.calculate()
+        nc = netCDF(self.get_nc(), 'r')
+        ibz = nc.variables['IBZKpoints'][:]
+        nc.close()
+        return ibz
+
+    get_ibz_k_points = get_ibz_kpoints
+
+    def get_bz_k_points(self):
+        'return list of kpoints in the Brillouin zone'
+        
+        nc = netCDF(self.get_nc(), 'r')
+        if 'BZKpoints' in nc.variables:
+            bz = nc.variables['BZKpoints'][:]
+        else:
+            bz = None
+        nc.close()
+        return bz
+    
+    def get_effective_potential(self, spin=1):
+        '''
+        returns the realspace local effective potential for the spin.
+        the units of the potential are eV
+
+        :Parameters:
+
+          spin : integer
+             specify which spin you want, 0 or 1
+          
+        '''
+        
+        if self.calculation_required():
+            self.calculate()
+            
+        nc = netCDF(self.get_nc(), 'r')
+        efp = np.transpose(nc.variables['EffectivePotential'][:][spin])
+        nc.close()
+        fftgrids = self.get_fftgrid()
+        hardgrid = fftgrids['hard']
+        x, y, z = self.get_ucgrid(hardgrid)
+        return (x, y, z, efp)
+        
+    def get_electrostatic_potential(self, spin=0):
+        '''get electrostatic potential
+
+        Netcdf documentation::
+        
+          double ElectrostaticPotential(number_of_spin,
+                                        hardgrid_dim3,
+                                        hardgrid_dim2,
+                                        hardgrid_dim1) ;
+                 ElectrostaticPotential:
+                     Description = "realspace local effective potential" ;
+                     unit = "eV" ;
+                     
+        '''
+        
+        if self.calculation_required():
+            self.calculate()
+            
+        nc = netCDF(self.get_nc(), 'r')
+        esp = np.transpose(nc.variables['ElectrostaticPotential'][:][spin])
+        nc.close()
+        fftgrids = self.get_fftgrid()
+
+        x, y, z = self.get_ucgrid(fftgrids['hard'])
+        
+        return (x, y, z, esp)
+    
+    def get_charge_density(self, spin=0):
+        '''
+        return x,y,z,charge density data
+        
+        x,y,z are grids sampling the unit cell
+        cd is the charge density data
+
+        netcdf documentation::
+        
+          ChargeDensity(number_of_spin,
+                        hardgrid_dim3,
+                        hardgrid_dim2,
+                        hardgrid_dim1)
+          ChargeDensity:Description = "realspace charge density" ;
+                  ChargeDensity:unit = "-e/A^3" ;
+
+        '''
+        
+        if self.calculation_required():
+            self.calculate()
+            
+        nc = netCDF(self.get_nc(), 'r')
+     
+        cd = np.transpose(nc.variables['ChargeDensity'][:][spin])
+
+        #I am not completely sure why this has to be done
+        #it does give units of electrons/ang**3
+        vol = self.get_atoms().get_volume()
+        cd /= vol
+        nc.close()
+        grids = self.get_fftgrid()
+
+        x, y, z = self.get_ucgrid(grids['hard'])
+        return x, y, z, cd
+
+    def get_ucgrid(self, dims):
+        '''Return X,Y,Z grids for uniform sampling of the unit cell
+
+        dims = (n0,n1,n2)
+
+        n0 points along unitcell vector 0
+        n1 points along unitcell vector 1
+        n2 points along unitcell vector 2
+        '''
+        
+        n0, n1, n2 = dims
+        
+        s0 = 1.0/n0
+        s1 = 1.0/n1
+        s2 = 1.0/n2
+
+        X, Y, Z = np.mgrid[0.0:1.0:s0,
+                          0.0:1.0:s1,
+                          0.0:1.0:s2]
+        
+        C = np.column_stack([X.ravel(),
+                             Y.ravel(),
+                             Z.ravel()])
+        
+        atoms = self.get_atoms()
+        uc = atoms.get_cell()
+        real = np.dot(C, uc)
+
+        #now convert arrays back to unitcell shape
+        RX = np.reshape(real[:, 0], (n0, n1, n2))
+        RY = np.reshape(real[:, 1], (n0, n1, n2))
+        RZ = np.reshape(real[:, 2], (n0, n1, n2))
+        return (RX, RY, RZ)
+
+    def get_number_of_grid_points(self):
+        'return soft fft grid'
+        
+        # needed by ase.dft.wannier
+        fftgrids = self.get_fftgrid()
+        return np.array(fftgrids['soft'])
+    
+    def get_wannier_localization_matrix(self, nbands, dirG, kpoint,
+                                        nextkpoint, G_I, spin):
+        'return wannier localization  matrix'
+
+        if self.calculation_required():
+            self.calculate()
+
+        if not hasattr(self, 'wannier'):
+            from utils.wannier import Wannier
+            self.wannier = Wannier(self)
+            self.wannier.set_bands(nbands)
+            self.wannier.set_spin(spin)
+        locmat = self.wannier.get_zi_bloch_matrix(dirG,
+                                                  kpoint,
+                                                  nextkpoint,
+                                                  G_I)
+        return locmat
+
+    def initial_wannier(self,
+                        initialwannier,
+                        kpointgrid,
+                        fixedstates,
+                        edf,
+                        spin):
+        'return initial wannier'
+
+        if self.calculation_required():
+            self.calculate()
+
+        if not hasattr(self, 'wannier'):
+            from utils.wannier import Wannier
+            self.wannier = Wannier(self)
+
+        self.wannier.set_data(initialwannier)
+        self.wannier.set_k_point_grid(kpointgrid)
+        self.wannier.set_spin(spin)
+
+        waves = [[self.get_reciprocal_bloch_function(band=band,
+                                                     kpt=kpt,
+                                                     spin=spin)
+                  for band in range(self.get_nbands())]
+                  for kpt in range(len(self.get_ibz_k_points()))]
+
+        self.wannier.setup_m_matrix(waves, self.get_bz_k_points())
+
+        #lfn is too keep line length below 78 characters
+        lfn = self.wannier.get_list_of_coefficients_and_rotation_matrices
+        c, U = lfn((self.get_nbands(), fixedstates, edf))
+
+        U = np.array(U)
+        for k in range(len(c)):
+            c[k] = np.array(c[k])
+        return c, U
+
+    def get_dipole_moment(self,atoms=None):
+        '''
+        return dipole moment of unit cell
+
+        Defined by the vector connecting the center of electron charge
+        density to the center of nuclear charge density.
+
+        Units = eV*angstrom
+
+        1 Debye = 0.208194 eV*angstrom
+
+        '''
+        if self.calculation_required():
+            self.calculate()
+
+        if atoms is None:
+            atoms = self.get_atoms()
+        
+        #center of electron charge density
+        x, y, z, cd = self.get_charge_density()
+
+        n1, n2, n3 = cd.shape
+        nelements = n1*n2*n3
+        voxel_volume = atoms.get_volume()/nelements
+        total_electron_charge = -cd.sum()*voxel_volume
+
+        
+        electron_density_center = np.array([(cd*x).sum(),
+                                            (cd*y).sum(),
+                                            (cd*z).sum()])
+        electron_density_center *= voxel_volume
+        electron_density_center /= total_electron_charge
+       
+        electron_dipole_moment = electron_density_center*total_electron_charge
+        electron_dipole_moment *= -1.0 #we need the - here so the two
+                                        #negatives don't cancel
+        # now the ion charge center
+        psps = self.get_pseudopotentials()['pspdict']
+        ion_charge_center = np.array([0.0, 0.0, 0.0])
+        total_ion_charge = 0.0
+        for atom in atoms:
+            Z = self.get_psp_nuclear_charge(psps[atom.symbol])
+            total_ion_charge += Z
+            pos = atom.position
+            ion_charge_center += Z*pos
+
+        ion_charge_center /= total_ion_charge
+        ion_dipole_moment = ion_charge_center*total_ion_charge
+
+        dipole_vector = (ion_dipole_moment + electron_dipole_moment)
+        return dipole_vector
+
+    
+    def get_reciprocal_bloch_function(self, band=0, kpt=0, spin=0):
+        '''return the reciprocal bloch function. Need for Jacapo
+        Wannier class.'''
+        
+        if self.calculation_required():
+            self.calculate()
+
+        nc = netCDF(self.get_nc(), 'r')
+
+        # read reciprocal bloch function
+        npw = nc.variables['NumberPlaneWavesKpoint'][:]
+        bf = nc.variables['WaveFunction'][kpt, spin, band]
+        wflist = np.zeros(npw[kpt], np.complex)
+        wflist.real = bf[0:npw[kpt], 1]
+        wflist.imag = bf[0:npw[kpt], 0]
+
+        nc.close()
+
+        return wflist
+
+    def get_reciprocal_fft_index(self, kpt=0):
+        '''return the Wave Function FFT Index'''
+        
+        nc = netCDF(self.get_nc(), 'r')
+        recind = nc.variables['WaveFunctionFFTindex'][kpt, :, :]
+        nc.close()
+        return recind
+
+    def get_ensemble_coefficients(self):
+        'returns exchange correlation ensemble coefficients'
+        
+        # adapted from ASE/dacapo.py
+        # def GetEnsembleCoefficients(self):
+        #     self.Calculate()
+        #     E = self.GetPotentialEnergy()
+        #     xc = self.GetNetCDFEntry('EnsembleXCEnergies')
+        #     Exc = xc[0]
+        #     exc_c = self.GetNetCDFEntry('EvaluateCorrelationEnergy')
+        #     exc_e =  self.GetNetCDFEntry('EvaluateExchangeEnergy')
+        #     exc = exc_c + exc_e
+        #     if self.GetXCFunctional() == 'RPBE':
+        #             Exc = exc[-1][-1]
+        # 
+        #     E0 = xc[1]       # Fx = 0
+        # 
+        #     diff0 = xc[2] # - Exc
+        #     diff1 = xc[3] # - Exc
+        #     diff2 = xc[4] # - Exc
+        #     coefs = (E + E0 - Exc,diff0-E0 ,diff1-E0,diff2-E0)
+        #     print 'ensemble: (%.9f, %.9f, %.9f, %.9f)'% coefs
+        #     return num.array(coefs)
+        if self.calculation_required():
+            self.calculate()
+
+        E = self.get_potential_energy()
+        nc = netCDF(self.get_nc(), 'r')
+        if 'EnsembleXCEnergies' in nc.variables:
+            v = nc.variables['EnsembleXCEnergies']
+            xc = v[:]
+
+        EXC = xc[0]
+
+        if 'EvaluateCorrelationEnergy' in nc.variables:
+            v = nc.variables['EvaluateCorrelationEnergy']
+            exc_c = v[:]
+            
+        if 'EvaluateExchangeEnergy' in nc.variables:
+            v = nc.variables['EvaluateExchangeEnergy']
+            exc_e = v[:]
+
+        exc = exc_c + exc_e
+
+        if self.get_xc == 'RPBE':
+            EXC = exc[-1][-1]
+            
+        E0 = xc[1]    # Fx = 0
+
+        diff0 = xc[2] # - Exc
+        diff1 = xc[3] # - Exc
+        diff2 = xc[4] # - Exc
+        coefs = (E + E0 - EXC, diff0-E0, diff1-E0, diff2-E0)
+        log.info('ensemble: (%.9f, %.9f, %.9f, %.9f)'% coefs)
+        return np.array(coefs)
+
+    def get_pseudo_wave_function(self, band=0, kpt=0, spin=0, pad=True):
+
+        '''return the pseudo wavefunction'''
+        
+        # pad=True does nothing here.
+        if self.calculation_required():
+            self.calculate()
+
+        ibz = self.get_ibz_kpoints()
+
+        #get the reciprocal bloch function
+        wflist = self.get_reciprocal_bloch_function(band=band,
+                                                    kpt=kpt,
+                                                    spin=spin)
+        # wflist == Reciprocal Bloch Function
+ 
+        recind = self. get_reciprocal_fft_index(kpt)
+        grids = self.get_fftgrid()
+        softgrid = grids['soft']
+        
+        # GetReciprocalBlochFunctionGrid
+        wfrec = np.zeros((softgrid), np.complex) 
+
+        for i in xrange(len(wflist)):
+            wfrec[recind[0, i]-1,
+                  recind[1, i]-1,
+                  recind[2, i]-1] = wflist[i]
+
+        # calculate Bloch Function
+        wf = wfrec.copy() 
+        dim = wf.shape
+        for i in range(len(dim)):
+            wf = np.fft.fft(wf, dim[i], axis=i)
+
+        #now the phase function to get the bloch phase
+        basis = self.get_atoms().get_cell()
+        kpoint = np.dot(ibz[kpt], basis) #coordinates of relevant
+                                         #kpoint in cartesian
+                                         #coordinates
+        def phasefunction(coor):
+            'return phasefunction'
+            pf = np.exp(1.0j*np.dot(kpoint, coor))
+            return pf
+
+        # Calculating the Bloch phase at the origin (0,0,0) of the grid
+        origin = np.array([0., 0., 0.])
+        blochphase = phasefunction(origin)
+        spatialshape = wf.shape[-len(basis):]
+        gridunitvectors = np.array(map(lambda unitvector,
+                                       shape:unitvector/shape,
+                                       basis,
+                                       spatialshape))
+
+        for dim in range(len(spatialshape)):
+            # Multiplying with the phase at the origin
+            deltaphase = phasefunction(gridunitvectors[dim])
+            # and calculating phase difference between each point
+            newphase = np.fromfunction(lambda i, phase=deltaphase:phase**i,
+                                     (spatialshape[dim],))
+            blochphase = np.multiply.outer(blochphase, newphase)
+
+        return blochphase*wf
+
+    def get_wave_function(self, band=0, kpt=0, spin=0):
+        '''return the wave function. This is the pseudo wave function
+        divided by volume.'''
+        
+        pwf = self.get_pseudo_wave_function(band=band,
+                                            kpt=kpt,
+                                            spin=spin,
+                                            pad=True)
+        vol = self.get_atoms().get_volume()
+        fftgrids = self.get_fftgrid()
+        softgrid = fftgrids['soft']
+    
+        x, y, z = self.get_ucgrid((softgrid))
+
+        return x, y, z, pwf/np.sqrt(vol)
+
+    def strip(self):
+        '''remove all large memory nc variables not needed for
+        anything I use very often. 
+        '''
+        self.delete_ncattdimvar(self.nc,
+                                ncdims=['max_projectors_per_atom'],
+                                ncvars=['WaveFunction',
+                                        'WaveFunctionFFTindex',
+                                        'NumberOfNLProjectors',
+                                        'NLProjectorPsi',
+                                        'TypeNLProjector1',
+                                        'NumberofNLProjectors',
+                                        'PartialCoreDensity',
+                                        'ChargeDensity',
+                                        'ElectrostaticPotential',
+                                        'StructureFactor'])
+
+# shortcut function names
+Jacapo.get_cd = Jacapo.get_charge_density
+Jacapo.get_wf = Jacapo.get_wave_function
+Jacapo.get_esp = Jacapo.get_electrostatic_potential
+Jacapo.get_occ = Jacapo.get_occupation_numbers
+Jacapo.get_ef = Jacapo.get_fermi_level
+Jacapo.get_number_of_bands = Jacapo.get_nbands
+Jacapo.get_electronic_temperature = Jacapo.get_ft
+Jacapo.get_number_of_electrons = Jacapo.get_valence
diff --git a/ase/calculators/jacapo/lda_psp.py b/ase/calculators/jacapo/lda_psp.py
new file mode 100644
index 0000000..f730c37
--- /dev/null
+++ b/ase/calculators/jacapo/lda_psp.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2005 jrk
+
+""" Default pseudopotential paths are defined here
+"""
+
+__docformat__ = 'reStructuredText'
+
+defaultpseudopotentials = {}
+
diff --git a/ase/calculators/jacapo/mayavis.py b/ase/calculators/jacapo/mayavis.py
new file mode 100644
index 0000000..9da8be9
--- /dev/null
+++ b/ase/calculators/jacapo/mayavis.py
@@ -0,0 +1,80 @@
+'''
+mayavi interface to plot atoms, unit cells, and volumetric data
+'''
+import numpy as np
+from enthought.mayavi import mlab
+mlab.figure(1, bgcolor=(1,1,1), size=(350, 350))
+mlab.clf()
+
+def plot_cylinder(start,end,tube_radius=0.1,color=(0,0,0)):
+    
+    mlab.plot3d([start[0],end[0]],[start[1],end[1]],[start[2],end[2]],
+                tube_radius=tube_radius,color=color)
+    
+    
+def plot_atoms(atoms):
+
+    for atom in atoms:
+        pos = atom.position
+        mlab.points3d([pos[0]],[pos[1]],[pos[2]],
+                      scale_factor=4,
+                      resolution=20,
+                      color=(1,0,0),        #this should get species specifuc
+                      scale_mode='none')
+
+    (u0,u1,u2) = atoms.get_cell()
+    origin = np.array([0.0,0.0,0.0])
+    
+    plot_cylinder(origin,u0)
+    plot_cylinder(origin,u1)
+    plot_cylinder(origin,u2)
+
+    plot_cylinder(u0,u0+u1)
+    plot_cylinder(u0,u0+u2)
+
+    plot_cylinder(u1,u1+u0)
+    plot_cylinder(u1,u1+u2)
+
+    plot_cylinder(u2,u2+u0)
+    plot_cylinder(u2,u2+u1)
+
+    plot_cylinder(u0+u1,u0+u1+u2)
+    plot_cylinder(u1+u2,u0+u1+u2)
+    plot_cylinder(u0+u2,u0+u1+u2)
+    mlab.show()
+
+
+    
+
+
+
+if __name__ == '__main__':
+    from ase.lattice.cubic import *
+
+    from ase.lattice.bravais import cross
+    import numpy as np
+
+    a = np.array([0.5,0,0])
+    c = np.array([0,1,0],dtype=np.float)
+    b1 = c - a
+
+    a = np.array([0,1,0],np.float)
+    c = np.array([0,0.5,0.5])
+    b2 = c - a
+
+    a3 = np.array([2,1,1],np.float)
+
+    a1 = cross(b1,a3)
+    a2 = cross(b2,a3)
+    v211 = FaceCenteredCubic(directions=[a1,a2,a3],
+                             miller=(None,None,[2,1,1]),
+                             symbol='Pd',    
+                             size=(1,1,2),  
+                             debug=0)
+
+    uc = v211.get_cell()
+    uc[2][2] += 10.0
+    v211.set_cell(uc)
+
+    plot_atoms(v211.repeat((2,2,1)))
+    
diff --git a/ase/calculators/jacapo/pbe_psp.py b/ase/calculators/jacapo/pbe_psp.py
new file mode 100644
index 0000000..c609c3d
--- /dev/null
+++ b/ase/calculators/jacapo/pbe_psp.py
@@ -0,0 +1,11 @@
+# Copyright (C) 2005 jrk
+
+""" Default pseudopotential paths are defined here
+"""
+
+__docformat__ = 'reStructuredText'
+
+defaultpseudopotentials = {'O':'008-O-gpbe-rc_1_3_nlc.uspp',
+                           'Pd':'046-Pd-gpe-n-6projectors-floc.uspp',
+                           'Ag':'047-Ag-gpe-n-floc.uspp'}
+
diff --git a/ase/calculators/jacapo/pw91_psp.py b/ase/calculators/jacapo/pw91_psp.py
new file mode 100644
index 0000000..442d1a6
--- /dev/null
+++ b/ase/calculators/jacapo/pw91_psp.py
@@ -0,0 +1,61 @@
+""" Default pseudopotential paths are defined here
+"""
+
+__docformat__ = 'reStructuredText'
+
+defaultpseudopotentials = {'H':'ch_e9g4.pseudo', #  H
+                           'Li':'Li_us_cc.pseudo', #  Li
+                           'Be':'Be_us_cc.pseudo', #  Be
+                           'B':'B_us_cc.pseudo', #  B
+                           'C':'C_us_gga.pseudo', #  C
+                           'N':'N_us.pseudo', #  N
+                           'O':'co_gef_e13_gga.pseudo', #  O
+                           'F':'F_pw91_us_7.3.4.pseudo', #  F
+                           'Na':'Na_tm_lda_cc.pseudo', #  Na
+                           'Mg':'mg_us_gga.pseudo', #  Mg
+                           'Al':'Al_us_gga_org.pseudo', #  Al
+                           'Si':'csi_e8ag4.pseudo', #  Si
+                           'P':'P_us.pseudo', #  P
+                           'S':'S_tm.pseudo', #  S
+                           'Cl':'Cl_us_gga.pseudo', #  Cl
+                           'K':'k_us_gga.pseudo', #  K
+                           'Ca':'Ca_us_cc_pw91.pseudo', #  Ca
+                           'Sc':'Sc_us_cc_pw91.pseudo', #  Sc
+                           'Ti':'ti_us_gga.pseudo', #  Ti
+                           'V':'V_us_pw91_13elec.pseudo', #  V
+                           'Cr':'Cr_us_pw91_14elec.pseudo', #  Cr
+                           'Mn':'Mn_us_gga.pseudo', #  Mn
+                           'Fe':'Fe_us_gga_d2.1.8.pseudo', #  Fe
+                           'Co':'Co_us_gga.pseudo', #  Co
+                           'Ni':'Ni_us_gga.pseudo', #  Ni
+                           'Cu':'Cu_us_gga.pseudo', #  Cu
+                           'Zn':'zn_us_gga.pseudo', #  Zn
+                           'Ga':'ga_pw91_us_13elec.pseudo', #  Ga
+                           'Ge':'ge_pw91_us_14elec.pseudo', #  Ge
+                           'As':'as_pw91_us_15elec.pseudo', #  As
+                           'Kr':'Kr_us_gga.pseudo', #  Kr
+                           'Sr':'Sr_us_cc_pw91.pseudo', #  Sr
+                           'Y':'Y_us_cc_pw91.pseudo', #  Y
+                           'Zr':'Zr_us_gga.pseudo', #  Zr
+                           'Nb':'Nb_us_pw91_13elec.pseudo', #  Nb
+                           'Mo':'Mo_us.pseudo', #  Mo
+                           'Ru':'Ru_us_gga.pseudo', #  Ru
+                           'Rh':'Rh_us_gga_fl.pseudo', #  Rh
+                           'Pd':'pd_us_gga.pseudo', #  Pd
+                           'Ag':'ag_us.pseudo', #  Ag
+                           'Cd':'Cd_us_gga.pseudo', #  Cd
+                           'Sn':'sn_us_f.pseudo', #  Sn
+                           'Sb':'sb_us_gga.pseudo', #  Sb
+                           'Te':'te_tm.pseudo', #  Te
+                           'Xe':'Xe_us_gga.pseudo', #  Xe
+                           'Cs':'cs_tm_7el.pseudo', #  Cs
+                           'Ba':'Ba_us_cc_pw91.pseudo', #  Ba
+                           'La':'La_us_cc_pw91.pseudo', #  La
+                           'Ta':'Ta_us_pw91_13elec.pseudo', #  Ta
+                           'W':'W_us_pw91_6elec.pseudo', #  W
+                           'Re':'re_us_gga_7elec.pseudo', #  Re
+                           'Os':'os_us_gga_7elec_7.3.4.pseudo', #  Os
+                           'Ir':'ir_us_gga_flocal.pseudo', #  Ir
+                           'Pt':'pt_us_gga.pseudo', #  Pt
+                           'Au':'Au_us_gga.pseudo', #  Au
+                           'Bi':'Bi_us_gga.pseudo'}
diff --git a/ase/calculators/jacapo/setup.py b/ase/calculators/jacapo/setup.py
new file mode 100644
index 0000000..252c47e
--- /dev/null
+++ b/ase/calculators/jacapo/setup.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+from glob import glob
+from os.path import join
+
+import os
+import sys
+
+long_description = """\
+Jacapo is a python package providing an interface to Dacapo that
+is compatible with the open source Atomic Simulation
+Environment in the python scripting language."""
+
+if sys.version_info < (2, 3, 0, 'final', 0):
+    raise SystemExit, 'Python 2.3 or later is required!'
+
+packages = ['Jacapo']
+
+tools = ['tools/ncsum',
+         'tools/plotnetcdf',
+         'tools/pysub',
+         'tools/qn_relax',
+         'tools/stripnetcdf']
+
+# Get the current version number:
+execfile('version.py')
+
+setup(name = 'python-Jacapo',
+      version=version,
+      description='Jacapo - ase + Dacapo',
+      url='http://www.fysik.dtu.dk/Campos/ase',
+      maintainer='John Kitchin',
+      maintainer_email='jkitchin at andrew.cmu.edu',
+      license='LGPL',
+      platforms=['linux'],
+      packages=packages,
+      scripts=tools,
+      long_description=long_description)
diff --git a/ase/calculators/jacapo/tools/dacapo.run b/ase/calculators/jacapo/tools/dacapo.run
new file mode 100755
index 0000000..b2095b7
--- /dev/null
+++ b/ase/calculators/jacapo/tools/dacapo.run
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+
+'''
+This script runs dacapo in either serial or parallel
+depending on the existance of an environment variable
+from a queue system. Three queue systems are currently 
+supported:
+PBS               PBS_NODEFILE
+Sun grid engine   PE_HOSTFILE
+LoadLeveler       LOADL_STEP_TYPE
+
+If one of these is found, then the parallel environement
+is set up for lam-mpi and the job run
+
+otherwise a serial job is run
+
+dacapo executables are found from one of these environment
+variables:
+
+DACAPOEXE_SERIAL      default serial executable
+DACAPOEXE_PARALLEL    default parallel executable
+
+You can trick it into running in parallel at the command line
+like this:
+
+env PBS_NODEFILE=pbs.nodes mydacapo.run CO.nc CO.nc -out CO.txt
+env PE_HOSTFILE=sge.nodes mydacapo.run CO.nc CO.nc -out CO.txt
+
+where pbs.nodes is a pbs-style nodefile
+and sge.nodes is a sun grid engine style nodefile
+
+python scripts as a rule tend to return zero I have found, even if you
+tell it to return something else with sys.exit(5) for example. The
+only thing I have been able to get to work is to return zero or not
+zero. sys.exit('anystring') makes the script return non-zero if an
+error occurs. That is why there are so many of these types of commands
+here.  I use the non-zero status to know if an error has occurred
+during the calculation.
+
+
+John Kitchin <jkitchin at andrew.cmu.edu>
+05/22/05
+'''
+
+import os,string,sys
+from subprocess import *    
+
+ARGS = string.join(sys.argv[1:],' ')
+
+def RunSerialDacapo(ARGS):
+    DACAPOEXE = os.environ.get('DACAPOEXE_SERIAL')
+    if DACAPOEXE is None:
+        raise Exception, 'DACAPOEXE_SERIAL was not found in your environment'
+    cmd = string.join([DACAPOEXE,ARGS],' ')
+    status = os.system(cmd)
+    if status != 0:
+        sys.exit('"%s" failed' % cmd)
+        
+### check if LoadLeveler
+'''
+the loadleveler I am familiar with does not use a nodefile
+that the user needs to know aobut. it uses the poe command
+which does this stuff for you. according to an old note in
+the original dacapo.run shell script the nodelist can be
+found in $LOADL_PROCESSOR_LIST
+'''
+if 'LOADL_STEP_TYPE' in os.environ.keys():
+    LL_type = os.environ.get('LOADL_STEP_TYPE')
+    if LL_type == 'PARALLEL':
+        os.environ['OMP_NUM_THREADS'] = '1'
+        MPICMD = 'poe'
+        DACAPOEXE = os.environ.get('DACAPOEXE_PARALLEL')
+        parcmd = string.join([MPICMD,DACAPOEXE,ARGS],' ')
+        status = os.system(parcmd)
+        if status != 0:
+            sys.exit('"%s" failed' % parcmd)
+            
+    elif LL_type == 'SERIAL':
+        RunSerialDacapo(ARGS)
+
+### next check for PBS or SGE
+elif ('PBS_NODEFILE' in os.environ.keys() or
+      'PE_HOSTFILE' in os.environ.keys()):
+
+    #print 'PBS_NODEFILE = ',os.environ.get('PBS_NODEFILE')
+    if 'PBS_NODEFILE' in os.environ.keys():
+        MACHINEFILE = os.environ.get('PBS_NODEFILE')
+        NPROCS = len(open(MACHINEFILE,'r').readlines())
+        JOBID = os.environ.get('PBS_JOBID')
+
+        import shutil
+        nodefile = 'pbs.%s.nodes' % JOBID
+        # i make a copy here for debugging purposes
+        # it is deleted after the job if finished
+        # and the PBS_NODEFILE is temporary somewhere anyway
+        shutil.copy(MACHINEFILE,nodefile)
+
+    # if its not PBS here it must be SGE, but
+    # I check again anyway
+    elif 'PE_HOSTFILE' in os.environ.keys():
+        '''
+        here is the typical contents of the PE_HOSTFILE
+
+        n14.bc.rzg.mpg.de 2 all.q at n14.bc.rzg.mpg.de UNDEFINED
+        o06.bc.rzg.mpg.de 2 all.q at o06.bc.rzg.mpg.de UNDEFINED
+        n11.bc.rzg.mpg.de 2 all.q at n11.bc.rzg.mpg.de UNDEFINED
+
+        below, I parse the contents of this file to create the nodefile
+        for lam-mpi:
+
+        n14.bc.rzg.mpg.de
+        n14.bc.rzg.mpg.de
+        o06.bc.rzg.mpg.de
+        o06.bc.rzg.mpg.de
+        n11.bc.rzg.mpg.de
+        n11.bc.rzg.mpg.de
+        '''
+
+        MACHINEFILE = os.environ.get('PE_HOSTFILE')    
+        JOBID = os.environ.get('JOB_ID')
+        NPROCS = 0
+
+        nodefile = 'sge.%s.nodes' % JOBID
+        nf = open(nodefile,'w')
+
+        for line in open(MACHINEFILE,'r'):
+            # nodename = fields[0]
+            # ncpus = fields[1]
+            # queue = fields[2]
+            # UNDEFINED = fields[3]
+            fields = string.split(line)
+            if __debug__:
+                print fields
+
+            nodename = fields[0]
+            nprocs = int(fields[1])
+            if __debug__:
+                print nodename,nprocs
+            for n in range(nprocs):
+                nodeline = '%s\n' % (fields[0])
+                nf.write(nodeline)
+
+            NPROCS += nprocs
+
+        nf.close()
+
+        if __debug__:
+            print 'SGE_O_WORKDIR = ',os.environ.get('SGE_O_WORKDIR')
+            print 'NHOSTS = ',os.environ.get('NHOSTS')
+            print 'NSLOTS = ',os.environ.get('NSLOTS')
+
+
+    if NPROCS > 1:
+        # now construct the mpirun command
+        MPICMD = 'mpirun -np %i' % NPROCS
+        DACAPOEXE = os.environ.get('DACAPOEXE_PARALLEL')
+        parcmd = string.join([MPICMD,DACAPOEXE,ARGS],' ')
+        if __debug__: print parcmd
+
+        print 'Running "%s"' % parcmd
+        p = Popen(parcmd,
+                  shell=True,
+                  stdin=PIPE,
+                  stdout=PIPE,
+                  close_fds=True,
+                  cwd=os.getcwd())
+
+        p_pid = p.pid
+
+        status = p.wait()
+
+        if status != 0:
+            (sout,serr) = p.communicate()
+            print 'stdout = ',sout
+            print 'stderr = ',serr
+            all_is_ok = False
+            print '**** the command failed ****'
+
+        if not all_is_ok:
+            sys.exit('"%s" failed' % parcmd)
+
+        print
+        print 'One iteration from parallel run complete'
+        print '*******************************************************'
+        print
+    else:
+        RunSerialDacapo(ARGS)
+   
+      
+else:
+    # serial job, no parallel environment found.
+    RunSerialDacapo(ARGS)
+
+#remove the nodefile
+try:
+    if os.path.exists(nodefile):
+        os.remove(nodefile)
+except:
+    pass
+        
diff --git a/ase/calculators/jacapo/tools/ncsum b/ase/calculators/jacapo/tools/ncsum
new file mode 100755
index 0000000..a007854
--- /dev/null
+++ b/ase/calculators/jacapo/tools/ncsum
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+from optparse import OptionParser
+from ase.visualize import view
+from ase.calculators.jacapo import *
+from Scientific.IO.NetCDF import NetCDFFile as NetCDF
+import os
+
+os.environ['DACAPO_READONLY'] = 'On'
+
+parser = OptionParser(usage='ncsum',
+                      version='0.1')
+parser.add_option('-e',
+                  nargs=0,
+                  help = 'print only the energy')
+
+parser.add_option('-p',
+                  nargs=0,
+                  help='view atoms')
+
+parser.add_option('-r',
+                  nargs=1,
+                  help='repeat plotted atoms in n1,n2,n3 times')
+
+options,args = parser.parse_args()
+
+for arg in args:
+
+    calc = Jacapo(arg)
+    atoms = calc.get_atoms()
+
+    if options.e is not None:
+        nc = netCDF(arg,'r')
+        energy = nc.variables.get('TotalEnergy',None)
+        if energy is not None:
+            print energy[:][-1]
+        else:
+            print None
+        nc.close()
+    else:
+        print calc
+
+    if options.p is not None:
+        if options.r is not None:
+            n1,n2,n3 = [int(x) for x in options.r.split(',')]
+        else:
+            n1,n2,n3 = (1,1,1)
+
+        view(atoms.repeat([n1,n2,n3]))
+
diff --git a/ase/calculators/jacapo/tools/plotnetcdf b/ase/calculators/jacapo/tools/plotnetcdf
new file mode 100755
index 0000000..975fa98
--- /dev/null
+++ b/ase/calculators/jacapo/tools/plotnetcdf
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+from optparse import OptionParser
+import os, tempfile
+from ase.calculators.jacapo import *
+from ase.io.pdb import *
+
+parser = OptionParser(usage='%prog [-r R1 R2 R3]] ncfile',
+                      version='%prog 0.1')
+
+parser.add_option('-r', '--repeat', type='int', nargs=3,
+                  help='Repeat R1, R2, R3 times along the three axes',
+                  metavar='R1 R2 R3')
+
+parser.add_option('-t', '--time', type='int', nargs=1,
+                  help='sleep for t seconds',
+                  metavar='t')
+
+options, args = parser.parse_args()
+ncfile = args[0]
+
+if options.repeat is None:
+    options.repeat = (1,1,1)
+
+atoms = Jacapo.read_atoms(ncfile)
+
+handle,tempfilename = tempfile.mkstemp()
+write_pdb(tempfilename,atoms.repeat(options.repeat))
+
+script = '''\
+zap
+load %s
+script %s/.rasmolrc
+select all
+spacefill
+#rotate x 180
+#rotate z 180
+''' % (tempfilename,os.environ['HOME'])
+
+handle,tempscriptname = tempfile.mkstemp()
+f = open(tempscriptname,'w')
+f.write(script)
+f.close()
+
+os.system('rasmol -script %s' % tempscriptname)
+        
+os.remove(tempfilename)
+os.remove(tempscriptname)
diff --git a/ase/calculators/jacapo/tools/printlocalsetup b/ase/calculators/jacapo/tools/printlocalsetup
new file mode 100755
index 0000000..6308c07
--- /dev/null
+++ b/ase/calculators/jacapo/tools/printlocalsetup
@@ -0,0 +1,297 @@
+#!/usr/bin/env python
+import os,string,sys
+
+# tell me about the architecture
+print 'Running as user: ', os.environ['USER']
+print 'In directory: ',os.getcwd()
+
+try:
+    import platform
+    print '------------- system information ---------------'
+    print 'Hostname       = ',os.environ['HOST']
+    print 'Architecture   = ',platform.architecture()
+    print 'distribution   = ',platform.dist()
+    print 'libc version   = ',platform.libc_ver()
+    print 'Machine type   = ',platform.machine()
+    print 'Platform       = ',platform.platform()
+    print 'processor type = ',platform.processor()
+    print 'system         = ',platform.system()
+    print 'system version = ',platform.version()
+
+    print
+    print '------------- Python information --------'
+    print 'python was compiled with: ',platform.python_compiler()
+    print 'python version = ',platform.python_version()
+    print 'python was built: ',platform.python_build()
+
+except:
+    print '*** you have an older version of python'
+    print '*** you missed some important system information because of that'
+    print '*** consider upgrading to version 2.3 or greater'
+    print 'python version = ',sys.version
+    print 'uname = '
+    os.system('uname -a')
+
+   
+print '-------------- User ---------------------'
+shell = os.environ.get('SHELL')
+print 'SHELL = ',shell
+
+print
+
+try:
+    import Numeric
+    print 'Numeric version = ',Numeric.__version__
+except:
+    print '*** Numeric is not installed.'
+    print '*** Get it from http://sourceforge.net/projects/numpy'
+
+try:
+    import numarray
+    print 'numarray version = ',numarray.__version__
+except:
+    print '*** numarray is not installed.'
+    print '*** Get it from http://sourceforge.net/projects/numpy'
+
+try:
+    import numpy
+    print 'numpy version = ', numpy.__version__
+except:
+    print '*** numpy is not installed'
+
+try:
+    import Scientific
+    print 'Found Scientific'
+    try:
+        import Scientific.IO.NetCDF
+        print 'Found Scientific.IO.NetCDF'
+    except:
+        print 'Scientific.IO.NetCDF appears broken.'
+        print 'Is netcdf installed?'
+        print 'did you set $NETCDF_PREFIX when you installed Scientific?'
+except:
+    print '*** Scientific not installed'
+    print '*** Get it at http://starship.python.net/~hinsen/ScientificPython/'
+
+
+try:
+    import ASE
+    print 'Found ASE at ', ASE.__file__
+except Exception,error:
+    print error
+    print '*** No ASE found. Did you install it?'
+
+try:
+    import ase
+    print 'Found an ase version: "%s"' % ase.__version__
+    print 'at :',ase.__file__
+except:
+    print '*** No ase found. Did you install it?'
+
+
+try:
+    import Dacapo
+    print 'Found Dacapo python modules'
+except:
+    print '*** No Dacapo modules found, did you install them?'
+    print '    Get them at dcwww.fysik.dtu.dk/campos'
+
+try:
+    import Gnuplot
+    print 'Found Gnuplot python module'
+except:
+    print '*** No Gnuplot module found'
+
+try:
+    import matplotlib
+    print 'Found matplotlib version: ',matplotlib.__version__
+except:
+    print 'no matplotlib found'
+
+libs = ['cblas',
+        'lapack',
+        'f77blas',
+        'atlas',
+        'fftw',
+        'netcdf',
+        'lamf77mpi']
+
+libpaths = ['/lib',
+            '/usr/lib',
+            '/usr/local/lib',
+            os.path.join(os.environ['HOME'],'lib'),
+            ]
+
+if 'LD_LIBRARY_PATH' in os.environ:
+    for libpath in os.environ['LD_LIBRARY_PATH'].split(':'):
+        libpaths.append(libpath)
+
+print
+print '------------------ libraries ------------------'
+for lib in libs:
+    found = False
+    for path in libpaths:
+        if os.path.exists(os.path.join(path,'lib%s.a' % lib)):
+            found = True
+            print 'found %s in %s' % ('lib%s.a' % lib,path)
+    if not found:
+        print '***  Could not find lib%s.a' % lib
+
+
+
+
+def IsOnPath(file):
+    if os.path.isabs(file):
+        if os.path.exists(file):
+            return file
+        else:
+            return False
+    else:
+        path = string.split(os.environ['PATH'],':')
+        for dir in path:
+            if os.path.isdir(dir):
+                if file in os.listdir(dir):
+                    return os.path.join(dir,file)
+    return False
+
+def FileIsExecutable(file):
+    if file is not None:
+        return os.access(file,os.X_OK)
+    else:
+        return False
+
+
+
+print
+print '------------------- compilers -----------------'
+c = ['pgf90','pgf77',
+     'ifort','ifc',
+     'g95',
+     'gcc','g77','f77', 'f90',
+     'mpif77','mpf90',
+     'xlf_r','xlf90_r','mpxlf_r'
+     ]
+for compiler in c:
+    if IsOnPath(compiler):
+        print '%s  found' % compiler
+    else:
+        print '*** %s not found' % compiler
+
+
+    
+print
+print '-------------- Check for ASE and Dacapo tools -------------'
+dacapo_tools = ['dacapo.run',
+                'stripnetcdf'
+                ]
+
+for exe in dacapo_tools:
+    f = IsOnPath(exe)
+    if f:
+        if FileIsExecutable(f):
+            print '%s found at %s' % (exe,f)
+        else:
+            print '%s found, but it is not executable' % exe
+            
+    else:
+        print '%s not found' % exe
+        print 'Dacapo/Tools is not on your executable path'
+        
+
+ase_executables = ['plottrajectory']
+
+for exe in ase_executables:
+    f = IsOnPath(exe)
+    if f:
+        if FileIsExecutable(f):
+            print '%s found at %s' % (exe,f)
+        else:
+            print '%s found, but it is not executable' % exe
+            
+    else:
+        print '*** %s not found' % exe
+        print 'ASE/Tools is not on your executable path'
+
+
+
+print
+print '-------- Location of dacapo executables ------------'
+        
+exe = os.environ.get('DACAPOEXE_SERIAL',None)
+f = IsOnPath(exe)
+if f:
+    if FileIsExecutable(f):
+        print 'default serial executable is: %s' % (exe)
+    else:
+        print '%s found, but it is not executable' % exe
+
+else:
+    print '*** %s not found' % exe
+    print 'No default serial dacapo executable found'
+        
+exe = os.environ.get('DACAPOEXE_PARALLEL',None)
+f = IsOnPath(exe)
+if f:
+    if FileIsExecutable(f):
+        print 'default parallel executable is: %s' % (exe)
+    else:
+        print '%s found, but it is not executable' % exe
+
+else:
+    print '*** %s not found' % exe
+    print 'No default parallel dacapo executable found'
+
+
+psp = os.environ.get('DACAPOPATH',None)
+if os.path.isdir(psp):
+    print 'Pseudopotential database = ',psp
+else:
+    print '*** "%s" is not a directory, please check  $DACAPOPATH'
+
+
+print
+print '-----------miscellaneous utilities-------------'
+for exe in ['rasmol','gnuplot','vmd','vtk',
+            'rsync','ssh','scp']:
+    f = IsOnPath(exe)
+    if f:
+        if FileIsExecutable(f):
+            print '%s found at %s' % (exe,f)
+        else:
+            print '%s found, but it is not executable' % exe
+            
+    else:
+        print '*** %s not found on your path' % exe
+        
+
+print
+print '--------------- mpi ------------------'
+for exe in ['recon','lamboot','mpirun','lamhalt']:
+    f = IsOnPath(exe)
+    if f:
+        if FileIsExecutable(f):
+            print '%s found at %s' % (exe,f)
+        else:
+            print '%s found, but it is not executable' % exe
+            
+    else:
+        print '*** %s not found' % exe
+        print 'maybe you do not have lam-mpi installed'
+
+
+
+
+print
+print '---------- PYTHON environment variables -------------'
+print 'PYTHONSTARTUP = ',os.environ.get('PYTHONSTARTUP')
+print 'PYTHONOPTIMIZE = ',os.environ.get('PYTHONOPTIMIZE')
+print 'PYTHONPATH:'
+for x in sys.path:
+    print '"%s"' % x
+
+
+print
+print '----------- system path --------------------'
+path = os.environ.get('PATH')
+for x in  string.split(path,':'):
+    print '"%s"'  % x
diff --git a/ase/calculators/jacapo/tools/pysub b/ase/calculators/jacapo/tools/pysub
new file mode 100755
index 0000000..0dc0c96
--- /dev/null
+++ b/ase/calculators/jacapo/tools/pysub
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+'''
+this is pure laziness to submit python scripts to PBS
+without the usual cd $PBS_O_WORKDIR stuff
+'''
+
+import os,sys, tempfile
+from optparse import OptionParser
+
+parser = OptionParser(usage='pysub',
+                      version='0.1')
+parser.add_option('-q',
+                  nargs=1,
+                  help = 'submit job to the queue with options to qsub')
+
+parser.add_option('-n',
+                  nargs=1,
+                  help='number of nodes to ask for')
+
+options,args = parser.parse_args()
+
+qdict = {'short':' -l cput=24:00:00,mem=500mb -j oe',
+         'long':' -l cput=168:00:00,mem=500mb -j oe',
+         'hogs':' -l cput=24:00:00,mem=2500mb -j oe',
+         'hogl':' -l cput=168:00:00,mem=2800mb -j oe',}
+
+for pyscript in args:
+
+    h,fname = tempfile.mkstemp()
+
+    cmd ='''\
+#!/bin/tcsh
+
+cd $PBS_O_WORKDIR
+
+python %s
+
+#end''' % pyscript
+
+    f = open(fname,'w')
+    f.write(cmd)
+    f.close()
+
+    if options.q is None:
+        qsub_options = '-l cput=24:00:00,mem=2500mb -j oe'
+    elif options.q in qdict:
+        qsub_options = qdict[options.q]
+    else:
+        qsub_options = options.q
+
+    if options.n is not None:
+        qsub_options += ' -l nodes=%i' % int(options.n)
+
+    cmd = 'qsub -N %(name)s %(options)s %(script)s' % {'name':pyscript,
+                                           'options':qsub_options,
+                                           'script':fname}
+
+    print cmd
+
+    os.system(cmd)
+    os.close(h)
+    os.remove(fname)
diff --git a/ase/calculators/jacapo/tools/qn_relax b/ase/calculators/jacapo/tools/qn_relax
new file mode 100755
index 0000000..b9c54f2
--- /dev/null
+++ b/ase/calculators/jacapo/tools/qn_relax
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+from torque import *
+from ase import *
+from ase.optimize import QuasiNewton
+from ase.constraints import FixAtoms
+from ase.calculators.jacapo import *
+
+import os, string, sys, tempfile
+
+from optparse import OptionParser
+
+'''
+qn_relax -q "-l cput=168:00:00,mem=2000mb -l nodes=3 -j oe"
+
+relax atoms tagged with 5 and 6
+qn_relax -t 5,6
+'''
+
+parser = OptionParser(usage='qn_relax',
+                      version='0.1')
+parser.add_option('-q',
+                  nargs=1,
+                  help = 'submit job to the queue with options to qsub')
+
+parser.add_option('-n',
+                  nargs=1,
+                  help = 'number of nodes to ask for')
+
+parser.add_option('-t',
+                  nargs=1,
+                  help = 'specify which tags to relax, comma-separated list')
+
+options,args = parser.parse_args()
+
+for ncfile in args:
+ 
+    base,ext = os.path.splitext(ncfile)
+    atoms = Jacapo.read_atoms(ncfile)
+
+    if options.t is None:
+        freetags = [1]
+    elif options.t == 'all':
+        freetags = atoms.get_tags()
+    else:
+        freetags = [int(x) for x in options.t.split(',')]
+
+    #True means fixed
+    mask = [atom.get_tag() not in freetags for atom in atoms]
+    
+    #if False not in mask:
+    #    raise Exception, 'No free atoms found!'
+
+    atoms.set_constraint(FixAtoms(mask=mask))
+
+    if options.q is None:
+        calc = atoms.get_calculator()
+        calc.stay_alive = True
+        qn = QuasiNewton(atoms,trajectory=base+'.traj')
+        qn.run(fmax=0.05)
+
+    else:
+        
+        h,fname = tempfile.mkstemp() 
+        script = '''\
+#!/bin/tcsh
+
+cd $PBS_O_WORKDIR
+
+qn_relax -t %(tags)s %(ncfile)s
+
+#end''' % {'ncfile':ncfile,
+        'tags':string.join([str(t) for t in freetags],',')}
+
+        print script
+        f = open(fname,'w')
+        f.write(script)
+        f.close()
+
+        qdict = {'short':'-l cput=24:00:00,mem=500mb -j oe',
+                 'long':'-l cput=168:00:00,mem=500mb -j oe',
+                 'hogs':'-l cput=24:00:00,mem=2500mb -j oe',
+                 'hogl':'-l cput=168:00:00,mem=2800mb -j oe',
+                 }
+
+        if options.q in qdict:
+            qsub_options = qdict[options.q]
+        else:
+            qsub_options = options.q
+
+        if options.n is not None:
+            qsub_options += ' -l nodes=%i' % int(options.n)
+
+        cmd = 'qsub -N %(name)s %(options)s %(script)s' % {'name':ncfile,
+                                                           'options':qsub_options,
+                                                           'script':fname}
+
+        print cmd
+
+        os.system(cmd)
+        os.close(h)
+        os.remove(fname)
diff --git a/ase/calculators/jacapo/tools/stripnetcdf b/ase/calculators/jacapo/tools/stripnetcdf
new file mode 100755
index 0000000..c0ffabb
--- /dev/null
+++ b/ase/calculators/jacapo/tools/stripnetcdf
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+from optparse import OptionParser
+from ase.calculators.jacapo import *
+from Scientific.IO.NetCDF import NetCDFFile as NetCDF
+
+parser = OptionParser(usage='stripnetcdf',
+                      version='0.1')
+
+options,args = parser.parse_args()
+
+for arg in args:
+
+    #check if it is a dacapo file
+    nc = NetCDF(arg,'r')
+    if hasattr(nc,'history'):
+        if nc.history != 'Dacapo':
+            nc.close()
+            continue
+    else:
+        nc.close()
+        continue
+
+    calc = Jacapo(arg)
+    calc.strip()
+    print 'stripped %s' % arg
+
diff --git a/ase/calculators/jacapo/utils/__init__.py b/ase/calculators/jacapo/utils/__init__.py
new file mode 100644
index 0000000..874952f
--- /dev/null
+++ b/ase/calculators/jacapo/utils/__init__.py
@@ -0,0 +1,12 @@
+import Scientific
+
+try:
+    if Scientific.__version__ < 2.8:
+        print 'your ScientifPython version is: ',Scientific.__version__ 
+        print 'ScientificPython 2.8 or greater required for numpy support in NetCDF'
+        raise Exception
+except AttributeError:
+    print 'It appears your ScientificPython has no version.'
+    print 'That probably means it is not 2.8, which is required'
+    raise Exception
+
diff --git a/ase/calculators/jacapo/utils/bader.py b/ase/calculators/jacapo/utils/bader.py
new file mode 100644
index 0000000..cc77e13
--- /dev/null
+++ b/ase/calculators/jacapo/utils/bader.py
@@ -0,0 +1,206 @@
+import commands, os, string, tempfile, shutil
+from ase import write
+from ase.units import Bohr
+
+class Bader:
+    '''class for running bader analysis and extracting data from it.
+
+    The class runs bader, extracts the charge density and outputs it
+    to a cube file. Then you call different functions of the class to
+    extract the charges, volumes, etc...
+
+    ACF.dat contains the coordinates of each atom, the charge
+    associated with it according to Bader partitioning, percentage of
+    the whole according to Bader partitioning and the minimum distance
+    to the surface. This distance should be compared to maximum
+    cut-off radius for the core region if pseudo potentials have been
+    used.
+
+    BCF.dat contains the coordinates of each Bader maxima, the charge
+    within that volume, the nearest atom and the distance to that
+    atom.
+
+    AtomVolumes.dat contains the number of each volume that has been
+    assigned to each atom. These numbers correspond to the number of
+    the BvAtxxxx.dat files.
+
+    The options for the executable are::
+
+        bader [ -c bader | voronoi ]
+              [ -n bader | voronoi ]
+              [ -b neargrid | ongrid ]
+              [ -r refine_edge_iterations ]
+              [ -ref reference_charge ]
+              [ -p all_atom | all_bader ]
+              [ -p sel_atom | sel_bader ] [volume list]
+              [ -p atom_index | bader_index ]
+              [ -i cube | chgcar ]
+              [ -h ] [ -v ]
+              chargefile
+
+    References:
+    
+    G. Henkelman, A. Arnaldsson, and H. Jonsson, A fast and robust
+    algorithm for Bader decomposition of charge density,
+    Comput. Mater. Sci. 36 254-360 (2006).
+
+    E. Sanville, S. D. Kenny, R. Smith, and G. Henkelman An improved
+    grid-based algorithm for Bader charge allocation,
+    J. Comp. Chem. 28 899-908 (2007).
+
+    W. Tang, E. Sanville, and G. Henkelman A grid-based Bader analysis
+    algorithm without lattice bias, J. Phys.: Condens. Matter 21
+    084204 (2009).
+    '''
+    def __init__(self,atoms):
+        '''
+ 
+        '''
+        self.atoms = atoms
+
+        #get density and write cube file
+        calc = atoms.get_calculator()
+        ncfile = calc.get_nc()
+        base,ext = os.path.splitext(ncfile)
+
+        x,y,z,density = calc.get_charge_density()
+        cubefile = base + '_charge_density.cube'
+        self.densityfile = cubefile
+
+        if not os.path.exists(cubefile):
+            write(cubefile, atoms,data=density*Bohr**3)
+        
+        #cmd to run for bader analysis. check if output exists so we
+        #don't run this too often.
+        acf_file = base + '_ACF.dat'
+        if not os.path.exists(acf_file):
+            #mk tempdir
+            tempdir = tempfile.mkdtemp()
+            
+            cwd = os.getcwd()
+            os.chdir(tempdir)
+            
+            cmd = 'bader %s' % abscubefile
+            status,output = commands.getstatusoutput(cmd)
+            
+            if status != 0:
+                print output
+
+            shutil.copy2('ACF.dat',os.path.join(cwd,acf_file))
+            
+            os.chdir(cwd)
+            shutil.rmtree(tempdir)
+
+        self.charges = []
+        self.volumes = []
+
+        #now parse the output
+        f = open(acf_file,'r')
+        #skip 2 lines
+        f.readline()
+        f.readline()
+
+        for i,atom in enumerate(self.atoms):
+            line = f.readline()
+            fields = line.split()
+            n = int(fields[0])
+            x = float(fields[1])
+            y = float(fields[2])
+            z = float(fields[3])
+            chg = float(fields[4])
+            mindist = float(fields[5])
+            vol = float(fields[6])
+
+            self.charges.append(chg)
+            self.volumes.append(vol)
+
+        f.close()
+
+    def get_bader_charges(self):
+        return self.charges
+
+    def get_bader_volumes(self):
+        'return volumes in Ang**3'
+        return [x*Bohr**3 for x in self.volumes]
+
+    def write_atom_volume(self,atomlist):
+        '''write bader atom volumes to cube files.
+        atomlist = [0,2] #for example
+
+        -p sel_atom Write the selected atomic volumes, read from the
+        subsequent list of volumes.
+        '''
+        alist = string.join([str(x) for x in atomlist],' ')
+        cmd = 'bader -p sel_atom %s %s' % (alist,self.densityfile)
+        print cmd
+        os.system(cmd)
+        
+    def write_bader_volume(self,atomlist):
+        """write bader atom volumes to cube files.
+
+        ::
+        
+          atomlist = [0,2] #  for example
+          
+        -p sel_bader Write the selected Bader volumes, read from the
+        subsequent list of volumes.
+        """
+        alist = string.join([str(x) for x in atomlist],' ')
+        cmd = 'bader -p sel_bader %s %s' % (alist,self.densityfile)
+        print cmd
+        os.system(cmd)
+
+    def write_atom_index(self):
+        ''' -p atom_index Write the atomic volume index to a charge
+        density file.
+        '''
+        cmd = 'bader -p atom_index %s' % (self.densityfile)
+        print cmd
+        os.system(cmd)
+
+    def write_bader_index(self):
+        '''
+        -p bader_index Write the Bader volume index to a charge
+        density file.
+        '''
+        cmd = 'bader -p bader_index %s' % (self.densityfile)
+        print cmd
+        os.system(cmd)
+
+    def write_all_atom(self):
+        '''
+        -p all_atom Combine all volumes associated with an atom and
+        write to file. This is done for all atoms and written to files
+        named BvAtxxxx.dat. The volumes associated with atoms are
+        those for which the maximum in charge density within the
+        volume is closest to the atom.
+        '''
+        cmd = 'bader -p all_atom %s' % (self.densityfile)
+        print cmd
+        os.system(cmd)
+
+    def write_all_bader(self):
+        '''
+        -p all_bader Write all Bader volumes (containing charge above
+        threshold of 0.0001) to a file. The charge distribution in
+        each volume is written to a separate file, named
+        Bvolxxxx.dat. It will either be of a CHGCAR format or a CUBE
+        file format, depending on the format of the initial charge
+        density file. These files can be quite large, so this option
+        should be used with caution.
+        '''
+        cmd = 'bader -p all_bader %s' % (self.densityfile)
+        print cmd
+        os.system(cmd)
+        
+if __name__ == '__main__':
+
+    from Jacapo import *
+
+    atoms = Jacapo.read_atoms('ethylene.nc')
+
+    b = Bader(atoms)
+
+    print b.get_bader_charges()
+    print b.get_bader_volumes()
+    b.write_atom_volume([3,4])
diff --git a/ase/calculators/jacapo/utils/bandstructure.py b/ase/calculators/jacapo/utils/bandstructure.py
new file mode 100644
index 0000000..78f5f20
--- /dev/null
+++ b/ase/calculators/jacapo/utils/bandstructure.py
@@ -0,0 +1,204 @@
+import os
+import numpy as np
+import matplotlib.pyplot as plt
+from ase.calculators.jacapo import *
+from ase.dft.dos import DOS
+
+class BandStructure:
+    '''outline of class to facilitate band structure calculations
+    '''
+    def __init__(self,
+                 atoms,
+                 BZpath=[],
+                 npoints=10,
+                 outnc='harris.nc'):
+        """Headline here ... XXX.
+        
+        atoms is an ase.Atoms object with calculator
+        attached. Presumably the self-consistent charge density has
+        already been calculated, otherwise, it will be.
+
+        BZpath is a list of tuples describing the path through the
+        Brillouin zone. The tuples have the form (label, kpt), e.g. ::
+
+          [('$\Gamma$',[0.0, 0.0, 0.0]),
+           ('X',[0.0, 0.5, 0.5]),
+           ('L',[0.5, 0.0, 0.0]),
+           ('$\Gamma$',[0.0, 0.0, 0.0])]
+
+        the label is used in the figure and can include latex markup.
+
+        npoints is the number of points on each segment. It can either
+        be a constant, which is used for every segment, or a list of
+        integers that is an integer for each segment.        
+        """
+
+        self.atoms = atoms
+        self.calc = atoms.get_calculator()
+        #first, we make sure the charge density is up to date.
+        self.calc.get_charge_density()
+        self.ef = self.calc.get_ef() #self-consistent fermi level
+
+        self.labels = [x[0] for x in BZpath]
+        self.kpt_path = [np.array(x[1],dtype=np.float) for x in BZpath]
+        self.npoints = npoints
+
+        #first, setup the kpt path
+        kpts = []
+        #start at second kpt and go to second to last segment
+        nsegments = len(self.kpt_path) - 1
+        for i in range(nsegments-1):
+            
+            #get number of points on path. this counts the first point
+            try:
+                i_npt = npoints[i]
+            except TypeError:
+                i_npt = npoints
+
+            #this is the vector connecting the two endpoint kpts of a segment
+            kdiff = self.kpt_path[i+1] - self.kpt_path[i]
+
+            #make a vector of evenly spaced intervals, one longer than needed
+            #because we chop off the last entry.
+            for j in np.linspace(0,1,i_npt+1)[0:-1]:
+                k = self.kpt_path[i] + j*kdiff
+                #shift by small random amount to break symmetry and
+                #prevent time-inversion reduction
+                krand = (1. + np.random.random(3))/1.e4
+                
+                k += krand
+                kpts.append(k)
+
+        #now fill in the last segment, and end on the last point
+        try:
+            i_npt = npoints[-1]
+        except TypeError:
+            i_npt = npoints
+
+        kdiff = self.kpt_path[-1] - self.kpt_path[-2]
+        for j in np.linspace(0,1,i_npt+1)[1:]:
+            k = self.kpt_path[-2] + j*kdiff
+            #shift by small random amount to break symmetry and
+            #prevent time-inversion reduction
+            krand = (1. + np.random.random(3))/1.e4
+            k += krand
+            kpts.append(k)
+
+        #these are now the points needed for the Harris calculation.
+        self.kpts = kpts
+
+        self.dos = DOS(self.calc)
+        self.dos_energies = self.dos.get_energies()
+        self.dos_dos = self.dos.get_dos()
+
+        #try to avoid rerunning the calculation if it is already done!
+        if os.path.exists(outnc):
+            self.calc = Jacapo(outnc)
+        else:
+            print 'calculation of harris required'
+            self.calc.set_nc(outnc)
+            #self.calc.debug=10
+
+            #save some time by not calculating stress
+            self.calc.set_stress(False)
+                        
+            #this seems to be necessary sometimes
+            self.calc.delete_ncattdimvar(outnc,
+                                         ncdims=['number_plane_waves'])
+
+            #this has to come after removing number_of_planewaves
+            self.calc.set_kpts(self.kpts)
+            
+            #freeze charge density
+            self.calc.set_charge_mixing(updatecharge='No')
+            #and, run calculation
+            self.calc.calculate()
+
+        
+
+    def plot(self):
+        '''
+        Make an interactive band-structure plot.
+
+        clicking on a band will make it thicker and print which band was selected.
+        '''
+
+        kpoints = self.calc.get_ibz_kpoints()
+        
+        eigenvalues = self.calc.get_all_eigenvalues() - self.ef
+        #eigenvalues = np.array([self.calc.get_eigenvalues(kpt=i)-self.ef
+        #                        for i in range(len(kpoints))])
+        
+        self.handles = [] #used to get band indexes from plot
+
+        fig = plt.figure()
+        #plot DOS in figure
+        ax = fig.add_subplot(122)
+        ax.plot(self.dos_dos,self.dos_energies)
+        plt.title('self-consistent Total DOS')
+        ax.set_xticks([])
+        ax.set_yticks([])
+        ax.set_ylim([-20,20])
+        
+        ax = fig.add_subplot(121)
+        ax.set_title('Band structure')
+
+        def onpick(event):
+            'make picked line bolder, set oldline back to regular thickness'
+            self.lastartist.set_linewidth(1)
+            self.lastartist = thisline = event.artist
+            thisline.set_linewidth(5)
+            plt.draw() #needed to update linewidth
+            
+            print 'Band %i selected' % self.handles.index(thisline)
+            #you could insert code here to plot wavefunction, etc...
+            
+        fig.canvas.mpl_connect('pick_event',onpick)
+
+        #we use indices for x. the tick labels are not shown and the distance
+        #appears unimportant
+        xdata = range(len(eigenvalues))
+
+        nkpts, nbands = eigenvalues.shape
+        for i in range(nbands):         
+            #eigenvalues has shape(nkpts,nbands)
+            #note the comma after line_handle
+            line_handle, = ax.plot(xdata,eigenvalues[:,i],'.-',ms=1,picker=2)
+            self.handles.append(line_handle)
+
+        self.lastartist = self.handles[-1]
+            
+        #plot Fermi level
+        ax.plot([0,len(self.kpts)],[0,0],'k--',label='$E_f$')
+        
+        plt.xlabel('|k|')
+        plt.ylabel('$E-E_f$ (eV)')
+
+        #set xtick locations and labels
+        xtick_locs = np.zeros(len(self.kpt_path))
+        try:
+            #this means the npoints is a list
+            i_npt = self.npoints[0]
+            for j,npt in enumerate(1,self.npoints):
+                xtick_locs[j] = xtick_locs[j-1] + npt
+        except TypeError:
+            #npoints is a single number
+            for j in range(1,len(self.labels)):
+                xtick_locs[j] = xtick_locs[j-1] + self.npoints
+
+        #the last location is off by one, so we fix it.
+        xtick_locs[-1] -= 1
+
+        ax.set_xlim([xtick_locs[0],xtick_locs[-1]])        
+        ax.set_xticks(xtick_locs)
+        ax.set_xticklabels(self.labels)
+        
+        #this seems reasonable to avoid very deep energy states and high energy states
+        ax.set_ylim([-20,20])
+        
+        plt.show()
+
+        return fig
+
+ 
+                              
diff --git a/ase/calculators/jacapo/utils/bee.py b/ase/calculators/jacapo/utils/bee.py
new file mode 100644
index 0000000..1ac5efc
--- /dev/null
+++ b/ase/calculators/jacapo/utils/bee.py
@@ -0,0 +1,49 @@
+'''
+adapted from ASE/Utilities/BEE.py
+'''
+
+from math import sqrt
+import numpy as num
+import numpy.random as ra
+
+"""Bayesian Error Estimation
+
+For details, see: "Bayesian Error Estimation in Density Functional
+Theory", J. J. Mortensen, K. Kaasbjerg, S. L. Frederiksen,
+J. K. Norskov, J. P. Sethna, K. W. Jacobsen, Phys. Rev. Lett. 95,
+216401 (2005)."""
+
+#                                 T
+# cost(c) = cost0 + 0.5 * (c - c0) H (c - c0)
+#
+
+# Cost function minimum value:
+cost0 = 3.4660625596
+
+# Best fit parameters:
+c0 = num.array([1.000787451, 0.1926284063, 1.896191546])
+
+# Hessian:
+# H = num.array([[ 1.770035168e+03, -3.732470432e+02, -2.105836167e+02],
+#                [-3.732470432e+02,  1.188857209e+02,  6.054102443e+01],
+#                [-2.105836167e+02,  6.054102443e+01,  3.211200293e+01]])
+#
+# 0.5 * np * T = cost0 (np=3: number of parameters)
+T = cost0 * 2 / 3
+
+def MakeEnsemble(N=1000, seed=117):
+    ra.seed(seed)
+    M = num.array([(0.066, -0.812, 1.996),
+                   (0.055, 0.206, 0.082),
+                   (-0.034, 0.007, 0.004)])
+    alpha = ra.normal(0.0, 1.0, (N, 3))
+    return c0 + num.dot(alpha, M)
+
+c = MakeEnsemble()
+
+def GetEnsembleEnergies(atoms, c=c):
+    if hasattr(atoms, 'get_calculator'):
+        coefs = atoms.get_calculator().get_ensemble_coefficients()
+    else:
+        coefs = atoms
+    return coefs[0] + num.dot(c, coefs[1:])
diff --git a/ase/calculators/jacapo/utils/findsym.py b/ase/calculators/jacapo/utils/findsym.py
new file mode 100755
index 0000000..b817256
--- /dev/null
+++ b/ase/calculators/jacapo/utils/findsym.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+'''
+isotropy 
+http://stokes.byu.edu/isolinux.html
+
+http://stokes.byu.edu/iso.tar.gz
+
+You will need to create a directory to unzip the above tarfile in,
+
+cd
+mkdir iso
+cd iso
+wget http://stokes.byu.edu/iso.tar.gz
+tar xvzf  iso.tar.gz
+
+#put this in your .cshrc
+setenv ISODATA $HOME/iso/
+set path=($HOME/iso $path)
+
+'''
+
+import math,os,re,string
+
+from Scientific.Geometry import Vector
+
+class FINDSYM:
+    def __init__(self,atoms,outfile=None):
+        
+        unitcell = atoms.get_cell()
+        A = Vector(unitcell[0])
+        B = Vector(unitcell[1])
+        C = Vector(unitcell[2])
+
+        # lengths of the vectors
+        a = A.length()#*angstroms2bohr
+        b = B.length()#*angstroms2bohr
+        c = C.length()#*angstroms2bohr
+
+        # angles between the vectors
+        rad2deg = 360./(2.*math.pi)
+        alpha = B.angle(C)*rad2deg
+        beta = A.angle(C)*rad2deg
+        gamma = A.angle(B)*rad2deg
+
+        scaledpositions = atoms.get_scaled_positions()
+        chemicalsymbols = [atom.get_symbol() for atom in atoms]
+
+        input = ''
+
+        input += 'title \n'
+        input += '0    tolerance\n'
+        input += '2        lattice parameters in lengths and angles\n'
+        input += '%1.3f %1.3f %1.3f %1.3f %1.3f %1.3f\n' % (a,b,c,
+                                                            alpha,beta,gamma)
+        input += '1  3 basis vectors for unit cell\n'
+
+        input += '1.00 0.00 0.00\n'
+        input += '0.00 1.00 0.00\n'
+        input += '0.00 0.00 1.00\n'
+
+        input += '%i   number of atoms\n' % len(atoms)
+
+        types = ''
+        for atom in atoms:
+            types += str(atom.get_atomic_number()) + ' '
+
+        input += types + '\n'
+
+        for i,atom in enumerate(atoms):
+            input += '%1.3f %1.3f %1.3f\n' % tuple(scaledpositions[i])
+
+        pin,pout = os.popen2('findsym')
+        pin.writelines(input)
+        pin.close()
+        self.output = pout.readlines()
+        pout.close()
+
+        if outfile:
+            f = open(outfile,'w')
+            f.writelines(self.output)
+            f.close()
+
+        if os.path.exists('findsym.log'):
+            os.remove('findsym.log')
+
+    def __str__(self):
+        return string.join(self.output)
+
+    def get_space_group(self):
+        regexp = re.compile('^Space Group')
+
+        for line in self.output:
+            if regexp.search(line):
+                return line
+
+
+if __name__ == '__main__':
+    from ase.calculators.jacapo import *
+    from optparse import OptionParser
+
+    parser = OptionParser(usage='findsym.py ncfile',
+                      version='0.1')
+
+    parser.add_option('-f',
+                      nargs=0,
+                      help = 'print full output')
+
+    parser.add_option('-o',
+                      nargs=1,
+                      help = 'save output in filename')
+
+    options,args = parser.parse_args()
+    
+    for ncfile in args:       
+
+        sg = FINDSYM(Jacapo.read_atoms(ncfile),outfile=options.o)
+
+        print sg.get_space_group()
+    
+        if options.f is not None:
+            print sg
diff --git a/ase/calculators/jacapo/utils/isotropy.tar.gz b/ase/calculators/jacapo/utils/isotropy.tar.gz
new file mode 100644
index 0000000..332b7dc
Binary files /dev/null and b/ase/calculators/jacapo/utils/isotropy.tar.gz differ
diff --git a/ase/calculators/jacapo/utils/jmath.py b/ase/calculators/jacapo/utils/jmath.py
new file mode 100644
index 0000000..94aa753
--- /dev/null
+++ b/ase/calculators/jacapo/utils/jmath.py
@@ -0,0 +1,68 @@
+'''
+mathematical utilities
+'''
+import numpy as np
+import bisect
+
+def vinterp3d(x,y,z,u,xi,yi,zi):
+
+    p = np.array([xi,yi,zi])
+
+    #1D arrays of cooridinates
+    xv = x[:,0,0]
+    yv = y[0,:,0]
+    zv = z[0,0,:]
+
+    # we subtract 1 because bisect tells us where to insert the
+    # element to maintain an ordered list, so we want the index to the
+    # left of that point
+    i = bisect.bisect_right(xv,xi) - 1
+    j = bisect.bisect_right(yv,yi) - 1
+    k = bisect.bisect_right(zv,zi) - 1
+
+    #occasionally we get the edge of the cell and we then go one index
+    #back again
+    if i == len(xv)-1: i-=1
+    if j == len(yv)-1: j-=1
+    if k == len(zv)-1: k-=1
+    
+    
+    #points at edge of cell. We only need P1, P2, P3, and P5
+    P1 = np.array([x[i,j,k],y[i,j,k],z[i,j,k]])
+    P2 = np.array([x[i+1,j,k],y[i+1,j,k],z[i+1,j,k]])
+    P3 = np.array([x[i,j+1,k],y[i,j+1,k],z[i,j+1,k]])
+    P5 = np.array([x[i,j,k+1],y[i,j,k+1],z[i,j,k+1]])
+
+    #values of u at edge of cell
+    u1 = u[i,j,k]
+    u2 = u[i+1,j,k]
+    u3 = u[i,j+1,k]
+    u4 = u[i+1,j+1,k]
+    u5 = u[i,j,k+1]
+    u6 = u[i+1,j,k+1]
+    u7 = u[i,j+1,k+1]
+    u8 = u[i+1,j+1,k+1]
+
+    #cell basis vectors, not the unit cell, but the voxel cell containing the point
+    cbasis = np.array([P2-P1,
+                       P3-P1,
+                       P5-P1])
+
+    #now get interpolated point in terms of the cell basis
+    s = np.dot(np.linalg.inv(cbasis.T),np.array([xi,yi,zi])-P1)
+
+    #now s = (sa, sb, sc) which are fractional coordinates in the vector space
+    #next we do the interpolations
+    ui1 = u1 + s[0]*(u2-u1)
+    ui2 = u3 + s[0]*(u4-u3)
+
+    ui3 = u5 + s[0]*(u6-u5)
+    ui4 = u7 + s[0]*(u8-u7)
+
+    ui5 = ui1 + s[1]*(ui2-ui1)
+    ui6 = ui3 + s[1]*(ui4-ui3)
+
+    ui7 = ui5 + s[2]*(ui6-ui5)
+
+    return ui7
+
diff --git a/ase/calculators/jacapo/utils/sgroup.py b/ase/calculators/jacapo/utils/sgroup.py
new file mode 100755
index 0000000..90f05ba
--- /dev/null
+++ b/ase/calculators/jacapo/utils/sgroup.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python
+
+'''
+Get the space group for a ListOfAtoms
+
+can be called as a script on a netcdf file
+
+sgroup.py [-f] ncfile
+
+http://cpc.cs.qub.ac.uk/summaries/ADON.html
+
+PROGRAM SUMMARY [Licence| Download | E-mail] adon.tar.gz(69 Kbytes) 
+Manuscript Title: Determination of the space group and unit cell for a periodic solid. 
+Authors: B.Z. Yanchitsky, A.N. Timoshevskii 
+Program title: SGROUP 
+Catalogue identifier: ADON 
+Journal reference: Comput. Phys. Commun. 139(2001)235 
+Programming language: C. 
+Computer: Intel/Pentium, Alpha Workstation. 
+Operating system: Slackware Linux 4.0, Digitial Unix 4.0D. 
+RAM: 1M words 
+Word size: 8 
+Keywords: Unit cell, Space group, Symmetry operations, Solid state physics, Crystal structure. 
+Classification: 7.8. 
+
+'''
+import math,os,re,string,tempfile
+
+from Scientific.Geometry import *
+from Scientific.IO.FortranFormat import *
+from numpy import *
+
+class SGROUP:
+
+    def __init__(self,atoms,outfile=None):
+        '''outfile is where the results will be stored if you want
+        them. Otherwise they go into a tempfile that is deleted.'''
+
+        id,infile = tempfile.mkstemp()
+        
+        if outfile is None:
+            od,ofile = tempfile.mkstemp()
+        else:
+            ofile = outfile
+
+        unitcell = atoms.get_cell()
+
+        A = Vector(unitcell[0])
+        B = Vector(unitcell[1])
+        C = Vector(unitcell[2])
+
+        # lengths of the vectors
+        a = A.length()#*angstroms2bohr
+        b = B.length()#*angstroms2bohr
+        c = C.length()#*angstroms2bohr
+
+        # angles between the vectors
+        rad2deg = 360./(2.*math.pi)
+        alpha = B.angle(C)*rad2deg
+        beta = A.angle(C)*rad2deg
+        gamma = A.angle(B)*rad2deg
+
+        scaledpositions = atoms.get_scaled_positions()
+        chemicalsymbols = [atom.get_symbol() for atom in atoms]
+
+        f = open(infile,'w')
+        f.write('P\n')
+
+        f.write('%1.4f %1.4f %1.4f %1.4f %1.4f %1.4f\n' % (a,b,c,
+                                                           alpha,beta,gamma))
+
+        f.write('%i\n' % len(atoms))
+        
+        for i,atom in enumerate(atoms):
+            f.write('%1.4f %1.4f %1.4f\n' % tuple(scaledpositions[i]))
+            f.write('%s\n\n' % chemicalsymbols[i])
+        f.close()
+
+        os.system('sgroup %s %s' % (infile,ofile))
+          
+        f = open(ofile,'r')
+        self.output= f.readlines()
+        f.close()
+
+        os.unlink(infile)
+        os.close(id)
+        
+        if outfile is None:
+            os.unlink(ofile)
+            os.close(od) # you must close the file descriptor or
+                         # eventually too many open files will occur
+                         # and cause an error when you are processing
+                         # many files.
+
+            
+    def __str__(self):
+        return string.join(self.output)
+        
+    def get_space_group(self):
+        'returns spacegroup number'
+        regexp = re.compile('^Number and name of space group:')
+        for line in self.output:
+            if regexp.search(line):
+                line = line[32:]    
+                r2 = re.compile('^\d+')
+                s = r2.search(line)
+                if hasattr(s,'group'):
+                    return int(s.group())
+                else:
+                    return None
+
+    def get_symmetry_operators(self):
+        '''
+        gets symmetry operators from output
+
+        it looks like this in the output.
+        I am not sure what the 4th number is, it is called
+        tau in Wien2k. I do not use it or return it here, but
+        it is parsed, and could be returned.
+        
+        Number of symmetry operations: 48
+        Operation: 1
+        1.0   0.0   0.0  0.000
+        0.0   1.0   0.0  0.000
+        0.0   0.0   1.0  0.000
+
+        Operation: 2
+        -1.0   0.0   0.0  0.000
+        0.0  -1.0   0.0  0.000
+        0.0   0.0   1.0  0.000
+
+        Operation: 3
+        '''
+        
+        re1 = '^Number of symmetry operations:'
+        regexp = re.compile(re1)
+        for i,line in enumerate(self.output):
+            if regexp.search(line):
+                # take integer after the colon
+                nsymops = int (string.split(line,':')[-1])
+                index = i
+                break
+
+        symmetry_operators = []
+        taus = []
+        for s in range(nsymops):
+            temparray=zeros((3,3))
+            temptau = [0,0,0]
+            if int(string.split(self.output[index+1],':')[-1]) != s+1:
+                raise Exception,'this symmetry operator %i does not match index' % s
+                               
+            x,y,z,tau = [float(var) for var in string.split(self.output[index+2])]
+            temparray[0] = [x,y,z]
+            temptau[0] = tau
+            
+            x,y,z,tau = [float(var) for var in string.split(self.output[index+3])]
+            temparray[1] = [x,y,z]
+            temptau[1] = tau
+            
+            x,y,z,tau = [float(var) for var in string.split(self.output[index+4])]
+            temparray[2] = [x,y,z]
+            temptau[2] = tau
+
+            # increase index for next operator
+            index += 5
+
+            symmetry_operators.append(temparray)
+            taus.append(array(temptau))
+
+        return symmetry_operators,taus
+            
+
+
+
+if __name__ == '__main__':
+    from ase.calculators.jacapo import *
+    from optparse import OptionParser
+
+    parser = OptionParser(usage='sgroup.py ncfile',
+                      version='0.1')
+
+    parser.add_option('-f',
+                      nargs=0,
+                      help = 'print full output')
+
+    parser.add_option('-o',
+                      nargs=1,
+                      help = 'save output in filename')
+
+    options,args = parser.parse_args()
+
+    #print options
+    
+    for ncfile in args:       
+
+        sg = SGROUP(Jacapo.read_atoms(ncfile),outfile=options.o)
+
+        print sg.get_space_group()
+    
+        if options.f is not None:
+            print sg
+
diff --git a/ase/calculators/jacapo/utils/sgroup.tar.gz b/ase/calculators/jacapo/utils/sgroup.tar.gz
new file mode 100644
index 0000000..e73f700
Binary files /dev/null and b/ase/calculators/jacapo/utils/sgroup.tar.gz differ
diff --git a/ase/calculators/jacapo/utils/symmol.out b/ase/calculators/jacapo/utils/symmol.out
new file mode 100644
index 0000000..5898348
--- /dev/null
+++ b/ase/calculators/jacapo/utils/symmol.out
@@ -0,0 +1,7 @@
+                   ============
+                      SYMMOL
+  A PROGRAM FOR THE SYMMETRIZATION OF GROUPS OF ATOMS
+        By Tullio Pilati and Alessandra Forni
+                Version November 4th 2002
+  ===================================================
+
diff --git a/ase/calculators/jacapo/utils/symmol.py b/ase/calculators/jacapo/utils/symmol.py
new file mode 100644
index 0000000..c52dbae
--- /dev/null
+++ b/ase/calculators/jacapo/utils/symmol.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+'''
+interface to symmol
+
+
+unzip symmol.tar.tz
+compile symmol if necessary:
+g77 -o symmol symmol.f
+
+make sure symmol is on your executable path
+'''
+
+import math,os,re,string
+
+from Scientific.Geometry import Vector
+from Scientific.IO.FortranFormat import *
+
+class SYMMOL:
+    def __init__(self,atoms,outfile=None):
+        
+        unitcell = atoms.get_cell()
+        A = Vector(unitcell[0])
+        B = Vector(unitcell[1])
+        C = Vector(unitcell[2])
+
+        # lengths of the vectors
+        a = A.length()#*angstroms2bohr
+        b = B.length()#*angstroms2bohr
+        c = C.length()#*angstroms2bohr
+
+        # angles between the vectors
+        rad2deg = 360./(2.*math.pi)
+        alpha = B.angle(C)*rad2deg
+        beta = A.angle(C)*rad2deg
+        gamma = A.angle(B)*rad2deg
+
+        scaledpositions = atoms.get_scaled_positions()
+        chemicalsymbols = [atom.get_symbol() for atom in atoms]
+
+        input = ''
+        input += '%1.3f %1.3f %1.3f %1.3f %1.3f %1.3f\n' % (a,b,c,
+                                                            alpha,beta,gamma)
+        input += '1 1 0.1 0.1\n' 
+
+        for atom in atoms:
+            sym = atom.get_symbol()
+            group = 1
+            x,y,z = atom.get_position()
+            #format(a6,i2,6f9.5)
+            input += str(FortranLine((sym,
+                                      group,
+                                      x,y,z),
+                                     FortranFormat('a6,i2,3f9.5')))+'\n'
+                    
+        pin,pout = os.popen2('symmol')
+        pin.writelines(input)
+        pin.close()
+        self.output = pout.readlines()
+        pout.close()
+
+        if outfile:
+            f = open(outfile,'w')
+            f.writelines(self.output)
+            f.close()
+
+        if os.path.exists('symmol.log'):
+            os.remove('symmol.log')
+
+    def __str__(self):
+        return string.join(self.output)
+
+    def get_point_group(self):
+        regexp = re.compile('^ Schoenflies symbol')
+
+        for line in self.output:
+            if regexp.search(line):
+                return line
+
+    def get_moments_of_inertia(self):
+        regexp = re.compile('^ PRINCIPAL INERTIA MOMENTS and DEGENERATION DEGREE')
+        lines = open('symmol.out').readlines()
+        for i,line in enumerate(lines):
+            
+            if regexp.search(line):
+                data = lines[i+1]
+                break
+        [Ia, Ib, Ic, degen] =  data.split()
+        return [float(Ia), float(Ib), float(Ic), int(degen)]
+
+    def get_symmetry_operators(self):
+        regexp = re.compile(' SYMMETRY GROUP MATRICES')
+        reg2 =  re.compile('^  \d+ CSM')
+        
+        lines = open('symmol.out').readlines()
+
+        matrices = []
+        types = []
+
+        for i,line in enumerate(lines):
+            if reg2.search(line):
+                fields = line.split()
+                types.append(fields[-1]) #type is at the end
+
+                row1 = [float(x) for x in lines[i+1].split()]
+                row2 = [float(x) for x in lines[i+2].split()]
+                row3 = [float(x) for x in lines[i+3].split()]
+
+                matrices.append(np.array([row1, row2, row3]))
+
+        return (matrices, types)
+                
+        
+
+if __name__ == '__test__':
+
+    from ase import Atoms
+    from ase.data import molecules
+    
+    mol = 'NH3'
+    atoms = Atoms(mol,
+                positions = molecules.data[mol]['positions'])
+    
+    sg = SYMMOL(atoms)
+    print sg.get_point_group()
+    print sg.get_moments_of_inertia()
+    print atoms.get_moments_of_inertia()
+    print sg.get_symmetry_operators()
+
+if __name__ == '__main__':
+    from ase.calculators.jacapo import *
+    from optparse import OptionParser
+
+    parser = OptionParser(usage='symmol.py ncfile',
+                      version='0.1')
+
+    parser.add_option('-f',
+                      nargs=0,
+                      help = 'print full output')
+
+    parser.add_option('-o',
+                      nargs=1,
+                      help = 'save output in filename')
+
+    options,args = parser.parse_args()
+    
+    for ncfile in args:       
+
+        sy = SYMMOL(Jacapo.read_atoms(ncfile),outfile=options.o)
+
+        print 'Point group = ',sy.get_point_group()
+        print 'Moments of inertia = ',sy.get_moments_of_inertia()
+        print 'Symmetry operators = ',sy.get_symmetry_operators()
+        if options.f is not None:
+            print sy
+
+ 
diff --git a/ase/calculators/jacapo/utils/symmol.tar.gz b/ase/calculators/jacapo/utils/symmol.tar.gz
new file mode 100644
index 0000000..10edeb1
Binary files /dev/null and b/ase/calculators/jacapo/utils/symmol.tar.gz differ
diff --git a/ase/calculators/jacapo/utils/wannier.py b/ase/calculators/jacapo/utils/wannier.py
new file mode 100644
index 0000000..1a48228
--- /dev/null
+++ b/ase/calculators/jacapo/utils/wannier.py
@@ -0,0 +1,617 @@
+""" class Wannier provides the function get_wannier_localization_matrix and initial_wannier.
+"""
+
+import numpy as np
+from Scientific.IO.NetCDF import NetCDFFile as netCDF
+
+def dagger(matrix,copy=1):
+        # First change the axis: (Does not allocate a new array)
+        matrix_conj=np.swapaxes(matrix,0,1)
+        if copy: # Allocate space for new array
+                return np.conjugate(matrix_conj)
+        else:    # The array of matrix is used for output
+                np.dot(matrix_conj.imag,-1,matrix_conj.imag)
+                return matrix_conj
+
+def project(a,b):
+    """ returns the projection of b onto a"""
+    return a*(np.dot(np.conjugate(a),b)/np.dot(np.conjugate(a),a))
+
+def translate(array,translation):
+        """Method for translating an array"""
+        newarray=array
+        size=array.shape
+        for dim in range(len(translation)):
+            axis=dim-len(translation)
+            newarray=np.concatenate((np.take(newarray,range(translation[dim],size[axis]),axis),np.take(newarray,range(translation[dim]),axis)),axis)
+        return newarray.copy()
+
+def cartesian2scaled(basis,cart):
+    return np.linalg.solve(np.transpose(basis),cart)
+
+def scaled2cartesian(basis,scaled):
+    return np.dot(scaled,basis)
+
+class Translation_Operator:
+    def __init__(self,dimensions,basis):
+        self.set_dimensions(dimensions)
+        self.set_basis(basis)
+
+    def set_dimensions(self,dim):
+        self.dimensions = dim
+
+    def get_dimensions(self):
+        return self.dimensions
+    
+    def set_basis(self,basis):
+        self.basis = basis
+
+    def get_basis(self):
+        return self.basis
+
+    def set_cartesian_translation_coordinates(self,trans_cartes):
+        self.cartesian_translation_coordinates = trans_cartes
+
+    def get_cartesian_translation_coordinates(self):
+        return self.cartesian_translation_coordinates
+
+    def get_coordinates(self):
+        trans_cartes = self.get_cartesian_translation_coordinates()
+        #trans_coor = np.linalg.solve(np.transpose(self.get_basis()),trans_cartes) 
+        trans_coor = cartesian2scaled(self.get_basis(),trans_cartes)
+        return np.multiply(trans_coor,self.get_dimensions())
+
+    def get_translational_diagonal(self,dim_index):
+        # Using that G = 2*pi*n/L : epx(-i*G*x_{1}) =
+        #                pow(exp(-i*2*pi/N*x_{1}),n)
+
+        length = self.get_dimensions()[dim_index]
+        coordinates = self.get_coordinates()[dim_index]
+        basis_neg = np.arange(0,-length/2,-1)
+        basis_pos = np.arange(length/2,0,-1)
+        basis = np.concatenate((basis_neg,basis_pos),-1)
+        prefactor=np.exp(-complex(0,1)*2*np.pi*coordinates/length)
+        translation=map(lambda x,prefactor=prefactor:prefactor**x,basis)
+        return np.array(translation)
+
+    def operate(self,state):
+        translation = np.multiply.outer(self.get_translational_diagonal(0),self.get_translational_diagonal(1))
+        translation = np.multiply.outer(translation,self.get_translational_diagonal(2))
+        return np.multiply(translation,state)       
+
+def coordinate_array_from_unit_vectors(shape, gridunitvectors,
+                                       origin=[0, 0, 0], indexfunction=None):
+        """
+        This method can be used to obtain an array representing the coordinates
+        of a space defined by 'gridunitvecors'. 'gridunitvectors' is in turn a
+        list containing the vectors defining the cells of the grid, i.e. the
+        vectors between neighboring grid points. These vectors are spanned
+        according to the specified shape. 
+
+        'origin' -- specifies the origin of the returned coordinate array. 
+        
+        'indexfunction' -- is a lambda expression that defines the indices 
+        with which each of the specified gridunitvectors are to be multiplied. 
+        'indexfunction' must take two arguments, 'i' and 'length' - default
+        is 'lambda i,length:i'. During exection the input index 'i' will run 
+        over the interval 0,1,..., 'length' -1.
+
+        **An Example**
+
+        To obtain a coordinate array of shape (10,10) with 
+        'gridunitvectors' =[[2,0],[0,1]] and the origin at [10,0] use:
+
+        'CoordinateArrayFromUnitVectors((10,10),[[2,0],[0,1],[10,0])'
+
+        Note that the output array will be of shape 
+        (< *dimension* > ,  < *spatialcoordinates* >).
+        """
+        
+        if indexfunction is None:
+            indexfunction = lambda i, length: i
+        
+        coordinatelist=[]
+        gridunitvectors=np.asarray(gridunitvectors)
+        # Looping over the dimensionality of the vectors
+        for dim in range(gridunitvectors.shape[1]):
+                coordinates=origin[dim]
+                # Contribution from each unitvector
+                for nunitvector in range(gridunitvectors.shape[0]):
+                        # Finding the indices from which the coordinate grid
+                        # is spanned
+                        indices=map(lambda i,f=indexfunction,l=shape[nunitvector]:f(i,l),range(shape[nunitvector]))
+                        coordinatefunc=lambda i,v=gridunitvectors[nunitvector,dim]:i*v
+                        coordinates=np.add.outer(coordinates,map(coordinatefunc,indices))
+                coordinatelist.append(coordinates)
+        return np.array(coordinatelist)
+
+def coordinates_from_function_values(dimensions,functionvalues):
+        # In general: Using basis functions of the form:
+        # 1/sqrt(N1*N2*N3)*exp(iG_dot_r)
+        normalization=np.sqrt(np.multiply.reduce(dimensions))
+        # Functionvalues of the form (a,x_i,x_j,x_k), where
+        # a is different functions and x_i,x_j,x_k the function values
+        # Return array of the form (a,G_i,G_j,G_k)
+
+        # The three last axes are transformed
+        coordinates=np.fft.ifft(np.fft.ifft(np.fft.ifft(functionvalues,n=functionvalues.shape[-1],axis=-1),n=functionvalues.shape[-2],axis=-2),n=functionvalues.shape[-3],axis=-3)
+        # Scaling:
+        np.multiply(coordinates,normalization,coordinates)
+        return coordinates
+
+
+class Wannier:
+
+   def __init__(self,calc):
+
+      self.calc = calc
+      self.set_bands(None)
+      self.set_spin(None)
+      self.has_changed = True
+
+   def set_spin(self,spin): 
+      self.spin = spin
+      self.has_changed = True
+
+   def get_spin(self): 
+      return self.spin
+
+   def get_fft_index(self):
+      if not hasattr(self,'fftindex'):
+          fftindex = []
+          kpoints = self.calc.get_bz_k_points()
+          for kpt in range(len(kpoints)):
+              fftindex.append(self.calc.get_reciprocal_fft_index())
+          self.fftindex = fftindex
+      return self.fftindex
+
+   def get_grid_dimensions(self):
+      fftgrids = self.calc.get_fftgrid()
+      return fftgrids['soft']
+      
+   def get_list_of_wave_functions(self):
+      if self.get_spin() == None or self.get_bands() == None:
+          raise "Bands and spin must be set before wave function list can be created"
+
+      if self.has_changed:
+          listofwavefct = []
+          kpoints = self.calc.get_bz_k_points()
+          for kpt in range(len(kpoints)):
+              eigenstates = []
+              for band in range(self.get_bands()):
+                  eigenstates.append(self.calc.get_reciprocal_bloch_function(band=band,kpt=kpt,spin=self.get_spin()))
+
+              listofwavefct.append(eigenstates)
+          self.listofwavefct = listofwavefct
+          self.has_changed = False
+      return self.listofwavefct
+
+   def set_bands(self,numberofbands):	
+      self.numberofbands = numberofbands
+      self.has_changed = True
+      
+   def get_bands(self): 
+      return self.numberofbands
+
+   def get_zi_bloch_matrix(self,dirG,kpoint,nextkpoint,G_I):
+      """ calculate matrix of ZIi,j values
+      This matrix consist of 3 matrices each of dimension MxM, i.e. corresponding to the full space.
+      """
+
+      #print 'DacapoWannier: Initialize ZIBlochMatrix ..'
+      if self.get_bands() == None or self.get_spin() == None:
+          raise "Bands and spin must be set before wannier localization matrix can be calculated"
+     	
+      phi=np.swapaxes(np.array(self.get_list_of_wave_functions()[kpoint]),0,1)
+      # K1 and reciprocal lattice vector G_I  given kpoint K
+      # that fulfills the criteria : K1-K-K0+G1=0
+      list1,list2 = self.get_gg_list(kpoint,nextkpoint,G_I)
+				
+      a=np.take(phi,list1,axis=0)
+      a=np.swapaxes(a,0,1)
+      phi1 = np.swapaxes(np.array(self.get_list_of_wave_functions()[nextkpoint]),0,1)
+      b=np.take(phi1,list2,axis=0)
+
+      ziblochmatrix = np.dot(np.conjugate(a),b)
+      usziblochmatrix = self.get_ultra_soft_non_loc_matrix(dirG,kpoint,nextkpoint)
+      ziblochmatrix += usziblochmatrix
+
+      return ziblochmatrix
+
+
+   def get_gg_list(self,kpt1,kpt2,GI):
+       """ define list of (G,G+G1) defining the product
+       phi(kpt1,G)*phi(kpt2,G+G1),
+
+       GI is one of
+       [[1,0,0],[0,1,0],[0,0,1],[1,1,0],[1,0,1],[0,1,1]]
+			 
+       The layout of fourier components is 
+       1   2   3   4   5   6   7   8   ngx = 8 
+       0   1   2   3   4  -3  -2  -1	n*2pi/L	
+       """
+
+       numberplanewaves = len(self.get_list_of_wave_functions()[kpt1][0])
+       reciprocalindex = self.get_fft_index()[kpt1]
+
+       ngrids = self.get_grid_dimensions()
+			
+       # setup the mapping from the 3D FFT grid to the wavefuction list
+       map2 = self.get_index_map(kpt2) 
+		
+       gglist = []
+       # print "Generating plane wave index list for direction ",GI," kpt12 ",kpt1,kpt2
+       list1 = []
+       list2 = []
+	   
+       # find G,G+GI
+       for n in range(numberplanewaves):
+           index = reciprocalindex[:,n]
+	   index = index - 1
+	   # find G,G+GI
+	   for dir in range(3): 
+	       index[dir] += GI[dir]
+	       if index[dir]>=ngrids[dir]:
+	           # wrap around
+		   index[dir] = 0
+				
+           # now find the corresponding index into phi(kpt2)
+           n1 = map2[index[0],index[1],index[2]]
+
+           if n1>=0:
+              list1.append(n)
+              list2.append(n1)
+			
+       # print '  Number of elements in GG list ',len(list1)
+       return list1,list2
+
+		
+
+   def get_index_map(self,kpt):
+       """ generate mapping from 3D FFT grid to the wavefunction list
+		
+       A negative number is returned from map(g1,g2,g3) is the
+       grid point does not exists in the wavefunction list
+       """
+       ngrids = self.get_grid_dimensions()
+       map_to_wflist = np.zeros(ngrids,np.int)
+       map_to_wflist = map_to_wflist - 1
+
+       numberplanewaves = len(self.get_list_of_wave_functions()[kpt][0])
+       reciprocalindex = self.get_fft_index()[kpt]
+			
+       for n in range(numberplanewaves):
+           i0 = reciprocalindex[0][n]-1
+	   i1 = reciprocalindex[1][n]-1
+	   i2 = reciprocalindex[2][n]-1
+	   map_to_wflist[i0,i1,i2] = n
+
+       return map_to_wflist
+
+   def get_ultra_soft_non_loc_matrix(self,GI,kpt,kpt1):
+        """ calculate
+              a                            I        I              I
+             W    = sum(n,m,I) <psi  | beta  > <beta  | psi   > * q
+               i,j                  ik      m        n      jk1     mn
+ 
+             n,m : projectors
+             I   : atom no
+             a (nbands,nbands) matrix is returned.
+        """
+
+        nc = netCDF(self.calc.get_nc(),'r')
+        if not 'WannierAugFactor' in nc.variables:
+            nc.close()
+            return
+
+        # Read NLProjectorPsi, StructureFactor, WannierAugFactor
+        vnlprojpsi = nc.variables['NLProjectorPsi'][:]
+        nlprojpsi = np.zeros((np.shape(vnlprojpsi[:,:,:,:,:,0])),np.complex)
+        nlprojpsi.real = vnlprojpsi[:,:,:,:,:,0]
+        nlprojpsi.imag = vnlprojpsi[:,:,:,:,:,1]
+        nlprojpsi = np.swapaxes(nlprojpsi,3,4)
+
+        vstrfactor = nc.variables['StructureFactor'][:]
+        strfactor = np.zeros((np.shape(vstrfactor[:,:,0])),np.complex)
+        strfactor.real = vstrfactor[:,:,0]
+        strfactor.imag = vstrfactor[:,:,1]
+
+        vaugfactor = nc.variables['WannierAugFactor'][:]
+        augfactor = np.zeros((np.shape(vaugfactor[:,:,:,:,0])),np.complex)
+        augfactor.real = vaugfactor[:,:,:,:,0]
+        augfactor.imag = vaugfactor[:,:,:,:,1]
+        augfactor = np.swapaxes(augfactor,0,1)
+
+        nc.close()
+
+        lst=[[1,0,0],[0,1,0],[0,0,1],[1,1,0],[1,0,1],[0,1,1]]
+
+        # find direction corresponding to GI
+        dir = lst.index(GI.tolist()) 
+
+        natoms = nlprojpsi.shape[2]
+        nbands = self.get_bands()
+        matrix = np.zeros([nbands,nbands],np.complex)
+        for atom in range(natoms):
+
+            if kpt==kpt1:
+                q=np.conjugate(augfactor[:,:,atom,dir+1])
+            else:
+                q=augfactor[:,:,atom,0]
+
+                if dir<3:
+                    q=q*np.conjugate(strfactor[atom,dir])
+
+            A=nlprojpsi[kpt,self.get_spin(),atom,:nbands,:]
+            B=nlprojpsi[kpt1,self.get_spin(),atom,:nbands,:]
+            matrix=matrix+np.dot(A,np.dot(q,dagger(B)))
+        return matrix
+
+##### code to calculate initial wannier functions ######
+
+   def set_data(self,data):
+        self.data = data
+
+   def set_k_point_grid(self,kpointgrid):
+        self.kpointgrid = np.array(kpointgrid)
+
+   def get_k_point_grid(self):
+        return self.kpointgrid
+
+   def get_repeated_unit_cell(self):
+        basis=self.calc.get_atoms().get_cell()
+        return np.transpose(np.transpose(basis)*self.get_k_point_grid())
+
+   def get_repeated_grid_dimensions(self):
+        return self.get_grid_dimensions()*self.get_k_point_grid()
+
+   def get_detailed_data(self):
+        if not hasattr(self,'detaileddata'):
+            """ Coverts data on the form
+                [atom,l,(m),a], or [center,l,(m),a].
+                atom is a number in a bracket, example [2] for atom number 2
+                center is 3 numbers in a bracket, example [1,0.5,0.3] denoting SCALED coordinates of the center.
+                Here m is optional. If m is left out all m values are used."""
+            datalist=self.data
+            detaileddata=[]
+            atoms=self.calc.get_atoms()
+            for data in datalist:
+                if len(data[0])==1:
+                    r_c=atoms[data[0][0]].get_position()
+                elif len(data[0])==3:
+                    r_c = scaled2cartesian(atoms.get_cell(),data[0])
+                else:
+                    print "First element in initial data must be of the form [atom] or [c1,c2,c3], where the latter is scaled coordinates of the center"
+                if len(data)==4:
+                    # m is specified        
+                    detaileddata.append([r_c,data[1],data[2],data[3]])
+                else:
+                    # Orbitals with all allowed m values are produced
+                    for m in range(-data[1],data[1]+1):
+                        detaileddata.append([r_c,data[1],m,data[2]])
+            self.detaileddata = detaileddata
+
+        return self.detaileddata
+
+   def get_origin_index(self):
+
+        griddim = self.get_repeated_grid_dimensions()
+        originindex = map(lambda coor:int(coor),list(np.multiply(np.array([0.5,0.5,0.5]),griddim)))
+        return originindex
+
+   def get_cartesian_coordinates(self):
+
+        griddim = self.get_repeated_grid_dimensions()
+        basis = self.calc.get_atoms().get_cell()
+         
+        # Set origin to center of grid
+        originindex = self.get_origin_index()
+        # origincoord is in scaled coordinates:
+        origincoord=-np.array(np.array(originindex,np.float)/griddim)
+        origincart = scaled2cartesian(basis,origincoord)
+        gridunitvectors = np.array(map(lambda unitvector,shape:unitvector/shape,basis,griddim))
+        c = coordinate_array_from_unit_vectors(shape=griddim,
+                                           gridunitvectors=gridunitvectors,
+                                           origin=origincart)
+        return c
+
+   def get_normalized_coordinates(self):
+ 
+        if not hasattr(self,'normalized_coordinates'):
+            originindex = tuple(self.get_origin_index())
+            c = self.get_cartesian_coordinates()
+            dist = np.sqrt(c[0]**2+c[1]**2+c[2]**2)
+
+            # We define "normalized" coordinates. To avoid undeterminancy at origin we move
+            # to the point (1,1,1)*1e-8
+            c[0][originindex]=1.0e-8
+            c[1][originindex]=1.0e-8
+            c[2][originindex]=1.0e-8
+
+            dist[originindex]=np.sqrt(3)*1.0e-8
+            self.normalized_coordinates = c/dist
+
+        return self.normalized_coordinates
+
+   def get_distance_array_at_origin(self):
+
+        # Set origin to center of grid
+        originindex = self.get_origin_index()
+
+        c = self.get_cartesian_coordinates()
+        dist=np.sqrt(c[0]**2+c[1]**2+c[2]**2)
+        # Translate back to origin
+        dist=translate(dist,originindex)
+        return dist
+
+   def setup_m_matrix(self,listofeigenstates,bzkpoints):
+
+        if self.data == None or self.kpointgrid == None:
+            raise "Must set data, kpointgrid, spin before calculating M matrix"
+
+        fftindex = self.get_fft_index()
+
+        unitcell=self.get_repeated_unit_cell()
+        griddim = self.get_repeated_grid_dimensions()    
+        data = self.get_detailed_data()
+        nkpoints = len(bzkpoints)
+        nbands = len(listofeigenstates[0])
+        M = np.zeros([nkpoints,nbands,len(data)],np.complex)
+        orbital = np.zeros(griddim,np.complex)
+        dist = self.get_distance_array_at_origin() 
+
+        transop = Translation_Operator(griddim,unitcell)
+        rec_basis = 2*np.pi*np.linalg.inv(np.transpose(self.calc.get_atoms().get_cell()))
+        large_rec_basis = 2*np.pi*np.linalg.inv(np.transpose(unitcell))
+
+        for i in range(len(data)):
+            # Translate orbital
+            r_c=data[i][0]
+            l,m=data[i][1],data[i][2]
+            a=data[i][3]
+            orbital = self.get_cubic_harmonic_at_origin(l,m)*np.exp(-dist/a)
+            orbital_fft = coordinates_from_function_values(griddim,orbital)
+            transop.set_cartesian_translation_coordinates(r_c)
+	    orbital_fft = transop.operate(orbital_fft)
+            for kpt in range(nkpoints):
+                kpoint = bzkpoints[kpt]
+                kptnumber = cartesian2scaled(large_rec_basis,scaled2cartesian(rec_basis,kpoint))
+                kptnumber[0]=round(kptnumber[0])
+                kptnumber[1]=round(kptnumber[1])
+                kptnumber[2]=round(kptnumber[2])
+                kptnumber=kptnumber.astype(int)
+                u_k = self.extract_periodic_part_of_small_cell(orbital_fft,kptnumber)
+                compact_u_k = self.get_compact_fft_representation(u_k,fftindex[kpt],len(listofeigenstates[kpt][0]))
+                M[kpt,:,i] = np.dot(np.conjugate(np.array(listofeigenstates[kpt])),compact_u_k)
+
+        self.mmatrix = M
+
+   def get_cubic_harmonic_at_origin(self,l,m):
+       """ l=0,1,2. m=-l,...,l"""
+
+       griddim = self.get_repeated_grid_dimensions()
+       harmonic = np.zeros(griddim,np.complex)
+       originindex = self.get_origin_index()
+       nc = self.get_normalized_coordinates()
+
+       # Constructing cubic harmonic
+       if l==0 and m==0:
+           harmonic=(1/np.sqrt(4*np.pi))*np.ones(nc[0].shape,np.Complex)
+           harmonic=translate(harmonic,originindex)
+       if l==1 and m==0:
+           # p_x
+           harmonic=np.sqrt(3/(4*np.pi))*nc[0]
+           harmonic=translate(harmonic,originindex)
+       if l==1 and m==-1:
+           # p_z
+           harmonic=np.sqrt(3/(4*np.pi))*nc[2]
+           harmonic=translate(harmonic,originindex)
+       if l==1 and m==1:
+           # p_y
+           harmonic=np.sqrt(3/(4*np.pi))*nc[1]
+           harmonic=translate(harmonic,originindex)
+       if l==2 and m==0:
+           harmonic=0.5*np.sqrt(5/(4*np.pi))*(3*(nc[0]**2)-np.ones(nc[0].shape,np.Complex))
+           harmonic=translate(harmonic,originindex)
+       if l==2 and m==-1:
+           harmonic=np.sqrt(15/(16*np.pi))*(nc[2]**2-nc[1]**2)
+           harmonic=translate(harmonic,originindex)
+       if l==2 and m==1:
+           harmonic=np.sqrt(15/(4*np.pi))*nc[0]*nc[1]
+           harmonic=translate(harmonic,originindex)
+       if l==2 and m==-2:
+           harmonic=np.sqrt(15/(4*np.pi))*nc[2]*nc[1]
+           harmonic=translate(harmonic,originindex)
+       if l==2 and m==2:
+           harmonic=np.sqrt(15/(4*np.pi))*nc[2]*nc[0]
+           harmonic=translate(harmonic,originindex)
+       return harmonic
+
+   def extract_periodic_part_of_small_cell(self,f,k):
+       n1,n2,n3=self.get_k_point_grid()
+       trans=[0,0,0]
+       if k[0]<0:
+           k[0]+=n1
+           trans[0]=self.get_grid_dimensions()[0]-1
+       if k[1]<0:
+           k[1]+=n2
+           trans[1]=self.get_grid_dimensions()[1]-1
+       if k[2]<0:
+           k[2]+=n3
+           trans[2]=self.get_grid_dimensions()[2]-1
+
+       u=f[k[0]::n1,k[1]::n2,k[2]::n3].copy()
+       return translate(u,trans)
+
+   def get_compact_fft_representation(self,freciprocal,fftindex,numberofpws):
+       wflist=np.zeros([numberofpws],np.complex)
+       for i in range(numberofpws):
+           wflist[i]=freciprocal[int(fftindex[0,i]-1),int(fftindex[1,i]-1),int(fftindex[2,i]-1)]
+       return wflist
+
+   def get_orthonormality_factor(self,matrix):
+       defect = abs(np.dot(dagger(matrix),matrix))-np.identity(matrix.shape[1],np.Float)
+       return max(abs(defect.flat))
+
+   def get_list_of_coefficients_and_rotation_matrices(self,matrixdimensions):
+       from ase.dft.wannier import normalize,gram_schmidt
+       import random, copy
+       M,N,L=matrixdimensions
+       nkpt=len(N)
+       Ulist=[]
+       clist=[]
+       if not hasattr(self,'mmatrix'):
+           raise "Must setup M Matrix first!"
+       coeffmatrix=self.mmatrix
+       for kpt in range(nkpt):
+           #First normalize the columns of coeffmatrix
+           normalize(coeffmatrix[kpt])
+           T=coeffmatrix[kpt][N[kpt]:].copy()
+           numberoforbitals=T.shape[1]
+           c=np.zeros([M-N[kpt],L[kpt]],np.complex)
+           U=np.zeros([N[kpt]+L[kpt],N[kpt]+L[kpt]],np.complex)
+           # Initialize weights
+           w=abs(np.sum(T*np.conjugate(T)))
+           for i in range(min(L[kpt],numberoforbitals)):
+               # Find index of maximal element in w
+               t=w.tolist().index(max(w))
+               c[:,i]=T[:,t]
+               # Orthogonalize c[:,i] on previous vectors
+               for j in range(i):
+                   c[:,i]=c[:,i]-project(c[:,j],T[:,t])
+               c[:,i]=c[:,i]/np.sqrt(np.dot(c[:,i],np.conjugate(c[:,i])))
+               # Update weights
+               w=w-abs(np.dot(np.conjugate(c[:,i]),T))**2
+           if numberoforbitals<L[kpt]:
+               # Supplement c by random vectors
+               for i in range(numberoforbitals,L[kpt]):
+                   for j in range(M-N[kpt]):
+                       c[j,i]=random.random()
+               gram_schmidt(c)
+           # Test whether columns are orthonormal
+           if L[kpt]>0:
+               test = self.get_orthonormality_factor(c)
+               if test>1.0e-3:
+                   print "ERROR: Columns of c not orthogonal!"
+                   
+           U[:N[kpt],:numberoforbitals]=coeffmatrix[kpt][:N[kpt]]
+           U[N[kpt]:,:numberoforbitals]=np.dot(dagger(c),coeffmatrix[kpt][N[kpt]:])
+           # Perform democratic Lowdin orthogonalization on U[:,numberoforbitals]
+           gram_schmidt(U[:,:numberoforbitals])
+           if numberoforbitals<(N[kpt]+L[kpt]):
+               #Supplement U by random vectors
+               for i in range(numberoforbitals,N[kpt]+L[kpt]):
+                   for j in range(N[kpt]+L[kpt]):
+                       U[j,i]=random.random()
+               # Finally orthogonalize everything
+               # Note, only random vectors are affected
+               gram_schmidt(U)
+           # Test whether columns are orthonormal
+           test = self.get_orthonormality_factor(U)
+           if test>1.0e-3:
+               print "ERROR: Columns of U not orthogonal for kpoint",kpt
+           Ulist.append(U)
+           clist.append(c)
+
+       return clist,Ulist
+
diff --git a/ase/calculators/jacapo/validate.py b/ase/calculators/jacapo/validate.py
new file mode 100644
index 0000000..f5614e9
--- /dev/null
+++ b/ase/calculators/jacapo/validate.py
@@ -0,0 +1,343 @@
+import os
+import numpy as np
+'''
+input validation module
+
+provides functions to validate all input variables to the Jacapo calculator.
+'''
+
+###########################################3
+### Validation functions
+##########################################
+def valid_int(x):
+    if (isinstance(x, int) or isinstance(x,np.int32)):
+        return True
+
+def valid_float(x):
+    return isinstance(x, float)
+
+def valid_int_or_float(x):
+    return ((isinstance(x, int) or isinstance(x,np.int32))
+            or isinstance(x, float))
+
+def valid_boolean(x):
+    return isinstance(x, bool)
+
+def valid_str(x):
+    return isinstance(x, str)
+
+def valid_atoms(x):
+    import ase
+    return isinstance(x, ase.Atoms)
+
+def valid_pw(x):
+    return (valid_int_or_float(x) and x>0 and x<2000)
+
+def valid_dw(x):
+    return (valid_int_or_float(x) and x>0 and x<2000)
+
+def valid_xc(x):
+    return (x in ['PW91', 'PBE', 'revPBE', 'RPBE', 'VWN'])
+
+def valid_nbands(x):
+    return valid_int(x)
+
+def valid_ft(x):
+    return(valid_float, x)
+
+def valid_spinpol(x):
+    return valid_boolean(x)
+
+def valid_fixmagmom(x):
+    return valid_float(x)
+
+def valid_symmetry(x):
+    return valid_boolean(x)
+
+def valid_calculate_stress(x):
+    return valid_boolean(x)
+
+def valid_kpts(x):
+    if isinstance(x, str):
+        return x in ['cc6_1x1',
+                     'cc12_2x3',
+                     'cc18_sq3xsq3',
+                     'cc18_1x1',
+                     'cc54_sq3xsq3',
+                     'cc54_1x1',
+                     'cc162_sq3xsq3',
+                     'cc162_1x1']
+    x = np.array(x)
+    #empty arg is no good
+    if x.shape == ():
+        return False
+    #monkhorst-pack
+    elif x.shape == (3,) and ((x.dtype == 'int32') or (x.dtype == 'int64')):
+        return True
+    #user-defined list
+    elif x.shape[1] == 3 and (str(x.dtype))[0:7] == 'float64':
+        return True
+    else:
+        return False
+
+def valid_dipole(x):
+    if valid_boolean(x):
+        return True
+    #dictionary passed in. we need to check the keys
+    valid_keys = {'status':valid_boolean,
+                  'mixpar':valid_float,
+                  'initval':valid_float,
+                  'adddipfield':valid_float,
+                  'position':valid_float}
+    for key in x:
+        if key not in valid_keys:
+            return False
+        else:
+            if x[key] is not None:
+                if not valid_keys[key](x[key]):
+                    return False
+    return True
+
+def valid_nc(x):
+    #todo check for read/write access?
+    return valid_str(x)
+
+def valid_status(x):
+    return valid_str(x)
+
+def valid_pseudopotentials(x):
+    #todo check that keys are symbols or numbers
+    #todo check that psp files exist
+
+    return True
+
+    DACAPOPATH = os.environ.get('DACAPOPATH', None)
+    if DACAPOPATH is None:
+        raise Exception, 'No $DACAPOPATH found. please set it in .cshrc or .bashrc'
+
+    from ase.data import chemical_symbols
+    for key in x:
+        if valid_str(key):
+            if key not in chemical_symbols:
+                return False
+        elif not (valid_int(key) and key > 0 and key < 112):
+            return False
+
+        #now check for existence of psp files
+        psp = x[key]
+        if not (os.path.exists(psp)
+                or os.path.exists(os.path.join(DACAPOPATH, psp))):
+            return False
+    return True
+
+def valid_extracharge(x):
+    return valid_float(x)
+
+def valid_extpot(x):
+    grids = get_fftgrid()
+    if (x.shape == np.array(grids['soft'])).all():
+        return True
+    else:
+        return False
+
+def valid_ascii_debug(x):
+    return (x.strip() in ['Off', 'MediumLevel', 'HighLevel'])
+
+def valid_ncoutput(x):
+    if x is None:
+        return
+    valid_keys = ['wf', 'cd', 'efp', 'esp']
+
+    for key in x:
+        if key not in valid_keys:
+            return False
+        else:
+            if x[key] not in ['Yes', 'No']:
+                return False
+    return True
+
+def valid_ados(x):
+    if x is None:
+        return
+    valid_keys = ['energywindow',
+                  'energywidth',
+                  'npoints',
+                  'cutoff']
+    for key in x:
+        if key not in valid_keys:
+            print '%s not in %s' % (key, str(valid_keys))
+            return False
+        if key == 'energywindow':
+            if not len(x['energywindow']) == 2:
+                print '%s is bad' % key
+                return False
+        if key == 'energywidth':
+            if not valid_float(x['energywidth']):
+                print key, ' is bad'
+                return False
+        elif key == 'npoints':
+            if not valid_int(x['npoints']):
+                print key, ' is bad'
+                return False
+        elif key == 'cutoff':
+            if not valid_float(x['cutoff']):
+                print key, ' is bad'
+                return False
+    return True
+
+
+def valid_decoupling(x):
+    if x is None:
+        return
+    valid_keys = ['ngaussians', 'ecutoff', 'gausswidth']
+    for key in x:
+        if key not in valid_keys:
+            return False
+        elif key == 'ngaussians':
+            if not valid_int(x[key]):
+                print key
+                return False
+        elif key == 'ecutoff':
+            if not valid_int_or_float(x[key]):
+                return False
+        elif key == 'gausswidth':
+            if not valid_float(x[key]):
+                print key, x[key]
+                return False
+    return True
+
+def valid_external_dipole(x):
+    if x is None:
+        return
+    if valid_float(x):
+        return True
+
+    valid_keys = ['value', 'position']
+
+    for key in x:
+        if key not in valid_keys:
+            return False
+        if key == 'value':
+            if not valid_float(x['value']):
+                return False
+        elif key == 'position':
+            if not valid_float(x['position']):
+                return False
+    return True
+
+def valid_stay_alive(x):
+    return valid_boolean(x)
+
+def valid_fftgrid(x):
+    valid_keys = ['soft', 'hard']
+    for key in x:
+        if key not in valid_keys:
+            return False
+        if x[key] is None:
+            continue
+
+        grid = np.array(x[key])
+        if (grid.shape != (3,) and grid.dtype != 'int32'):
+            return False
+    return True
+
+def valid_convergence(x):
+    valid_keys = ['energy',
+                  'density',
+                  'occupation',
+                  'maxsteps',
+                  'maxtime']
+    for key in x:
+        if key not in valid_keys:
+            return False
+        if x[key] is None:
+            continue
+        if key == 'energy':
+            if not valid_float(x[key]):
+                return False
+        elif key == 'density':
+            if not valid_float(x[key]):
+                return False
+        elif key == 'occupation':
+            if not valid_float(x[key]):
+                return False
+        elif key == 'maxsteps':
+            if not valid_int(x[key]):
+                return False
+        elif key == 'maxtime':
+            if not valid_int(x[key]):
+                return False
+    return True
+
+def valid_charge_mixing(x):
+    valid_keys = ['method',
+                  'mixinghistory',
+                  'mixingcoeff',
+                  'precondition',
+                  'updatecharge']
+
+    for key in x:
+        if key not in valid_keys:
+            return False
+        elif key == 'method':
+            if x[key] not in ['Pulay']:
+                return False
+        elif key == 'mixinghistory':
+            if not valid_int(x[key]):
+                return False
+        elif key == 'mixingcoeff':
+            if not valid_float(x[key]):
+                return False
+        elif key == 'precondition':
+            if x[key] not in ['Yes', 'No']:
+                return False
+        elif key == 'updatecharge':
+            if x[key] not in ['Yes', 'No']:
+                return False
+    return True
+
+def valid_electronic_minimization(x):
+    valid_keys = ['method', 'diagsperband']
+    for key in x:
+        if key not in valid_keys:
+            return False
+        elif key == 'method':
+            if x[key] not in ['resmin',
+                              'eigsolve',
+                              'rmm-diis']:
+                return False
+        elif key == 'diagsperband':
+            if not (valid_int(x[key]) or x[key] is None):
+                return False
+    return True
+
+def valid_occupationstatistics(x):
+    return (x in ['FermiDirac', 'MethfesselPaxton'])
+
+
+def valid_mdos(x):
+    return True
+
+def valid_psp(x):
+    valid_keys = ['sym','psp']
+    if x is None:
+        return True
+    for key in x:
+        if key not in valid_keys:
+            return False
+        if not valid_str(x[key]):
+            return False
+        if key == 'sym':
+            from ase.data import chemical_symbols
+            if key not in chemical_symbols:
+                return False
+        if key == 'psp':
+            
+            if os.path.exists(x['psp']):
+                return True
+
+            if os.path.exists(os.path.join(os.environ['DACAPOPATH'],
+                                           x['psp'])):
+                return True
+            #psp not found
+            return False
diff --git a/ase/calculators/jacapo/version.py b/ase/calculators/jacapo/version.py
new file mode 100644
index 0000000..c85336c
--- /dev/null
+++ b/ase/calculators/jacapo/version.py
@@ -0,0 +1,3 @@
+version = '0.6.7'
+
+
diff --git a/ase/calculators/lammps.py b/ase/calculators/lammps.py
new file mode 100644
index 0000000..84ebe13
--- /dev/null
+++ b/ase/calculators/lammps.py
@@ -0,0 +1,791 @@
+#!/usr/bin/env python
+
+# lammps.py (2011/03/29)
+# An ASE calculator for the LAMMPS classical MD code available from
+#       http://lammps.sandia.gov/
+# The environment variable LAMMPS_COMMAND must be defined to point to the LAMMPS binary.
+#
+# Copyright (C) 2009 - 2011 Joerg Meyer, joerg.meyer at ch.tum.de
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this file; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+# or see <http://www.gnu.org/licenses/>.
+
+
+import os 
+import shutil
+import shlex
+import time
+from subprocess import Popen, PIPE
+from threading import Thread
+from re import compile as re_compile, IGNORECASE
+from tempfile import mkdtemp, NamedTemporaryFile, mktemp as uns_mktemp
+import numpy as np
+import decimal as dec
+from ase import Atoms
+from ase.parallel import paropen
+from ase.units import GPa
+
+__all__ = ['LAMMPS', 'write_lammps_data']
+
+# "End mark" used to indicate that the calculation is done
+CALCULATION_END_MARK = '__end_of_ase_invoked_calculation__'
+
+class LAMMPS:
+
+    def __init__(self, label='lammps', tmp_dir=None, parameters={}, 
+                 specorder=None, files=[], always_triclinic=False, 
+                 keep_alive=True, keep_tmp_files=False,
+                 no_data_file=False):
+        """The LAMMPS calculators object
+
+        files: list
+            Short explanation XXX
+        parameters: dict
+            Short explanation XXX
+        specorder: list
+            Short explanation XXX
+        keep_tmp_files: bool
+            Retain any temporary files created. Mostly useful for debugging.
+        tmp_dir: str
+            path/dirname (default None -> create automatically).
+            Explicitly control where the calculator object should create 
+            its files. Using this option implies 'keep_tmp_files'
+        no_data_file: bool
+            Controls whether an explicit data file will be used for feeding
+            atom coordinates into lammps. Enable it to lessen the pressure on
+            the (tmp) file system. THIS OPTION MIGHT BE UNRELIABLE FOR CERTIAN
+            CORNER CASES (however, if it fails, you will notice...).
+        keep_alive: bool
+            When using LAMMPS as a spawned subprocess, keep the subprocess
+            alive (but idling whn unused) along with the calculator object.
+        always_triclinic: bool
+            Force use of a triclinic cell in LAMMPS, even if the cell is
+            a perfect parallelepiped.
+        """
+
+        self.label = label
+        self.parameters = parameters
+        self.specorder = specorder
+        self.files = files
+        self.always_triclinic = always_triclinic
+        self.calls = 0
+        self.forces = None
+        self.keep_alive = keep_alive
+        self.keep_tmp_files = keep_tmp_files
+        self.no_data_file = no_data_file
+        if tmp_dir is not None:
+            # If tmp_dir is pointing somewhere, don't remove stuff!
+            self.keep_tmp_files = True
+        self._lmp_handle = None        # To handle the lmp process
+
+        # read_log depends on that the first (three) thermo_style custom args
+        # can be capitilized and matched aginst the log output. I.e. 
+        # don't use e.g. 'ke' or 'cpu' which are labeled KinEng and CPU.
+        self._custom_thermo_args = ['step', 'temp', 'press', 'cpu', 
+                                    'pxx', 'pyy', 'pzz', 'pxy', 'pxz', 'pyz',
+                                    'ke', 'pe', 'etotal',
+                                    'vol', 'lx', 'ly', 'lz', 'atoms']
+        self._custom_thermo_mark = ' '.join([x.capitalize() for x in
+                                             self._custom_thermo_args[0:3]])
+
+        # Match something which can be converted to a float
+        f_re = r'([+-]?(?:(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|nan|inf))'
+        n = len(self._custom_thermo_args)
+        # Create a re matching exactly N white space separated floatish things
+        self._custom_thermo_re = re_compile(r'^\s*' + r'\s+'.join([f_re]*n) + r'\s*$',
+                                            flags=IGNORECASE)
+        # thermo_content contains data "writen by" thermo_style.
+        # It is a list of dictionaries, each dict (one for each line
+        # printed by thermo_style) contains a mapping between each 
+        # custom_thermo_args-argument and the corresponding
+        # value as printed by lammps. thermo_content will be 
+        # re-populated by the read_log method.
+        self.thermo_content = []
+
+        if tmp_dir is None:
+            self.tmp_dir = mkdtemp(prefix='LAMMPS-')
+        else:
+            self.tmp_dir=os.path.realpath(tmp_dir)
+            if not os.path.isdir(self.tmp_dir):
+                os.mkdir(self.tmp_dir, 0755)
+        
+        for f in files:
+            shutil.copy(f, os.path.join(self.tmp_dir, f))
+
+    def clean(self, force=False):
+
+        self._lmp_end()
+
+        if not self.keep_tmp_files:
+            shutil.rmtree(self.tmp_dir)
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.thermo_content[-1]['pe']
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        tc = self.thermo_content[-1]
+        # 1 bar (used by lammps for metal units) = 1e-4 GPa
+        return np.array([tc[i] for i in ('pxx','pyy','pzz',
+                                         'pyz','pxz','pxy')])*(-1e-4*GPa)
+
+    def update(self, atoms):
+        if not hasattr(self,'atoms') or self.atoms != atoms:
+            self.calculate(atoms)
+
+    def calculate(self, atoms):
+        self.atoms = atoms.copy()
+        pbc = self.atoms.get_pbc()
+        if all(pbc):
+            cell = self.atoms.get_cell()
+        elif not any(pbc):
+            # large enough cell for non-periodic calculation -
+            # LAMMPS shrink-wraps automatically via input command 
+            #       "periodic s s s"
+            # below
+            cell = 2 * np.max(np.abs(self.atoms.get_positions())) * np.eye(3)
+        else: 
+            print "WARNING: semi-periodic ASE cell detected -"
+            print "         translation to proper LAMMPS input cell might fail"
+            cell = self.atoms.get_cell()
+        self.prism = prism(cell)
+        self.run()
+
+    def _lmp_alive(self):
+        # Return True if this calculator is currently handling a running lammps process
+        return self._lmp_handle and not isinstance(self._lmp_handle.poll(), int)
+
+    def _lmp_end(self):
+        # Close lammps input and wait for lammps to end. Return process return value
+        if self._lmp_alive():
+            self._lmp_handle.stdin.close()
+            return self._lmp_handle.wait()
+        
+    def run(self):
+        """Method which explicitely runs LAMMPS."""
+
+        self.calls += 1
+
+        # set LAMMPS command from environment variable
+        if 'LAMMPS_COMMAND' in os.environ:
+            lammps_cmd_line = shlex.split(os.environ['LAMMPS_COMMAND'])
+            if len(lammps_cmd_line) == 0:
+                self.clean()
+                raise RuntimeError('The LAMMPS_COMMAND environment variable '
+                                   'must not be empty')
+            # want always an absolute path to LAMMPS binary when calling from self.dir                       
+            lammps_cmd_line[0] = os.path.abspath(lammps_cmd_line[0])
+
+        else:
+	    self.clean()
+            raise RuntimeError('Please set LAMMPS_COMMAND environment variable')
+        if 'LAMMPS_OPTIONS' in os.environ:
+            lammps_options = shlex.split(os.environ['LAMMPS_OPTIONS'])
+        else:
+            lammps_options = shlex.split('-echo log -screen none')
+
+
+        # change into subdirectory for LAMMPS calculations
+        cwd = os.getcwd()
+        os.chdir(self.tmp_dir)
+ 
+
+        # setup file names for LAMMPS calculation
+        label = '%s%06d' % (self.label, self.calls)
+        lammps_in = uns_mktemp(prefix='in_'+label, dir=self.tmp_dir)
+        lammps_log = uns_mktemp(prefix='log_'+label, dir=self.tmp_dir)
+        lammps_trj_fd = NamedTemporaryFile(prefix='trj_'+label, dir=self.tmp_dir,
+                                           delete=(not self.keep_tmp_files))
+        lammps_trj = lammps_trj_fd.name
+        if self.no_data_file:
+            lammps_data = None
+        else:
+            lammps_data_fd = NamedTemporaryFile(prefix='data_'+label, dir=self.tmp_dir,
+                                                delete=(not self.keep_tmp_files))
+            self.write_lammps_data(lammps_data=lammps_data_fd)
+            lammps_data = lammps_data_fd.name
+            lammps_data_fd.flush()
+
+
+        # see to it that LAMMPS is started
+        if not self._lmp_alive():
+            # Attempt to (re)start lammps
+            self._lmp_handle = Popen(lammps_cmd_line+lammps_options+['-log', '/dev/stdout'], 
+                                    stdin=PIPE, stdout=PIPE)
+        lmp_handle = self._lmp_handle
+
+
+        # Create thread reading lammps stdout (for reference, if requested,
+        # also create lammps_log, although it is never used)
+        if self.keep_tmp_files:
+            lammps_log_fd = open(lammps_log, 'w')
+            fd = special_tee(lmp_handle.stdout, lammps_log_fd)
+        else:
+            fd = lmp_handle.stdout
+        thr_read_log = Thread(target=self.read_lammps_log, args=(fd,))
+        thr_read_log.start()
+
+
+        # write LAMMPS input (for reference, also create the file lammps_in, 
+        # although it is never used)
+        if self.keep_tmp_files:
+            lammps_in_fd = open(lammps_in, 'w')
+            fd = special_tee(lmp_handle.stdin, lammps_in_fd)
+        else:
+            fd = lmp_handle.stdin
+        self.write_lammps_in(lammps_in=fd, lammps_trj=lammps_trj, lammps_data=lammps_data)
+
+        if self.keep_tmp_files:
+            lammps_in_fd.close()
+
+
+        # Wait for log output to be read (i.e., for LAMMPS to finish)
+        # and close the log file if there is one
+        thr_read_log.join()
+        if self.keep_tmp_files:
+            lammps_log_fd.close()
+
+        if not self.keep_alive:
+            self._lmp_end()
+
+        exitcode = lmp_handle.poll()
+        if exitcode and exitcode != 0:
+            cwd = os.getcwd()
+            raise RuntimeError('LAMMPS exited in %s with exit code: %d.' %\
+                                   (cwd,exitcode))
+
+        # A few sanity checks
+        if len(self.thermo_content) == 0:
+            raise RuntimeError('Failed to retreive any thermo_style-output')
+        if int(self.thermo_content[-1]['atoms']) != len(self.atoms):
+            # This obviously shouldn't happen, but if prism.fold_...() fails, it could
+            raise RuntimeError('Atoms have gone missing')
+
+
+        self.read_lammps_trj(lammps_trj=lammps_trj)
+        lammps_trj_fd.close()
+        if not self.no_data_file:
+            lammps_data_fd.close()
+
+        os.chdir(cwd)
+
+    def write_lammps_data(self, lammps_data=None):
+        """Method which writes a LAMMPS data file with atomic structure."""
+        if (lammps_data == None):
+            lammps_data = 'data.' + self.label
+        write_lammps_data(lammps_data, self.atoms, self.specorder, 
+                          force_skew=self.always_triclinic, prismobj=self.prism)
+
+    def write_lammps_in(self, lammps_in=None, lammps_trj=None, lammps_data=None):
+        """Method which writes a LAMMPS in file with run parameters and settings."""
+
+        if isinstance(lammps_in, str):
+            f = paropen(lammps_in, 'w')
+            close_in_file = True
+        else:
+            # Expect lammps_in to be a file-like object
+            f = lammps_in
+            close_in_file = False
+            
+        if self.keep_tmp_files:
+            f.write('# (written by ASE)\n')
+
+        # Write variables
+        f.write('clear\n' +
+                ('variable dump_file string "%s"\n' % lammps_trj) +
+                ('variable data_file string "%s"\n' % lammps_data))
+
+        parameters = self.parameters
+        pbc = self.atoms.get_pbc()
+        f.write('units metal \n')
+        if ('boundary' in parameters):
+            f.write('boundary %s \n' % parameters['boundary'])
+        else:
+            f.write('boundary %c %c %c \n' % tuple('sp'[x] for x in pbc))
+        f.write('atom_modify sort 0 0.0 \n')
+        for key in ('neighbor' ,'newton'):
+            if key in parameters:
+                f.write('%s %s \n' % (key, parameters[key]))
+        f.write('\n')
+
+
+        # If self.no_lammps_data, 
+        # write the simulation box and the atoms
+        if self.no_data_file:
+            if self.keep_tmp_files:
+                f.write('## Original ase cell\n')
+                f.write(''.join(['# %.16f %.16f %.16f\n' % tuple(x)
+                                 for x in self.atoms.get_cell()]))
+
+            p = self.prism
+            f.write('lattice sc 1.0\n')
+            xhi, yhi, zhi, xy, xz, yz = p.get_lammps_prism_str()
+            if self.always_triclinic or p.is_skewed():
+                f.write('region asecell prism 0.0 %s 0.0 %s 0.0 %s ' % \
+                            (xhi, yhi, zhi))
+                f.write('%s %s %s side in units box\n' % \
+                            (xy, xz, yz))
+            else:
+                f.write(('region asecell block 0.0 %s 0.0 %s 0.0 %s '
+                         'side in units box\n') % (xhi, yhi, zhi))
+                    
+            symbols = self.atoms.get_chemical_symbols()
+            if self.specorder is None:
+                # By default, atom types in alphabetic order
+                species = sorted(list(set(symbols)))
+            else:
+                # By request, specific atom type ordering
+                species = self.specorder
+
+            n_atom_types = len(species)
+            species_i = dict([(s,i+1) for i,s in enumerate(species)])
+
+            f.write('create_box %i asecell\n' % n_atom_types)
+            for s, pos in zip(symbols, self.atoms.get_positions()):
+                if self.keep_tmp_files:
+                    f.write('# atom pos in ase cell: %.16f %.16f %.16f\n' % tuple(pos))
+                f.write('create_atoms %i single %s %s %s units box\n' % \
+                            ((species_i[s],)+p.pos_to_lammps_fold_str(pos)))
+                
+
+        # if NOT self.no_lammps_data, then simply refer to the data-file
+        else:
+            f.write('read_data %s\n' % lammps_data)
+
+
+        # Write interaction stuff
+        f.write('\n### interactions \n')
+        if ( ('pair_style' in parameters) and ('pair_coeff' in parameters) ):
+            pair_style = parameters['pair_style']
+            f.write('pair_style %s \n' % pair_style)
+            for pair_coeff in parameters['pair_coeff']:
+                f.write('pair_coeff %s \n' % pair_coeff)
+            if 'mass' in parameters:
+                for mass in parameters['mass']:
+                    f.write('mass %s \n' % mass)
+        else:
+            # simple default parameters 
+            # that should always make the LAMMPS calculation run
+            f.write('pair_style lj/cut 2.5 \n' +
+                    'pair_coeff * * 1 1 \n' +
+                    'mass * 1.0 \n')
+
+        f.write('\n### run\n' +
+                'fix fix_nve all nve\n' +
+                ('dump dump_all all custom 1 %s id type x y z vx vy vz fx fy fz\n' % lammps_trj) )
+        f.write(('thermo_style custom %s\n' +
+                'thermo_modify flush yes\n' +
+                'thermo 1\n') % (' '.join(self._custom_thermo_args)))
+
+        if 'minimize' in parameters:
+            f.write('minimize %s\n' % parameters['minimize'])
+        if 'run' in parameters:
+            f.write('run %s\n' % parameters['run'])
+        if not (('minimize' in parameters) or ('run' in parameters)):
+            f.write('run 0\n')
+
+        f.write('print "%s"\n' % CALCULATION_END_MARK)
+        f.write('log /dev/stdout\n') # Force LAMMPS to flush log
+
+        if close_in_file:
+            f.close()
+
+    def read_lammps_log(self, lammps_log=None, PotEng_first=False):
+        """Method which reads a LAMMPS output log file."""
+
+        if (lammps_log == None):
+            lammps_log = self.label + '.log'
+
+        if isinstance(lammps_log, str):
+            f = paropen(lammps_log, 'w')
+            close_log_file = True
+        else:
+            # Expect lammps_in to be a file-like object
+            f = lammps_log
+            close_log_file = False
+
+        thermo_content = []
+        line = f.readline()
+        while line and line.strip() != CALCULATION_END_MARK:
+            # get thermo output
+            if line.startswith(self._custom_thermo_mark):
+                m = True
+                while m:
+                    line = f.readline()
+                    m = self._custom_thermo_re.match(line)
+                    if m:
+                        # create a dictionary between each of the thermo_style args
+                        # and it's corresponding value
+                        thermo_content.append(dict(zip(self._custom_thermo_args, 
+                                                       map(float, m.groups()))))
+            else:
+                line = f.readline()
+
+        if close_log_file:
+            f.close()
+
+        self.thermo_content = thermo_content
+
+    def read_lammps_trj(self, lammps_trj=None, set_atoms=False):
+        """Method which reads a LAMMPS dump file."""
+        if (lammps_trj == None):
+            lammps_trj = self.label + '.lammpstrj'
+
+        f = paropen(lammps_trj, 'r')
+        while True:
+            line = f.readline()
+
+            if not line:
+                break
+
+            #TODO: extend to proper dealing with multiple steps in one trajectory file
+            if 'ITEM: TIMESTEP' in line:
+                n_atoms = 0
+                lo = [] ; hi = [] ; tilt = []
+                id = [] ; type = []
+                positions = [] ; velocities = [] ; forces = []
+
+            if 'ITEM: NUMBER OF ATOMS' in line:
+                line = f.readline()
+                n_atoms = int(line.split()[0])
+            
+            if 'ITEM: BOX BOUNDS' in line:
+                # save labels behind "ITEM: BOX BOUNDS" in triclinic case (>=lammps-7Jul09)
+                tilt_items = line.split()[3:]
+                for i in range(3):
+                    line = f.readline()
+                    fields = line.split()
+                    lo.append(float(fields[0]))
+                    hi.append(float(fields[1]))
+                    if (len(fields) >= 3):
+                        tilt.append(float(fields[2]))
+            
+            if 'ITEM: ATOMS' in line:
+                # (reliably) identify values by labels behind "ITEM: ATOMS" - requires >=lammps-7Jul09
+                # create corresponding index dictionary before iterating over atoms to (hopefully) speed up lookups...
+                atom_attributes = {}
+                for (i, x) in enumerate(line.split()[2:]):
+                    atom_attributes[x] = i
+                for n in range(n_atoms):
+                    line = f.readline()
+                    fields = line.split()
+                    id.append( int(fields[atom_attributes['id']]) )
+                    type.append( int(fields[atom_attributes['type']]) )
+                    positions.append( [ float(fields[atom_attributes[x]]) for x in ['x', 'y', 'z'] ] )
+                    velocities.append( [ float(fields[atom_attributes[x]]) for x in ['vx', 'vy', 'vz'] ] )
+                    forces.append( [ float(fields[atom_attributes[x]]) for x in ['fx', 'fy', 'fz'] ] )
+        f.close()
+
+        # determine cell tilt (triclinic case!)
+        if (len(tilt) >= 3):
+            # for >=lammps-7Jul09 use labels behind "ITEM: BOX BOUNDS" to assign tilt (vector) elements ...
+            if (len(tilt_items) >= 3):
+                xy = tilt[tilt_items.index('xy')]
+                xz = tilt[tilt_items.index('xz')]
+                yz = tilt[tilt_items.index('yz')]
+            # ... otherwise assume default order in 3rd column (if the latter was present)
+            else:
+                xy = tilt[0]
+                xz = tilt[1]
+                yz = tilt[2]
+        else:
+            xy = xz = yz = 0
+        xhilo = (hi[0] - lo[0]) - xy - xz
+        yhilo = (hi[1] - lo[1]) - yz
+        zhilo = (hi[2] - lo[2])
+	
+# The simulation box bounds are included in each snapshot and if the box is triclinic (non-orthogonal), 
+# then the tilt factors are also printed; see the region prism command for a description of tilt factors. 
+# For triclinic boxes the box bounds themselves (first 2 quantities on each line) are a true "bounding box" 
+# around the simulation domain, which means they include the effect of any tilt.
+# [ http://lammps.sandia.gov/doc/dump.html , lammps-7Jul09 ]
+#
+# This *should* extract the lattice vectors that LAMMPS uses from the true "bounding box" printed in the dump file
+# It might fail in some cases (negative tilts?!) due to the MIN / MAX construction of these box corners:
+#
+#	void Domain::set_global_box() 
+#	[...]
+#	  if (triclinic) {
+#	    [...]
+#	    boxlo_bound[0] = MIN(boxlo[0],boxlo[0]+xy);
+#	    boxlo_bound[0] = MIN(boxlo_bound[0],boxlo_bound[0]+xz);
+#	    boxlo_bound[1] = MIN(boxlo[1],boxlo[1]+yz);
+#	    boxlo_bound[2] = boxlo[2];
+#
+#	    boxhi_bound[0] = MAX(boxhi[0],boxhi[0]+xy);
+#	    boxhi_bound[0] = MAX(boxhi_bound[0],boxhi_bound[0]+xz);
+#	    boxhi_bound[1] = MAX(boxhi[1],boxhi[1]+yz);
+#	    boxhi_bound[2] = boxhi[2];
+#	  }
+# [ lammps-7Jul09/src/domain.cpp ]
+#
+        cell = [[xhilo,0,0],[xy,yhilo,0],[xz,yz,zhilo]]
+
+        # assume that LAMMPS does not reorder atoms internally
+        cell_atoms = np.array(cell)
+        type_atoms = np.array(type)
+
+        if self.atoms:
+            cell_atoms = self.atoms.get_cell()
+
+            # BEWARE: reconstructing the rotation from the LAMMPS output trajectory file
+            #         fails in case of shrink wrapping for a non-periodic direction
+            # -> hence rather obtain rotation from prism object used to generate the LAMMPS input
+            #rotation_lammps2ase = np.dot(np.linalg.inv(np.array(cell)), cell_atoms)
+            rotation_lammps2ase = np.linalg.inv(self.prism.R)
+
+            type_atoms = self.atoms.get_atomic_numbers()
+            positions_atoms = np.array( [np.dot(np.array(r), rotation_lammps2ase) for r in positions] )
+            velocities_atoms = np.array( [np.dot(np.array(v), rotation_lammps2ase) for v in velocities] )
+            forces_atoms = np.array( [np.dot(np.array(f), rotation_lammps2ase) for f in forces] )
+
+        if (set_atoms):
+            # assume periodic boundary conditions here (like also below in write_lammps)
+            self.atoms = Atoms(type_atoms, positions=positions_atoms, cell=cell_atoms)
+
+        self.forces = forces_atoms
+
+
+
+class special_tee:
+    """A special purpose, with limited applicability, tee-like thing.
+
+    A subset of stuff read from, or written to, orig_fd, 
+    is also written to out_fd.
+    It is used by the lammps calculator for creating file-logs of stuff read from,
+    or written to, stdin and stdout, respectively.
+    """
+    def __init__(self, orig_fd, out_fd):
+        self._orig_fd = orig_fd
+        self._out_fd = out_fd
+        self.name = orig_fd.name
+    def write(self, data):
+        self._orig_fd.write(data)
+        self._out_fd.write(data)
+    def read(self, *args, **kwargs):
+        data = self._orig_fd.read(*args, **kwargs)
+        self._out_fd.write(data)
+        return data
+    def readline(self, *args, **kwargs):
+        data = self._orig_fd.readline(*args, **kwargs)
+        self._out_fd.write(data)
+        return data
+    def readlines(self, *args, **kwargs):
+        data = self._orig_fd.readlines(*args, **kwargs)
+        self._out_fd.write(''.join(data))
+        return data
+    def flush(self):
+        self._orig_fd.flush()
+        self._out_fd.flush()
+
+
+class prism:
+    def __init__(self, cell, pbc=(True,True,True), digits=10):
+        """Create a lammps-style triclinic prism object from a cell
+
+        The main purpose of the prism-object is to create suitable 
+        string representations of prism limits and atom positions
+        within the prism.
+        When creating the object, the digits parameter (default set to 10)
+        specify the precission to use.
+        lammps is picky about stuff being within semi-open intervals,
+        e.g. for atom positions (when using create_atom in the in-file), 
+        x must be within [xlo, xhi).
+        """
+        a, b, c = cell
+        an, bn, cn = [np.linalg.norm(v) for v in cell]
+        
+        alpha = np.arccos(np.dot(b, c)/(bn*cn))
+        beta  = np.arccos(np.dot(a, c)/(an*cn))
+        gamma = np.arccos(np.dot(a, b)/(an*bn))
+        
+        xhi = an
+        xyp = np.cos(gamma)*bn
+        yhi = np.sin(gamma)*bn
+        xzp = np.cos(beta)*cn
+        yzp = (bn*cn*np.cos(alpha) - xyp*xzp)/yhi
+        zhi = np.sqrt(cn**2 - xzp**2 - yzp**2)
+    
+        # Set precision
+        self.car_prec = dec.Decimal('10.0') ** \
+            int(np.floor(np.log10(max((xhi,yhi,zhi))))-digits)
+        self.dir_prec = dec.Decimal('10.0') ** (-digits)
+        self.acc = float(self.car_prec)
+        self.eps = np.finfo(xhi).eps
+
+        # For rotating positions from ase to lammps
+        Apre = np.array(((xhi, 0,   0),
+                         (xyp, yhi, 0),
+                         (xzp, yzp, zhi)))
+        self.R = np.dot(np.linalg.inv(cell), Apre)
+
+        # Actual lammps cell may be different from what is used to create R
+        def fold(vec, pvec, i):
+            p = pvec[i]
+            x = vec[i] + 0.5*p
+            n = (np.mod(x, p) - x)/p
+            return [float(self.f2qdec(a)) for a in (vec + n*pvec)]
+
+        Apre[1,:] = fold(Apre[1,:], Apre[0,:], 0)
+        Apre[2,:] = fold(Apre[2,:], Apre[1,:], 1)
+        Apre[2,:] = fold(Apre[2,:], Apre[0,:], 0)
+
+        self.A = Apre
+        self.Ainv = np.linalg.inv(self.A)
+
+        if self.is_skewed() and \
+                (not (pbc[0] and pbc[1] and pbc[2])):
+            raise RuntimeError('Skewed lammps cells MUST have '
+                               'PBC == True in all directions!')
+
+    def f2qdec(self, f):
+        return dec.Decimal(repr(f)).quantize(self.car_prec, dec.ROUND_DOWN)
+
+    def f2qs(self, f):
+        return str(self.f2qdec(f))
+
+    def f2s(self, f):
+        return str(dec.Decimal(repr(f)).quantize(self.car_prec, dec.ROUND_HALF_EVEN))
+
+    def dir2car(self, v):
+        "Direct to cartesian coordinates"
+        return np.dot(v, self.A)
+
+    def car2dir(self, v):
+        "Cartesian to direct coordinates"
+        return np.dot(v, self.Ainv)
+
+    def fold_to_str(self,v):
+        "Fold a position into the lammps cell (semi open), return a tuple of str" 
+        # Two-stage fold, first into box, then into semi-open interval
+        # (within the given precission).
+        d = [x % (1-self.dir_prec) for x in 
+             map(dec.Decimal, map(repr, np.mod(self.car2dir(v) + self.eps, 1.0)))]
+        return tuple([self.f2qs(x) for x in 
+                      self.dir2car(map(float, d))])
+        
+    def get_lammps_prism(self):
+        A = self.A
+        return (A[0,0], A[1,1], A[2,2], A[1,0], A[2,0], A[2,1])
+
+    def get_lammps_prism_str(self):
+        "Return a tuple of strings"
+        p = self.get_lammps_prism()
+        return tuple([self.f2s(x) for x in p])
+
+    def pos_to_lammps_str(self, position):
+        "Rotate an ase-cell postion to the lammps cell orientation, return tuple of strs"
+        return tuple([self.f2s(x) for x in np.dot(position, self.R)])
+
+    def pos_to_lammps_fold_str(self, position):
+        "Rotate and fold an ase-cell postion into the lammps cell, return tuple of strs"
+        return self.fold_to_str(np.dot(position, self.R))
+
+    def is_skewed(self):
+        acc = self.acc
+        prism = self.get_lammps_prism()
+        axy, axz, ayz = [np.abs(x) for x in prism[3:]]
+        return (axy >= acc) or (axz >= acc) or (ayz >= acc)
+        
+
+def write_lammps_data(fileobj, atoms, specorder=[], force_skew=False, prismobj=None):
+    """Method which writes atomic structure data to a LAMMPS data file."""
+    if isinstance(fileobj, str):
+        f = paropen(fileobj, 'w')
+        close_file = True
+    else:
+        # Presume fileobj acts like a fileobj
+        f = fileobj
+        close_file = False
+
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            raise ValueError('Can only write one configuration to a lammps data file!')
+        atoms = atoms[0]
+
+    f.write(f.name + ' (written by ASE) \n\n')
+
+    symbols = atoms.get_chemical_symbols()
+    n_atoms = len(symbols)
+    f.write('%d \t atoms \n' % n_atoms)
+
+    if specorder is None:
+        # This way it is assured that LAMMPS atom types are always
+        # assigned predictively according to the alphabetic order 
+        species = sorted(list(set(symbols)))
+    else:
+        # To index elements in the LAMMPS data file 
+        # (indices must correspond to order in the potential file)
+        species = specorder
+    n_atom_types = len(species)
+    f.write('%d  atom types\n' % n_atom_types)
+
+    if prismobj is None:
+        p = prism(atoms.get_cell())
+    else:
+        p = prismobj
+    xhi, yhi, zhi, xy, xz, yz = p.get_lammps_prism_str()
+
+    f.write('0.0 %s  xlo xhi\n' % xhi)
+    f.write('0.0 %s  ylo yhi\n' % yhi)
+    f.write('0.0 %s  zlo zhi\n' % zhi)
+    
+    if force_skew or p.is_skewed():
+        f.write('%s %s %s  xy xz yz\n' % (xy, xz, yz))
+    f.write('\n\n')
+
+    f.write('Atoms \n\n')
+    for i, r in enumerate(map(p.pos_to_lammps_str,
+                              atoms.get_positions())):
+        s = species.index(symbols[i]) + 1
+        f.write('%6d %3d %s %s %s\n' % ((i+1, s)+tuple(r)))
+    
+    if close_file:
+        f.close()
+
+
+if __name__ == '__main__':
+
+    pair_style = 'eam'
+    Pd_eam_file = 'Pd_u3.eam'
+    pair_coeff = [ '* * ' + Pd_eam_file ]
+    parameters = { 'pair_style' : pair_style, 'pair_coeff' : pair_coeff }
+    files = [ Pd_eam_file ]
+    calc = LAMMPS(parameters=parameters, files=files)
+    from ase import Atoms
+    a0 = 3.93
+    b0 = a0 / 2.0
+    if True:
+        bulk = Atoms(['Pd']*4,
+                     positions=[(0,0,0),(b0,b0,0),(b0,0,b0),(0,b0,b0)],
+                     cell=[a0]*3,
+                     pbc=True)
+        # test get_forces
+        print 'forces for a = %f' % a0
+        print calc.get_forces(bulk)
+        # single points for various lattice constants
+        bulk.set_calculator(calc)
+        for n in range(-5,5,1):
+            a = a0 * (1 + n/100.0)
+            bulk.set_cell([a]*3)
+            print 'a : %f , total energy : %f' % (a, bulk.get_potential_energy())
+
+    calc.clean()
+
diff --git a/ase/calculators/lj.py b/ase/calculators/lj.py
new file mode 100644
index 0000000..a1cc48f
--- /dev/null
+++ b/ase/calculators/lj.py
@@ -0,0 +1,41 @@
+import numpy as np
+
+
+class LennardJones:
+    def __init__(self, epsilon=1.0, sigma=1.0):
+        self.epsilon = epsilon
+        self.sigma = sigma
+        self.positions = None
+
+    def update(self, atoms):
+        assert not atoms.get_pbc().any()
+        if (self.positions is None or
+            (self.positions != atoms.get_positions()).any()):
+            self.calculate(atoms)
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self._forces
+
+    def get_stress(self, atoms):
+        return np.zeros((3, 3))
+    
+    def calculate(self, atoms):
+        positions = atoms.get_positions()
+        self.energy = 0.0
+        self._forces = np.zeros((len(atoms), 3))
+        for i1, p1 in enumerate(positions):
+            for i2, p2 in enumerate(positions[:i1]):
+                diff = p2 - p1
+                d2 = np.dot(diff, diff)
+                c6 = (self.sigma**2 / d2)**3
+                c12 = c6**2
+                self.energy += 4 * self.epsilon * (c12 - c6)
+                F = 24 * self.epsilon * (2 * c12 - c6) / d2 * diff
+                self._forces[i1] -= F
+                self._forces[i2] += F
+        self.positions = positions.copy()
diff --git a/ase/calculators/morse.py b/ase/calculators/morse.py
new file mode 100644
index 0000000..9f927bc
--- /dev/null
+++ b/ase/calculators/morse.py
@@ -0,0 +1,46 @@
+from math import exp, sqrt
+import numpy as np
+
+class MorsePotential:
+    """Morse potential.
+
+    Default values chosen to be similar as Lennard-Jones.
+    """
+    def __init__(self, rho0=6.0, epsilon=1.0, r0=1.0):
+        self.epsilon = epsilon
+        self.rho0 = rho0
+        self.r0 = r0
+        self.positions = None
+
+    def update(self, atoms):
+        assert not atoms.get_pbc().any()
+        if (self.positions is None or
+            (self.positions != atoms.get_positions()).any()):
+            self.calculate(atoms)
+
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self._forces
+
+    def get_stress(self, atoms):
+        return np.zeros((3, 3))
+    
+    def calculate(self, atoms):
+        positions = atoms.get_positions()
+        self.energy = 0.0
+        self._forces = np.zeros((len(atoms), 3))
+        preF = 2 * self.epsilon * self.rho0 / self.r0
+        for i1, p1 in enumerate(positions):
+            for i2, p2 in enumerate(positions[:i1]):
+                diff = p2 - p1
+                r = sqrt(np.dot(diff, diff))
+                expf = exp(self.rho0 * (1.0 - r / self.r0))
+                self.energy += self.epsilon * expf * (expf - 2)
+                F = preF * expf * (expf - 1) * diff / r
+                self._forces[i1] -= F
+                self._forces[i2] += F
+        self.positions = positions.copy()
diff --git a/ase/calculators/neighborlist.py b/ase/calculators/neighborlist.py
new file mode 100644
index 0000000..4c8483c
--- /dev/null
+++ b/ase/calculators/neighborlist.py
@@ -0,0 +1,160 @@
+from math import sqrt
+
+import numpy as np
+
+
+class NeighborList:
+    """Neighbor list object.
+
+    cutoffs: list of float
+        List of cutoff radii - one for each atom.
+    skin: float
+        If no atom has moved more than the skin-distance since the
+        last call to the ``update()`` method, then the neighbor list
+        can be reused.  This will save some expensive rebuilds of
+        the list, but extra neighbors outside the cutoff will be
+        returned.
+    self_interaction: bool
+        Should an atom return itself as a neighbor?
+    bothways: bool
+        Return all neighbors.  Default is to return only "half" of
+        the neighbors.
+    
+    Example::
+
+      nl = NeighborList([2.3, 1.7])
+      nl.update(atoms)
+      indices, offsets = nl.get_neighbors(0)
+      
+    """
+    
+    def __init__(self, cutoffs, skin=0.3, sorted=False, self_interaction=True,
+                 bothways=False):
+        self.cutoffs = np.asarray(cutoffs) + skin
+        self.skin = skin
+        self.sorted = sorted
+        self.self_interaction = self_interaction
+        self.bothways = bothways
+        self.nupdates = 0
+
+    def update(self, atoms):
+        """Make sure the list is up to date."""
+        if self.nupdates == 0:
+            self.build(atoms)
+            return True
+        
+        if ((self.pbc != atoms.get_pbc()).any() or
+            (self.cell != atoms.get_cell()).any() or
+            ((self.positions - atoms.get_positions())**2).sum(1).max() >
+            self.skin**2):
+            self.build(atoms)
+            return True
+        
+        return False
+    
+    def build(self, atoms):
+        """Build the list."""
+        self.positions = atoms.get_positions()
+        self.pbc = atoms.get_pbc()
+        self.cell = atoms.get_cell()
+        if len(self.cutoffs) > 0:
+            rcmax = self.cutoffs.max()
+        else:
+            rcmax = 0.0
+
+        icell = np.linalg.inv(self.cell)
+        scaled = np.dot(self.positions, icell)
+        scaled0 = scaled.copy()
+
+        N = []
+        for i in range(3):
+            if self.pbc[i]:
+                scaled0[:, i] %= 1.0
+                v = icell[:, i]
+                h = 1 / sqrt(np.dot(v, v))
+                n =  int(2 * rcmax / h) + 1
+            else:
+                n = 0
+            N.append(n)
+            
+        offsets = np.empty((len(atoms), 3), int)
+        (scaled0 - scaled).round(out=offsets)
+        positions0 = np.dot(scaled0, self.cell)
+        natoms = len(atoms)
+        indices = np.arange(natoms)
+
+        self.nneighbors = 0
+        self.npbcneighbors = 0
+        self.neighbors = [np.empty(0, int) for a in range(natoms)]
+        self.displacements = [np.empty((0, 3), int) for a in range(natoms)]
+        for n1 in range(0, N[0] + 1):
+            for n2 in range(-N[1], N[1] + 1):
+                for n3 in range(-N[2], N[2] + 1):
+                    if n1 == 0 and (n2 < 0 or n2 == 0 and n3 < 0):
+                        continue
+                    displacement = np.dot((n1, n2, n3), self.cell)
+                    for a in range(natoms):
+                        d = positions0 + displacement - positions0[a]
+                        i = indices[(d**2).sum(1) <
+                                    (self.cutoffs + self.cutoffs[a])**2]
+                        if n1 == 0 and n2 == 0 and n3 == 0:
+                            if self.self_interaction:
+                                i = i[i >= a]
+                            else:
+                                i = i[i > a]
+                        self.nneighbors += len(i)
+                        self.neighbors[a] = np.concatenate(
+                            (self.neighbors[a], i))
+                        disp = np.empty((len(i), 3), int)
+                        disp[:] = (n1, n2, n3)
+                        disp += offsets[i] - offsets[a]
+                        self.npbcneighbors += disp.any(1).sum()
+                        self.displacements[a] = np.concatenate(
+                            (self.displacements[a], disp))
+
+        if self.bothways:
+            neighbors2 = [[] for a in range(natoms)]
+            displacements2 = [[] for a in range(natoms)]
+            for a in range(natoms):
+                for b, disp in zip(self.neighbors[a], self.displacements[a]):
+                    neighbors2[b].append(a)
+                    displacements2[b].append(-disp)
+            for a in range(natoms):
+                self.neighbors[a] = np.concatenate((self.neighbors[a],
+                                                    neighbors2[a]))
+                self.displacements[a] = np.array(list(self.displacements[a]) +
+                                                 displacements2[a])
+
+        if self.sorted:
+            for a, i in enumerate(self.neighbors):
+                mask = (i < a)
+                if mask.any():
+                    j = i[mask]
+                    offsets = self.displacements[a][mask]
+                    for b, offset in zip(j, offsets):
+                        self.neighbors[b] = np.concatenate(
+                            (self.neighbors[b], [a]))
+                        self.displacements[b] = np.concatenate(
+                                (self.displacements[b], [-offset]))
+                    mask = np.logical_not(mask)
+                    self.neighbors[a] = self.neighbors[a][mask]
+                    self.displacements[a] = self.displacements[a][mask]
+                
+        self.nupdates += 1
+
+    def get_neighbors(self, a):
+        """Return neighbors of atom number a.
+
+        A list of indices and offsets to neighboring atoms is
+        returned.  The positions of the neighbor atoms can be
+        calculated like this::
+
+          indices, offsets = nl.get_neighbors(42)
+          for i, offset in zip(indices, offsets):
+              print atoms.positions[i] + dot(offset, atoms.get_cell())
+
+        Notice that if get_neighbors(a) gives atom b as a neighbor,
+        then get_neighbors(b) will not return a as a neighbor - unless
+        bothways=True was used."""
+        
+        return self.neighbors[a], self.displacements[a]
diff --git a/ase/calculators/nwchem.py b/ase/calculators/nwchem.py
new file mode 100644
index 0000000..746afd5
--- /dev/null
+++ b/ase/calculators/nwchem.py
@@ -0,0 +1,383 @@
+"""This module defines an ASE interface to NWchem
+
+http://www.nwchem-sw.org/
+"""
+import os
+import sys
+
+import numpy as np
+
+from ase.units import Hartree, Bohr
+from ase.io.nwchem import write_nwchem, read_nwchem
+from ase.calculators.general import Calculator
+
+class KPoint:
+    def __init__(self, s):
+        self.s = s
+        self.eps_n = []
+        self.f_n = []
+
+class NWchem(Calculator):
+    def __init__(self,
+                 label='nwchem',
+                 task='energy',
+                 geometry=None,
+                 xc='LDA',
+                 convergence = {'energy'  : None,
+                                'density' : None,
+                                'gradient': None,
+                                'lshift': None, # set to 0.0 for nolevelshifting
+                                },
+                 smear=0.001*Hartree, # smear must be specified to appear in out!
+                 grid=None,
+                 tolerances=None,
+                 cgmin=False,
+                 maxiter = 120,
+                 basis='3-21G',
+                 basispar=None,
+                 ecp=None,
+                 so=None,
+                 charge=None,
+                 multiplicity=None,
+                 spinorbit=False,
+                 kpts=None,
+                 dftcontrol='', # additional dft control string
+                 control='', # additional outside of dft block control string
+                 ):
+        """Construct NWchem-calculator object.
+
+        Parameters
+        ==========
+        label: str
+            Prefix to use for filenames (label.nw, label.out, ...).
+            Default is 'nwchem'.
+        xc: str
+            Exchange-correlation functional. LDA and PBE are predefined,
+            use nchem names instead.
+        basis: str
+            Basis set
+        maxiter: int
+            Maximal number of iteratations in self-consistent field convergence.
+            """
+
+        self.label = label
+        self.task = task
+        self.geometry = geometry
+        self.xc = xc
+        self.convergence = convergence
+        self.smear = round(smear/Hartree, 4)
+        self.grid = grid
+        self.tolerances = tolerances
+        self.cgmin = cgmin
+        self.maxiter = maxiter
+        self.basis = basis
+        if basispar is not None:
+            self.basispar = 'basis ' + basispar
+        else:
+            self.basispar = 'basis'
+        self.ecp = ecp
+        self.so = so
+        self.charge = charge
+        self.multiplicity = multiplicity
+        self.spinorbit = spinorbit
+        self.kpts = kpts
+        self.dftcontrol = dftcontrol
+        self.control = control
+
+        # does nwchem have stress ???
+        self.stress = np.zeros((3, 3))
+
+        # atoms must be set
+        self.atoms = None
+
+        self.converged = False
+
+    def execute(self, command):
+        from subprocess import Popen, PIPE
+        try:
+            # the sub process gets started here
+            proc = Popen([command], shell=True, stderr=PIPE)
+            error = proc.communicate()[1]
+            if error:
+                raise OSError(error + '\ncheck ' + self.output)
+        except OSError, e:
+            print >> sys.stderr, 'Execution failed:', e
+            sys.exit(1)
+
+    def run(self):
+        """Method which explicitely runs Nwchem."""
+
+        command = os.environ.get('NWCHEM_COMMAND', 'nwchem')
+        # The label may contain escapable characters!
+        self.execute(command + ' "' + \
+                     self.label + '.nw" > "' + self.output + '"')
+
+    def get_forces(self, atoms):
+        self.get_potential_energy(atoms)
+        return self.forces
+
+    def get_ibz_k_points(self):
+        return np.array([0., 0., 0.])
+
+    def get_electronic_temperature(self):
+        return self.electronic_temperature
+
+    def read_smear(self):
+        smear = None
+        for line in open(self.label + '.out'): # find last one
+            if line.find('Smearing applied:') != -1:
+                smear = float(line.split(':')[1].split()[0].strip().lower().replace('d', 'e'))
+        return smear
+
+    def get_number_of_bands(self):
+        return self.nvector
+
+    def read_number_of_bands(self):
+        nvector = 0
+        for line in open(self.label + '.out'):
+            if line.find('Vector ') != -1: # count all printed vectors
+                nvector += 1
+        if not nvector:
+            nvector = None
+        return nvector
+
+    def get_number_of_electrons(self):
+        return self.nelect
+
+    def read_number_of_electrons(self):
+        nelect = None
+        for line in open(self.label + '.out'): # find last one
+            if line.find('of electrons') != -1:
+                nelect = float(line.split(':')[1].strip())
+        return nelect
+
+    def get_number_of_iterations(self):
+        return self.niter
+
+    def read_number_of_iterations(self):
+        niter = 0
+        for line in open(self.label + '.out'):
+            if line.find('d= ') != -1: # count all iterations
+                niter += 1
+        if not niter:
+            niter = None
+        return niter
+
+    def get_magnetic_moment(self, atoms):
+        return self.magnetic_moment
+
+    def read_magnetic_moment(self):
+        magmom = None
+        for line in open(self.label + '.out'):
+            if line.find('Spin multiplicity') != -1: # last one
+                magmom = float(line.split(':')[-1].strip()) - 1
+        return magmom
+
+    def get_magnetic_moments(self, atoms):
+        # local magnetic moments are not available in nwchem
+        # so set the total magnetic moment on the atom no. 0 and fill with 0.0
+        magmoms = [0.0 for a in range(len(atoms))]
+        magmoms[0] = self.get_magnetic_moment(atoms)
+        return np.array(magmoms)
+
+    def get_dipole_moment(self, atoms=None):
+        return self.dipole
+
+    def read_dipole_moment(self):
+        dipolemoment=[]
+        for line in open(self.label + '.out'):
+            for component in [
+                '1   1 0 0',
+                '1   0 1 0',
+                '1   0 0 1'
+                ]:
+                if line.find(component) != -1:
+                    value = float(line.split(component)[1].split()[0])  # total dipole component
+                    value = value * Bohr
+                    dipolemoment.append(value)
+        if len(dipolemoment) == 0:
+            dipolemoment = None
+        return dipolemoment
+
+    def get_potential_energy(self, atoms):
+        # update atoms
+        self.set_atoms(atoms)
+        # if update of energy is neccessary
+        if self.energy is None or self.forces is None:
+            # write input file
+            f = open(self.label + '.nw', 'w')
+            if self.charge is not None:
+                f.write('charge ' + str(self.charge) + '\n')
+            write_nwchem(f, atoms, self.geometry)
+
+            def format_basis_set(string, tag=self.basispar):
+                formatted = tag + '\n'
+                lines = string.split('\n')
+                if len(lines) > 1:
+                    formatted += string
+                else:
+                    formatted += '  * library '  + string + '\n'
+                return formatted + 'end\n'
+            basis = format_basis_set(self.basis)
+            if self.ecp is not None:
+                basis += format_basis_set(self.ecp, 'ecp')
+            if self.so is not None:
+                basis += format_basis_set(self.so, 'so')
+            f.write(basis)
+
+            if self.xc == 'RHF':
+                task = 'scf'
+            else:
+                if self.spinorbit:
+                    task = 'sodft'
+                else:
+                    task = 'dft'
+                nwchem_xc_map = {
+                    'LDA' : 'slater pw91lda',
+                    'PBE' : 'xpbe96 cpbe96',
+                    }
+                if self.xc in nwchem_xc_map:
+                    xc = nwchem_xc_map[self.xc]
+                else:
+                    xc = self.xc
+                f.write('\n' + task + '\n')
+                f.write('  mult ' + str(self.multiplicity) + '\n')
+                f.write('  xc ' + xc + '\n')
+                f.write('  iterations ' + str(self.maxiter) + '\n')
+                for key in self.convergence:
+                    if key == 'lshift':
+                        if self.convergence[key] is not None:
+                            if not (self.convergence[key] > 0.0):
+                                f.write('  convergence nolevelshifting\n')
+                            else:
+                                f.write('  convergence ' + key + ' ' +
+                                        str(self.convergence[key]/Hartree) + '\n')
+                    else:
+                        if self.convergence[key] is not None:
+                            f.write('  convergence ' + key + ' ' +
+                                    str(self.convergence[key]) + '\n')
+                if self.smear is not None:
+                    f.write('  smear ' + str(self.smear) + '\n')
+                if self.grid is not None:
+                    f.write('  grid ' + str(self.grid) + '\n')
+                if self.tolerances is not None:
+                    f.write('  tolerances ' + str(self.tolerances) + '\n')
+                if self.cgmin:
+                    f.write('  cgmin\n')
+                if self.dftcontrol:
+                    f.write(self.dftcontrol + '\n')
+                f.write('end\n')
+
+            if self.control:
+                f.write(self.control + '\n')
+
+#            f.write('\ntask ' + task + ' gradient\n')
+            f.write('\ntask ' + task + ' ' + self.task + '\n')
+            f.close()
+
+            # calculate energy
+            self.output = self.label + '.out'
+            self.run()
+            # read output
+            self.atoms = read_nwchem(self.output)
+            self.read_energy()
+            if self.task.find('gradient') > -1:
+                self.read_forces()
+            self.niter = self.read_number_of_iterations()
+            self.nelect = self.read_number_of_electrons()
+            self.nvector = self.read_number_of_bands()
+            self.magnetic_moment = self.read_magnetic_moment()
+            self.electronic_temperature = self.read_smear() * Hartree
+            self.dipole = self.read_dipole_moment()
+        else:
+            print 'taking old values (E)'
+
+        return self.energy * Hartree
+
+    def read_energy(self):
+        """Read Energy from nwchem output file."""
+        text = open(self.output, 'r').read()
+        lines = iter(text.split('\n'))
+
+        # Energy:
+        for line in lines:
+            estring = 'Total '
+            if self.xc == 'RHF':
+                estring += 'SCF'
+            else:
+                estring += 'DFT'
+            estring += ' energy'
+            if line.find(estring) >=0:
+                energy = float(line.split()[4])
+                break
+        self.energy = energy
+
+        # Eigenstates
+        spin = -1
+        kpts = []
+        for line in lines:
+            if line.find('Molecular Orbital Analysis') >= 0:
+                spin += 1
+                kpts.append(KPoint(spin))
+            if spin >= 0:
+                if line.find('Vector') >= 0:
+                    line = line.lower().replace('d', 'e')
+                    line = line.replace('=', ' ')
+                    word = line.split()
+                    kpts[spin].f_n.append(float(word[3]))
+                    kpts[spin].eps_n.append(float(word[5]))
+        self.kpts = kpts
+
+    def read_forces(self):
+        """Read Forces from nwchem output file."""
+        file = open(self.output, 'r')
+        lines = file.readlines()
+        file.close()
+
+        for i, line in enumerate(lines):
+            if line.find('ENERGY GRADIENTS') >=0:
+                gradients = []
+                for j in range(i + 4, i + 4 + len(self.atoms)):
+                    word = lines[j].split()
+                    gradients.append([float(word[k]) for k in range(5,8)])
+        self.forces =  - np.array(gradients) * Hartree / Bohr
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        """Return eigenvalue array."""
+        return np.array(self.kpts[spin].eps_n) * Hartree
+
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        """Return occupation number array."""
+        return self.kpts[spin].f_n
+
+    def get_number_of_spins(self):
+        """Return the number of spins in the calculation.
+
+        Spin-paired calculations: 1, spin-polarized calculation: 2."""
+        return len(self.kpts)
+
+    def get_spin_polarized(self):
+        """Is it a spin-polarized calculation?"""
+        return len(self.kpts) == 2
+
+    def set_atoms(self, atoms):
+        if self.atoms == atoms:
+            return
+
+        self.atoms = atoms
+        self.energy = None
+        self.forces = None
+
+        if self.multiplicity is None:
+            # obtain multiplicity from magnetic momenta
+            multiplicity = 1 + atoms.get_initial_magnetic_moments().sum()
+            self.multiplicity = int(multiplicity)
+            if self.multiplicity != multiplicity:
+                raise RuntimeError('Noninteger multiplicity not possible.\n' +
+                                   'Check initial magnetic moments.')
+        else:
+            if self.multiplicity != int(self.multiplicity):
+                raise RuntimeError('Noninteger multiplicity not possible.')
+
+    def update(self, atoms):
+        self.set_atoms(atoms)
diff --git a/ase/calculators/siesta.py b/ase/calculators/siesta.py
new file mode 100644
index 0000000..f0f6957
--- /dev/null
+++ b/ase/calculators/siesta.py
@@ -0,0 +1,889 @@
+"""This module defines an ASE interface to SIESTA.
+
+http://www.uam.es/departamentos/ciencias/fismateriac/siesta
+"""
+
+import os
+from os.path import join, isfile, islink, getmtime
+from cmath import exp
+import array
+
+import numpy as np
+
+from ase.data import chemical_symbols
+from ase.units import Rydberg, fs
+from ase.io.siesta import read_rho, read_fdf, read_struct
+from ase.io.cube import read_cube_data
+
+class Siesta:
+    """Class for doing SIESTA calculations.
+
+    The default parameters are very close to those that the SIESTA
+    Fortran code would use.  These are the exceptions::
+
+      calc = Siesta(label='siesta', xc='LDA', pulay=5, mix=0.1)
+
+    Use the set_fdf method to set extra FDF parameters::
+
+      calc.set_fdf('PAO.EnergyShift', 0.01 * Rydberg)
+
+    """
+    def __init__(self, label='siesta', xc='LDA', kpts=None, nbands=None,
+                 width=None, meshcutoff=None, charge=None,
+                 pulay=5, mix=0.1, maxiter=120,
+                 basis=None, ghosts=[],
+                 write_fdf=True):
+        """Construct SIESTA-calculator object.
+
+        Parameters
+        ==========
+        label: str
+            Prefix to use for filenames (label.fdf, label.txt, ...).
+            Default is 'siesta'.
+        xc: str
+            Exchange-correlation functional.  Must be one of LDA, PBE,
+            revPBE, RPBE.
+        kpts: list of three int
+            Monkhost-Pack sampling.
+        nbands: int
+            Number of bands.
+        width: float
+            Fermi-distribution width in eV.
+        meshcutoff: float
+            Cutoff energy in eV for grid.
+        charge: float
+            Total charge of the system.
+        pulay: int
+            Number of old densities to use for Pulay mixing.
+        mix: float
+            Mixing parameter between zero and one for density mixing.
+        write_fdf: bool
+            Use write_fdf=False to use your own fdf-file.
+
+        Examples
+        ========
+        Use default values:
+
+        >>> h = Atoms('H', calculator=Siesta())
+        >>> h.center(vacuum=3.0)
+        >>> e = h.get_potential_energy()
+
+        """
+
+        self.name = 'Siesta'
+        self.label = label#################### != out
+        self.xc = xc
+        self.kpts = kpts
+        self.nbands = nbands
+        self.width = width
+        self.meshcutoff = meshcutoff
+        self.charge = charge
+        self.pulay = pulay
+        self.mix = mix
+        self.maxiter = maxiter
+        self.basis = basis
+        self.ghosts = ghosts
+        self.write_fdf_file = write_fdf
+
+        self.converged = False
+        self.fdf = {}
+        self.e_fermi = None
+
+    def update(self, atoms):
+        if (not self.converged or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.initialize(atoms)
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        self.numbers = atoms.get_atomic_numbers().copy()
+        self.species = []
+        for a, Z in enumerate(self.numbers):
+            if a in self.ghosts:
+                Z = -Z
+            if Z not in self.species:
+                self.species.append(Z)
+
+        if 'SIESTA_PP_PATH' in os.environ:
+            pppaths = os.environ['SIESTA_PP_PATH'].split(':')
+        else:
+            pppaths = []
+
+        for Z in self.species:
+            symbol = chemical_symbols[abs(Z)]
+            name = symbol + '.vps'
+            name1 = symbol + '.psf'
+            found = False
+            for path in pppaths:
+                filename = join(path, name)
+                filename1 = join(path, name1)
+                if isfile(filename) or islink(filename):
+                    found = True
+                    if path != '.':
+                        if islink(name) or isfile(name):
+                            os.remove(name)
+                        os.symlink(filename, name)
+
+                elif isfile(filename1) or islink(filename1):
+                    found = True
+                    if path != '.':
+                        if islink(name1) or isfile(name1):
+                            os.remove(name1)
+                        os.symlink(filename1, name1)
+            if not found:
+                raise RuntimeError('No pseudopotential for %s!' % symbol)
+
+        self.converged = False
+
+    def get_potential_energy(self, atoms, force_consistent=False):
+        self.update(atoms)
+
+        if force_consistent:
+            return self.efree
+        else:
+            # Energy extrapolated to zero Kelvin:
+            return  (self.etotal + self.efree) / 2
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress.copy()
+
+    def get_dipole_moment(self, atoms):
+        """Returns total dipole moment of the system."""
+        self.update(atoms)
+        return self.dipole
+
+    def read_dipole(self):
+        dipolemoment = np.zeros([1, 3])
+        for line in open(self.label + '.txt', 'r'):
+            if line.rfind('Electric dipole (Debye)') > -1:
+                dipolemoment = np.array([float(f) for f in line.split()[5:8]])
+        #debye to e*Ang (the units of VASP)
+        dipolemoment = dipolemoment*0.2081943482534
+        return dipolemoment
+
+    def get_pseudo_density(self, spin=None, pad=True):
+        """Return pseudo-density array.
+
+        If *spin* is not given, then the total density is returned.
+        Otherwise, the spin up or down density is returned (spin=0 or 1).
+        """
+        filename = self.label + '.RHO'
+        if not isfile(filename):
+            raise RuntimeError('Could not find rho-file (make sure to add fdf-option '
+                               '"SaveRho=True" to your calculation)')
+
+        rho = read_rho(filename)
+
+        if spin is None:
+            return rho.sum(axis=3)
+        elif rho.shape[3] != 2:
+            raise RuntimeError('Explicit spin-value requested. '
+                               'Only total density is available.')
+        elif spin == 0 or spin == 1:
+            return rho[:, :, :, spin]
+        else:
+            raise RuntimeError('Invalid spin-value requested. '
+                               'Expected 0 or 1, got %s' % spin)
+
+
+    def get_pseudo_wave_function(self, band=0, kpt=0, spin=None):
+        """Return pseudo-wave-function array.
+
+        The method is limited to the gamma point, and is implemented
+        as a wrapper to denchar (a tool shipped with siesta);
+        denchar must be available in the command path.
+
+        When retrieving a p_w_f from a non-spin-polarized calculation,
+        spin must be None (default), and for spin-polarized
+        calculations, spin must be set to either 0 (up) or 1 (down).
+
+        As long as the necessary files are present and named
+        correctly, old p_w_fs can be read as long as the
+        calculator label is set. E.g.
+
+        >>> c = Siesta(label='name_of_old_calculation')
+        >>> pwf = c.get_pseudo_wave_function()
+
+        The broadcast and pad options are not implemented.
+        """
+
+        # Not implemented: kpt=0, broadcast=True, pad=True
+        # kpoint must be Gamma
+        assert kpt == 0, \
+            "siesta.get_pseudo_wave_function is unfortunately limited " \
+            "to the gamma point only. kpt must be 0."
+
+        # In denchar, band numbering starts from 1
+        assert type(band) is int and band >= 0
+        band = band+1
+
+        if spin is None:
+            spin_name = ""
+        elif spin == 0:
+            spin_name = ".UP"
+        elif spin == 1:
+            spin_name = ".DOWN"
+
+        label = self.label
+        # If <label>.WF<band>.cube already exist and is newer than <label>.fdf,
+        # just return it
+        fn_wf = label+('.WF%i%s.cube'%(band,spin_name))
+        fn_fdf = label+'.fdf'
+        if isfile(fn_wf) and isfile(fn_fdf) and (getmtime(fn_wf) > getmtime(fn_fdf)):
+            x, _ = read_cube_data(fn_wf)
+            return x
+
+        if not isfile(fn_fdf):
+            raise RuntimeError('Could not find the fdf-file. It is required as '
+                               'part of the input for denchar.')
+
+        fdf_mtime = getmtime(fn_fdf)
+        for suf in ['.WFS', '.PLD', '.DM', '.DIM']:
+            if not isfile(label+suf):
+                raise RuntimeError('Could not find file "%s%s" which is required '
+                                   'when extracting wave functions '
+                                   '(make sure the fdf options "WriteDenchar" is '
+                                   'True, and WaveFuncKpoints is [0.0 0.0 0.0]")' %
+                                   (label, suf))
+            if not getmtime(label+suf) > fdf_mtime:
+                # This should be handled in a better way, e.g. by implementing
+                # a "calculation_required() and calculate()"
+                raise RuntimeError('The calculation is not up to date.')
+
+        # Simply read the old fdf-file and pick some meta info from there.
+        # However, strictly it's not always neccesary
+        fdf = read_fdf(fn_fdf)
+        if 'latticeconstant' in fdf:
+            const = float(fdf['latticeconstant'][0])
+            unit =  fdf['latticeconstant'][1]
+        else:
+            const = 1.0
+            unit = 'Ang'
+
+        if 'latticevectors' in fdf:
+            cell = np.array(fdf['latticevectors'], dtype='d')
+        else:
+            raise RuntimeError('Failed to find the lattice vectors in the fdf-file.')
+
+        if 'spinpolarized' in fdf and \
+                fdf['spinpolarized'][0].lower() in ['yes', 'true', '.true.', 'T', '']:
+            if spin is None:
+                raise RuntimeError('The calculation was spin polarized, pick either '
+                                   'spin=0 or 1.')
+        else:
+            if not spin is None:
+                raise RuntimeError('The calculation was not spin polarized, '
+                                   'spin argument must be None.')
+
+        denc_fdf = open(fn_fdf).readlines()
+        denc_fdf.append('Denchar.TypeOfRun 3D\n')
+        denc_fdf.append('Denchar.PlotWaveFunctions T\n')
+        for dim, dir in zip(cell.transpose(), ['X', 'Y', 'Z']):
+            # Naive square box limits to denchar
+            denc_fdf.append('Denchar.Min%s %f %s\n' % (dir, const*dim.min(), unit))
+            denc_fdf.append('Denchar.Max%s %f %s\n' % (dir, const*dim.max(), unit))
+
+        # denchar rewinds stdin and fails if stdin is a pipe
+        denc_fdf_file = open(label+'.denchar.fdf', 'w')
+        denc_fdf_file.write(''.join(denc_fdf))
+        denc_fdf_file.close()
+
+        try:
+            from subprocess import Popen, PIPE
+            p = Popen('denchar', shell=True, stdin=open(label+'.denchar.fdf'),
+                      stdout=PIPE, stderr=PIPE, close_fds=True)
+            exitcode = p.wait()
+        except ImportError:
+            raise RuntimeError('get_pseudo_wave_function implemented only with subprocess.')
+
+        if exitcode == 0:
+            if not isfile(fn_wf):
+                raise RuntimeError('Could not find the requested file (%s)'%fn_wf)
+            x, _ = read_cube_data(fn_wf)
+            return x
+        elif exitcode == 127:
+            raise RuntimeError('No denchar executable found. Make sure it is in the path.')
+        else:
+            import sys
+            print >>sys.stderr, ''.join(p.stderr.readlines())
+            raise RuntimeError('Execution of denchar failed!')
+
+
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+
+        if self.write_fdf_file:
+            self.write_fdf(atoms)
+
+        siesta = os.environ['SIESTA_SCRIPT']
+        locals = {'label': self.label}
+        execfile(siesta, {}, locals)
+        exitcode = locals['exitcode']
+        if exitcode != 0:
+            raise RuntimeError(('Siesta exited with exit code: %d.  ' +
+                                'Check %s.txt for more information.') %
+                               (exitcode, self.label))
+
+        self.dipole = self.read_dipole()
+        self.read()
+
+        atoms_structout = read_struct('%s.STRUCT_OUT' % self.label)
+        atoms.cell = atoms_structout.cell
+        atoms.positions = atoms_structout.positions
+
+        self.converged = True
+
+    def set_fdf(self, key, value):
+        """Set FDF parameter."""
+        self.fdf[key] = value
+
+    def write_fdf(self, atoms):
+        """Write input parameters to fdf-file."""
+        fh = open(self.label + '.fdf', 'w')
+
+        fdf = {
+            'SystemLabel': self.label,
+            'AtomicCoordinatesFormat': 'Ang',
+            'LatticeConstant': 1.0,
+            'NumberOfAtoms': len(atoms),
+            'MeshCutoff': self.meshcutoff,
+            'NetCharge': self.charge,
+            'ElectronicTemperature': self.width,
+            'NumberOfEigenStates': self.nbands,
+            'DM.UseSaveDM': self.converged,
+            'PAO.BasisSize': self.basis,
+            'SolutionMethod': 'diagon',
+            'DM.NumberPulay': self.pulay,
+            'DM.MixingWeight': self.mix,
+            'MaxSCFIterations': self.maxiter
+            }
+
+        if self.xc != 'LDA':
+            fdf['xc.functional'] = 'GGA'
+            fdf['xc.authors'] = self.xc
+
+        magmoms = atoms.get_initial_magnetic_moments()
+        if magmoms.any():
+            fdf['SpinPolarized'] = True
+            fh.write('%block InitSpin\n')
+            for n, M in enumerate(magmoms):
+                if M != 0:
+                    fh.write('%d %.14f\n' % (n + 1, M))
+            fh.write('%endblock InitSpin\n')
+
+        fdf['Number_of_species'] = len(self.species)
+
+        fdf.update(self.fdf)
+
+        for key, value in fdf.items():
+            if value is None:
+                continue
+
+            if isinstance(value, list):
+                fh.write('%%block %s\n' % key)
+                for line in value:
+                    fh.write(line + '\n')
+                fh.write('%%endblock %s\n' % key)
+            else:
+                unit = keys_with_units.get(fdfify(key))
+                if unit is None:
+                    fh.write('%s %s\n' % (key, value))
+                else:
+                    if 'fs**2' in unit:
+                        value /= fs**2
+                    elif 'fs' in unit:
+                        value /= fs
+                    fh.write('%s %f %s\n' % (key, value, unit))
+
+        fh.write('%block LatticeVectors\n')
+        for v in self.cell:
+            fh.write('%.14f %.14f %.14f\n' % tuple(v))
+        fh.write('%endblock LatticeVectors\n')
+
+        fh.write('%block Chemical_Species_label\n')
+        for n, Z in enumerate(self.species):
+            fh.write('%d %s %s\n' % (n + 1, Z, chemical_symbols[abs(Z)]))
+        fh.write('%endblock Chemical_Species_label\n')
+
+        fh.write('%block AtomicCoordinatesAndAtomicSpecies\n')
+        a = 0
+        for pos, Z in zip(self.positions, self.numbers):
+            if a in self.ghosts:
+                Z = -Z
+            a += 1
+            fh.write('%.14f %.14f %.14f' %  tuple(pos))
+            fh.write(' %d\n' % (self.species.index(Z) + 1))
+        fh.write('%endblock AtomicCoordinatesAndAtomicSpecies\n')
+
+        if self.kpts is not None:
+            fh.write('%block kgrid_Monkhorst_Pack\n')
+            for i in range(3):
+                for j in range(3):
+                    if i == j:
+                        fh.write('%d ' % self.kpts[i])
+                    else:
+                        fh.write('0 ')
+                fh.write('%.1f\n' % (((self.kpts[i] + 1) % 2) * 0.5))
+            fh.write('%endblock kgrid_Monkhorst_Pack\n')
+
+        fh.close()
+
+    def read(self):
+        """Read results from SIESTA's text-output file."""
+        text = open(self.label + '.txt', 'r').read().lower()
+        assert 'error' not in text
+        lines = iter(text.split('\n'))
+
+        # Get the number of grid points used:
+        for line in lines:
+            if line.startswith('initmesh: mesh ='):
+                self.grid = [int(word) for word in line.split()[3:8:2]]
+                break
+
+        # Stress (fixed so it's compatible with a MD run from siesta):
+        for line in lines:
+            if line.startswith('siesta: stress tensor '):
+                self.stress = np.empty((3, 3))
+                for i in range(3):
+                    tmp = lines.next().split()
+                    if len(tmp) == 4:
+                        self.stress[i] = [float(word) for word in tmp[1:]]
+                    else:
+                        self.stress[i] = [float(word) for word in tmp]
+                break
+        else:
+            raise RuntimeError
+
+        text = open(self.label + '.txt', 'r').read().lower()
+        lines = iter(text.split('\n'))
+        # Energy (again a fix to make it compatible with a MD run from siesta):
+        counter = 0
+        for line in lines:
+            if line.startswith('siesta: etot    =') and counter == 0:
+                counter += 1
+            elif line.startswith('siesta: etot    ='):
+                self.etotal = float(line.split()[-1])
+                self.efree = float(lines.next().split()[-1])
+                break
+        else:
+            raise RuntimeError
+
+        # Forces (changed so forces smaller than -999eV/A can be fetched):
+        lines = open(self.label + '.FA', 'r').readlines()
+        assert int(lines[0]) == len(self.numbers)
+        assert len(lines) == len(self.numbers) + 1
+        lines = lines[1:]
+        self.forces = np.zeros((len(lines), 3))
+        for i in range(len(lines)):
+            self.forces[i, 0] = float(lines[i][6:18].strip())
+            self.forces[i, 1] = float(lines[i][18:30].strip())
+            self.forces[i, 2] = float(lines[i][30:42].strip())
+
+    def read_eig(self):
+        if self.e_fermi is not None:
+            return
+
+        assert os.access(self.label + '.EIG', os.F_OK)
+        assert os.access(self.label + '.KP', os.F_OK)
+
+        # Read k point weights
+        text = open(self.label + '.KP', 'r').read()
+        lines = text.split('\n')
+        n_kpts = int(lines[0].strip())
+        self.weights = np.zeros((n_kpts,))
+        for i in range(n_kpts):
+            l = lines[i + 1].split()
+            self.weights[i] = float(l[4])
+
+        # Read eigenvalues and fermi-level
+        text = open(self.label+'.EIG','r').read()
+        lines = text.split('\n')
+        self.e_fermi = float(lines[0].split()[0])
+        tmp = lines[1].split()
+        self.n_bands = int(tmp[0])
+        n_spin_bands = int(tmp[1])
+        self.spin_pol = n_spin_bands == 2
+        lines = lines[2:-1]
+        lines_per_kpt = (self.n_bands * n_spin_bands / 10 +
+                         int((self.n_bands * n_spin_bands) % 10 != 0))
+        self.eig = dict()
+        for i in range(len(self.weights)):
+            tmp = lines[i * lines_per_kpt:(i + 1) * lines_per_kpt]
+            v = [float(v) for v in tmp[0].split()[1:]]
+            for l in tmp[1:]:
+                v.extend([float(t) for t in l.split()])
+            if self.spin_pol:
+                self.eig[(i, 0)] = np.array(v[0:self.n_bands])
+                self.eig[(i, 1)] = np.array(v[self.n_bands:])
+            else:
+                self.eig[(i, 0)] = np.array(v)
+
+    def get_k_point_weights(self):
+        self.read_eig()
+        return self.weights
+
+    def get_fermi_level(self):
+        self.read_eig()
+        return self.e_fermi
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        self.read_eig()
+        return self.eig[(kpt, spin)]
+
+    def get_number_of_spins(self):
+        self.read_eig()
+        if self.spin_pol:
+            return 2
+        else:
+            return 1
+
+    def read_hs(self, filename, is_gamma_only=False, magnus=False):
+        """Read the Hamiltonian and overlap matrix from a Siesta
+           calculation in sparse format.
+
+        Parameters
+        ==========
+        filename: str
+            The filename should be on the form jobname.HS
+        is_gamma_only: {False, True), optional
+            Is it a gamma point calculation?
+        magnus: bool
+            The fileformat was changed by Magnus in Siesta at some
+            point around version 2.xxx.
+            Use mangus=False, to use the old file format.
+
+        Note
+        ====
+        Data read in is put in self._dat.
+
+        Examples
+        ========
+            >>> calc = Siesta()
+            >>> calc.read_hs('jobname.HS')
+            >>> print calc._dat.fermi_level
+            >>> print 'Number of orbitals: %i' % calc._dat.nuotot
+        """
+        assert not magnus, 'Not implemented; changes by Magnus to file io'
+        assert not is_gamma_only, 'Not implemented. Only works for k-points.'
+        class Dummy:
+            pass
+        self._dat = dat = Dummy()
+        # Try to read supercell and atom data from a jobname.XV file
+        filename_xv = filename[:-2] + 'XV'
+        #assert isfile(filename_xv), 'Missing jobname.XV file'
+        if isfile(filename_xv):
+            print 'Reading supercell and atom data from ' + filename_xv
+            fd = open(filename_xv, 'r')
+            dat.cell = np.zeros((3, 3)) # Supercell
+            for a_vec in dat.cell:
+                a_vec[:] = np.array(fd.readline().split()[:3], float)
+            dat.rcell = 2 * np.pi * np.linalg.inv(dat.cell.T)
+            dat.natoms = int(fd.readline().split()[0])
+            dat.symbols = []
+            dat.pos_ac = np.zeros((dat.natoms, 3))
+            for a in range(dat.natoms):
+                line = fd.readline().split()
+                dat.symbols.append(chemical_symbols[int(line[1])])
+                dat.pos_ac[a, :] = [float(line[i]) for i in range(2, 2 + 3)]
+        # Read in the jobname.HS file
+        fileobj = file(filename, 'rb')
+        fileobj.seek(0)
+        dat.fermi_level = float(open(filename[:-3] + '.EIG', 'r').readline())
+        dat.is_gammay_only = is_gamma_only
+        dat.nuotot, dat.ns, dat.mnh = getrecord(fileobj, 'l')
+        nuotot, ns, mnh = dat.nuotot, dat.ns, dat.mnh
+        print 'Number of orbitals found: %i' % nuotot
+        dat.numh = numh = np.array([getrecord(fileobj, 'l')
+                                    for i in range(nuotot)], 'l')
+        dat.maxval = max(numh)
+        dat.listhptr = listhptr = np.zeros(nuotot, 'l')
+        listhptr[0] = 0
+        for oi in xrange(1, nuotot):
+            listhptr[oi] = listhptr[oi - 1] + numh[oi - 1]
+        dat.listh = listh = np.zeros(mnh, 'l')
+
+        print 'Reading sparse info'
+        for oi in xrange(nuotot):
+            for mi in xrange(numh[oi]):
+                listh[listhptr[oi] + mi] = getrecord(fileobj, 'l')
+
+        dat.nuotot_sc = max(listh)
+        dat.h_sparse = h_sparse = np.zeros((mnh, ns), float)
+        dat.s_sparse = s_sparse = np.zeros(mnh, float)
+        print 'Reading H'
+        for si in xrange(ns):
+            for oi in xrange(nuotot):
+                for mi in xrange(numh[oi]):
+                    h_sparse[listhptr[oi] + mi, si] = getrecord(fileobj, 'd')
+        print 'Reading S'
+        for oi in xrange(nuotot):
+            for mi in xrange(numh[oi]):
+                s_sparse[listhptr[oi] + mi] = getrecord(fileobj, 'd')
+
+        dat.qtot, dat.temperature = getrecord(fileobj, 'd')
+        if not is_gamma_only:
+            print 'Reading X'
+            dat.xij_sparse = xij_sparse = np.zeros([3, mnh], float)
+            for oi in xrange(nuotot):
+                for mi in xrange(numh[oi]):
+                    xij_sparse[:, listhptr[oi] + mi] = getrecord(fileobj, 'd')
+        fileobj.close()
+
+    def get_hs(self, kpt=(0, 0, 0), spin=0, remove_pbc=None, kpt_scaled=True):
+        """Hamiltonian and overlap matrices for an arbitrary k-point.
+
+        The default values corresponds to the Gamma point for
+        spin 0 and periodic boundary conditions.
+
+        Parameters
+        ==========
+        kpt : {(0, 0, 0), (3,) array_like}, optional
+            k-point in scaled or absolute coordinates.
+            For the latter the units should be Bohr^-1.
+        spin : {0, 1}, optional
+            Spin index
+        remove_pbc : {None, ({'x', 'y', 'z'}, basis)}, optional
+            Use remove_pbc to truncate h and s along a cartesian
+            axis.
+        basis: {str, dict}
+            The basis specification as either a string or a dictionary.
+        kpt_scaled : {True, bool}, optional
+            Use kpt_scaled=False if `kpt` is in absolute units (Bohr^-1).
+
+        Note
+        ====
+        read_hs should be called before get_hs gets called.
+
+        Examples
+        ========
+        >>> calc = Siesta()
+        >>> calc.read_hs('jobname.HS')
+        >>> h, s = calc.get_hs((0.0, 0.375, 0.375))
+        >>> h -= s * calc._dat.fermi_level # fermi level is now at 0.0
+        >>> basis = 'szp'
+        >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))
+        >>> basis = {'Au:'sz}', 'C':'dzp', None:'szp'}
+        >>> h, s = calc.get_hs((0.0, 0.375, 0.375), remove_pbc=('x', basis))
+
+        """
+        if not hasattr(self, '_dat'):# XXX Crude check if data is avail.
+            print 'Please read in data first by calling the method read_hs.'
+            return None, None
+        dot = np.dot
+        dat = self._dat
+        kpt_c = np.array(kpt, float)
+        if kpt_scaled:
+            kpt_c = dot(kpt_c, dat.rcell)
+
+        h_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
+        s_MM = np.zeros((dat.nuotot, dat.nuotot), complex)
+        h_sparse, s_sparse = dat.h_sparse, dat.s_sparse
+        x_sparse = dat.xij_sparse
+        numh, listhptr, listh = dat.numh, dat.listhptr, dat.listh
+        indxuo = np.mod(np.arange(dat.nuotot_sc), dat.nuotot)
+
+        for iuo in xrange(dat.nuotot):
+            for j in range(numh[iuo]):
+                ind =  listhptr[iuo] + j
+                jo = listh[ind] - 1
+                juo = indxuo[jo]
+                kx = dot(kpt_c, x_sparse[:, ind])
+                phasef = exp(1.0j * kx)
+                h_MM[iuo, juo] += phasef * h_sparse[ind, spin]
+                s_MM[iuo, juo] += phasef * s_sparse[ind]
+
+        if remove_pbc is not None:
+            direction, basis = remove_pbc
+            centers_ic = get_bf_centers(dat.symbols, dat.pos_ac, basis)
+            d = 'xyz'.index(direction)
+            cutoff = dat.cell[d, d] * 0.5
+            truncate_along_axis(h_MM, s_MM, direction, centers_ic, cutoff)
+
+        h_MM *= complex(Rydberg)
+        return h_MM, s_MM
+
+
+def getrecord(fileobj, dtype):
+    """Used to read in binary files.
+    """
+    typetosize = {'l':4, 'f':4, 'd':8}# XXX np.int, np.float32, np.float64
+    assert dtype in typetosize # XXX
+    size = typetosize[dtype]
+    record = array.array('l')
+    trunk = array.array(dtype)
+    record.fromfile(fileobj, 1)
+    nofelements = int(record[-1]) / size
+    trunk.fromfile(fileobj, nofelements)
+    record.fromfile(fileobj, 1)
+    data = np.array(trunk, dtype=dtype)
+    if len(data)==1:
+        data = data[0]
+    return data
+
+def truncate_along_axis(h, s, direction, centers_ic, cutoff):
+    """Truncate h and s such along a cartesian axis.
+
+    Parameters:
+
+    h: (N, N) ndarray
+        Hamiltonian matrix.
+    s: (N, N) ndarray
+        Overlap matrix.
+    direction: {'x', 'y', 'z'}
+        Truncate allong a cartesian axis.
+    centers_ic: (N, 3) ndarray
+        Centers of the basis functions.
+    cutoff: float
+        The (direction-axis projected) cutoff distance.
+    """
+    dtype = h.dtype
+    ni = len(centers_ic)
+    d = 'xyz'.index(direction)
+    pos_i = centers_ic[:, d]
+    for i in range(ni):
+        dpos_i = abs(pos_i - pos_i[i])
+        mask_i = (dpos_i < cutoff).astype(dtype)
+        h[i, :] *= mask_i
+        h[:, i] *= mask_i
+        s[i, :] *= mask_i
+        s[:, i] *= mask_i
+
+def get_nao(symbol, basis):
+    """Number of basis functions.
+
+    Parameters
+    ==========
+    symbol: str
+        The chemical symbol.
+    basis: str
+        Basis function type.
+    """
+    ls = valence_config[symbol]
+    nao = 0
+    zeta = {'s':1, 'd':2, 't':3, 'q':4}
+    nzeta = zeta[basis[0]]
+    is_pol = 'p' in basis
+    for l in ls:
+        nao += (2 * l + 1) * nzeta
+    if is_pol:
+        l_pol = None
+        l = -1
+        while l_pol is None:
+            l += 1
+            if not l in ls:
+                l_pol = l
+        nao += 2 * l_pol + 1
+    return nao
+
+def get_bf_centers(symbols, positions, basis):
+    """Centers of basis functions.
+
+    Parameters
+    ==========
+    symbols: str, (N, ) array_like
+        chemical symbol for each atom.
+    positions: float, (N, 3) array_like
+        Positions of the atoms.
+    basis: {str,  dict}
+        Basis set specification as either a string or a dictionary
+
+    Examples
+    ========
+    >>> symbols = ['O', 'H']
+    >>> positions = [(0, 0, 0), (0, 0, 1)]
+    >>> basis = 'sz'
+    >>> print get_bf_centers(symbols, positions, basis)
+    [[0 0 0]
+     [0 0 0]
+     [0 0 0]
+     [0 0 0]
+     [0 0 1]]
+    >>> basis = {'H':'dz', None:'sz'}
+    >>> print get_bf_centers(symbols, positions, basis)
+    [[0 0 0]
+     [0 0 0]
+     [0 0 0]
+     [0 0 0]
+     [0 0 1]
+     [0 0 1]]
+
+    """
+    centers_ic = []
+    dict_basis = False
+    if type(basis)==dict:
+        dict_basis = True
+    for symbol, pos in zip(symbols, positions):
+        if dict_basis:
+            if symbol not in basis:
+                bas = basis[None]
+            else:
+                bas = basis[symbol]
+        else:
+            bas = basis
+        for i in range(get_nao(symbol, bas)):
+            centers_ic.append(pos)
+    return np.asarray(centers_ic)
+
+def fdfify(key):
+    return key.lower().replace('_', '').replace('.', '').replace('-', '')
+
+valence_config = {
+    'H': (0,),
+    'C': (0, 1),
+    'N': (0, 1),
+    'O': (0, 1),
+    'S': (0, 1),
+    'Li': (0,),
+    'Na': (0,),
+    'Ni': (0, 2),
+    'Cu': (0, 2),
+    'Pd': (0, 2),
+    'Ag': (0, 2),
+    'Pt': (0, 2),
+    'Au': (0, 2)}
+
+keys_with_units = {
+    'paoenergyshift': 'eV',
+    'zmunitslength': 'Bohr',
+    'zmunitsangle': 'rad',
+    'zmforcetollength': 'eV/Ang',
+    'zmforcetolangle': 'eV/rad',
+    'zmmaxdispllength': 'Ang',
+    'zmmaxdisplangle': 'rad',
+    'meshcutoff': 'eV',
+    'dmenergytolerance': 'eV',
+    'electronictemperature': 'eV',
+    'oneta': 'eV',
+    'onetaalpha': 'eV',
+    'onetabeta': 'eV',
+    'onrclwf': 'Ang',
+    'onchemicalpotentialrc': 'Ang',
+    'onchemicalpotentialtemperature': 'eV',
+    'mdmaxcgdispl': 'Ang',
+    'mdmaxforcetol': 'eV/Ang',
+    'mdmaxstresstol': 'eV/Ang**3',
+    'mdlengthtimestep': 'fs',
+    'mdinitialtemperature': 'eV',
+    'mdtargettemperature': 'eV',
+    'mdtargetpressure': 'eV/Ang**3',
+    'mdnosemass': 'eV*fs**2',
+    'mdparrinellorahmanmass': 'eV*fs**2',
+    'mdtaurelax': 'fs',
+    'mdbulkmodulus': 'eV/Ang**3',
+    'mdfcdispl': 'Ang',
+    'warningminimumatomicdistance': 'Ang',
+    'rcspatial': 'Ang',
+    'kgridcutoff': 'Ang',
+    'latticeconstant': 'Ang'}
diff --git a/ase/calculators/singlepoint.py b/ase/calculators/singlepoint.py
new file mode 100644
index 0000000..504b2f1
--- /dev/null
+++ b/ase/calculators/singlepoint.py
@@ -0,0 +1,132 @@
+import numpy as np
+
+
+class SinglePointCalculator:
+    """Special calculator for a single configuration.
+
+    Used to remember the energy, force and stress for a given
+    configuration.  If the positions, atomic numbers, unit cell, or
+    boundary conditions are changed, then asking for
+    energy/forces/stress will raise an exception."""
+    
+    def __init__(self, energy, forces, stress, magmoms, atoms):
+        """Save energy, forces and stresses for the current configuration."""
+        self.energy = energy
+        if forces is not None:
+            forces = np.array(forces, float)
+        self.forces = forces
+        if stress is not None:
+            stress = np.array(stress, float)
+        self.stress = stress
+        if magmoms is not None:
+            magmoms = np.array(magmoms, float)
+        self.magmoms = magmoms
+        self.atoms = atoms.copy()
+
+    def calculation_required(self, atoms, quantities):
+        ok = self.atoms == atoms
+        return ('forces' in quantities and (self.forces is None or not ok) or
+                'energy' in quantities and (self.energy is None or not ok) or
+                'stress' in quantities and (self.stress is None or not ok) or
+                'magmoms' in quantities and (self.magmoms is None or not ok))
+
+    def update(self, atoms):
+        if self.atoms != atoms:
+            raise RuntimeError('Energy, forces and stress no longer correct.')
+
+    def get_potential_energy(self, atoms=None):
+        if atoms is not None:
+            self.update(atoms)
+        if self.energy is None:
+            raise RuntimeError('No energy.')
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        if self.forces is None:
+            raise RuntimeError('No forces.')
+        return self.forces
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        if self.stress is None:
+            raise NotImplementedError
+        return self.stress
+
+    def get_spin_polarized(self):
+        return self.magmoms is not None and self.magmoms.any()
+
+    def get_magnetic_moments(self, atoms=None):
+        if atoms is not None:
+            self.update(atoms)
+        if self.magmoms is not None:
+            return self.magmoms
+        else:
+            return np.zeros(len(self.positions))
+
+class SinglePointKPoint:
+    def __init__(self, kpt, spin):
+        self.k = kpt
+        self.s = spin
+        self.eps_n = []
+        self.f_n = []
+
+class SinglePointDFTCalculator(SinglePointCalculator):
+    def __init__(self, energy, forces, stress, magmoms, atoms,
+                 eFermi=None):
+        SinglePointCalculator.__init__(self, energy, forces, stress, 
+                                       magmoms, atoms)
+        if eFermi is not None:
+            self.eFermi = eFermi
+        self.kpts = None
+
+    def get_fermi_level(self):
+        """Return the Fermi-level(s)."""
+        return self.eFermi
+
+    def get_bz_k_points(self):
+        """Return the k-points."""
+        if self.kpts is not None:
+            # we assume that only the gamma point is defined
+            return np.zeros((1, 3))
+        return None
+
+    def get_number_of_spins(self):
+        """Return the number of spins in the calculation.
+
+        Spin-paired calculations: 1, spin-polarized calculation: 2."""
+        if self.kpts is not None:
+            # we assume that only the gamma point is defined
+            return len(self.kpts)
+        return None
+
+    def get_spin_polarized(self):
+        """Is it a spin-polarized calculation?"""
+        nos = self.get_number_of_spins()
+        if nos is not None:
+            return nos == 2
+        return None
+    
+    def get_ibz_k_points(self):
+        """Return k-points in the irreducible part of the Brillouin zone."""
+        return self.get_bz_k_points()
+
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        """Return occupation number array."""
+        # we assume that only the gamma point is defined
+        assert(kpt == 0)
+        if self.kpts is not None:
+            for kpt in self.kpts:
+                if kpt.s == spin:
+                    return kpt.f_n
+        return None
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        """Return eigenvalue array."""
+        # we assume that only the gamma point is defined
+        assert(kpt == 0)
+        if self.kpts is not None:
+            for kpt in self.kpts:
+                if kpt.s == spin:
+                    return kpt.eps_n
+        return None
diff --git a/ase/calculators/test.py b/ase/calculators/test.py
new file mode 100644
index 0000000..93ecb5a
--- /dev/null
+++ b/ase/calculators/test.py
@@ -0,0 +1,167 @@
+from math import pi, ceil
+
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.parallel import world, rank, distribute_cpus
+try:
+    from gpaw.mpi import SerialCommunicator
+except:
+    pass
+
+def make_test_dft_calculation():
+    a = b = 2.0
+    c = 6.0
+    atoms = Atoms(positions=[(0, 0, c / 2)],
+                  symbols='H',
+                  pbc=(1, 1, 0),
+                  cell=(a, b, c),
+                  calculator=TestCalculator())
+    return atoms
+
+
+class TestCalculator:
+    def __init__(self, nk=8):
+        assert nk % 2 == 0
+        bzk = []
+        weights = []
+        ibzk = []
+        w = 1.0 / nk**2
+        for i in range(-nk + 1, nk, 2):
+            for j in range(-nk + 1, nk, 2):
+                k = (0.5 * i / nk, 0.5 * j / nk, 0)
+                bzk.append(k)
+                if i >= j > 0:
+                    ibzk.append(k)
+                    if i == j:
+                        weights.append(4 * w)
+                    else:
+                        weights.append(8 * w)
+        assert abs(sum(weights) - 1.0) < 1e-12
+        self.bzk = np.array(bzk)
+        self.ibzk = np.array(ibzk)
+        self.weights = np.array(weights)
+
+        # Calculate eigenvalues and wave functions:
+        self.init()
+
+    def init(self):
+        nibzk = len(self.weights)
+        nbands = 1
+
+        V = -1.0
+        self.eps = 2 * V * (np.cos(2 * pi * self.ibzk[:, 0]) +
+                            np.cos(2 * pi * self.ibzk[:, 1]))
+        self.eps.shape = (nibzk, nbands)
+
+        self.psi = np.zeros((nibzk, 20, 20, 60), complex)
+        phi = np.empty((2, 2, 20, 20, 60))
+        z = np.linspace(-1.5, 1.5, 60, endpoint=False)
+        for i in range(2):
+            x = np.linspace(0, 1, 20, endpoint=False) - i
+            for j in range(2):
+                y = np.linspace(0, 1, 20, endpoint=False) - j
+                r = (((x[:, None]**2 +
+                       y**2)[:, :, None] +
+                      z**2)**0.5).clip(0, 1)
+                phi = 1.0 - r**2 * (3.0 - 2.0 * r)
+                phase = np.exp(pi * 2j * np.dot(self.ibzk, (i, j, 0)))
+                self.psi += phase[:, None, None, None] * phi
+
+    def get_pseudo_wave_function(self, band=0, kpt=0, spin=0):
+        assert spin == 0 and band == 0
+        return self.psi[kpt]
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        assert spin == 0
+        return self.eps[kpt]
+
+    def get_number_of_bands(self):
+        return 1
+
+    def get_k_point_weights(self):
+        return self.weights
+
+    def get_number_of_spins(self):
+        return 1
+
+    def get_fermi_level(self):
+        return 0.0
+
+
+class TestPotential:
+    def get_forces(self, atoms):
+        E = 0.0
+        R = atoms.positions
+        F = np.zeros_like(R)
+        for a, r in enumerate(R):
+            D = R - r
+            d = (D**2).sum(1)**0.5
+            x = d - 1.0
+            E += np.vdot(x, x)
+            d[a] = 1
+            F -= (x / d)[:, None] * D
+        self.energy = 0.25 * E
+        return F
+
+    def get_potential_energy(self, atoms):
+        self.get_forces(atoms)
+        return self.energy
+
+    def get_stress(self, atoms):
+        raise NotImplementedError
+
+
+def numeric_force(atoms, a, i, d=0.001):
+    """Evaluate force along i'th axis on a'th atom using finite difference.
+
+    This will trigger two calls to get_potential_energy(), with atom a moved
+    plus/minus d in the i'th axial direction, respectively.
+    """
+    p0 = atoms.positions[a, i]
+    atoms.positions[a, i] += d
+    eplus = atoms.get_potential_energy()
+    atoms.positions[a, i] -= 2 * d
+    eminus = atoms.get_potential_energy()
+    atoms.positions[a, i] = p0
+    return (eminus - eplus) / (2 * d)
+
+
+def numeric_forces(atoms, indices=None, axes=(0, 1, 2), d=0.001,
+                   parallel=None):
+    """Evaluate finite-difference forces on several atoms.
+
+    Returns an array of forces for each specified atomic index and
+    each specified axis, calculated using finite difference on each
+    atom and direction separately.  Array has same shape as if
+    returned from atoms.get_forces(); uncalculated elements are zero.
+
+    Calculates all forces by default."""
+
+    if indices is None:
+        indices = range(len(atoms))
+    F_ai = np.zeros_like(atoms.positions)
+    n = len(indices) * len(axes)
+    if parallel is None:
+        atom_tasks = [atoms] * n
+        master = True
+    else:
+        calc_comm, tasks_comm, tasks_rank = distribute_cpus(parallel, world)
+        master = calc_comm.rank == 0 
+        calculator = atoms.get_calculator()
+        calculator.set(communicator=calc_comm)
+        atom_tasks = [None] * n
+        for i in range(n):
+            if ((i - tasks_rank) % tasks_comm.size) == 0:
+                atom_tasks[i] = atoms
+    for ia, a in enumerate(indices):
+        for ii, i in enumerate(axes):
+            atoms = atom_tasks[ia * len(axes) + ii]
+            if atoms is not None:
+                print '# rank', rank, 'calculating atom', a, 'xyz'[i]
+                force = numeric_force(atoms, a, i, d)
+                if master:
+                    F_ai[a, i] = force
+    if parallel is not None:
+        world.sum(F_ai)
+    return F_ai
diff --git a/ase/calculators/tip3p.py b/ase/calculators/tip3p.py
new file mode 100644
index 0000000..4a2a302
--- /dev/null
+++ b/ase/calculators/tip3p.py
@@ -0,0 +1,180 @@
+"""TIP3P potential, constraints and dynamics."""
+from math import pi, sin, cos
+
+import numpy as np
+
+import ase.units as units
+from ase.parallel import world
+from ase.md.md import MolecularDynamics
+
+qH = 0.417
+sigma0 = 3.15061
+epsilon0 = 0.1521 * units.kcal / units.mol
+rOH = 0.9572
+thetaHOH = 104.52 / 180 * pi
+
+
+class TIP3P:
+    def __init__(self, rc=9.0, width=1.0):
+        self.energy = None
+        self.forces = None
+
+        self.rc1 = rc - width
+        self.rc2 = rc
+        
+    def get_spin_polarized(self):
+        return False
+    
+    def update(self, atoms):
+        if (self.energy is None or
+            len(self.numbers) != len(atoms) or
+            (self.numbers != atoms.get_atomic_numbers()).any()):
+            self.calculate(atoms)
+        elif ((self.positions != atoms.get_positions()).any() or
+              (self.pbc != atoms.get_pbc()).any() or
+              (self.cell != atoms.get_cell()).any()):
+            self.calculate(atoms)
+
+    def calculation_required(self, atoms, quantities):
+        if len(quantities) == 0:
+            return False
+
+        return (self.energy is None or
+                len(self.numbers) != len(atoms) or
+                (self.numbers != atoms.get_atomic_numbers()).any() or
+                (self.positions != atoms.get_positions()).any() or
+                (self.pbc != atoms.get_pbc()).any() or
+                (self.cell != atoms.get_cell()).any())
+                
+    def get_potential_energy(self, atoms):
+        self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces.copy()
+    
+    def get_stress(self, atoms):
+        raise NotImplementedError
+    
+    def calculate(self, atoms):
+        self.positions = atoms.get_positions().copy()
+        self.cell = atoms.get_cell().copy()
+        self.pbc = atoms.get_pbc().copy()
+        natoms = len(atoms)
+        nH2O = natoms // 3
+
+        assert self.pbc.all()
+        C = self.cell.diagonal()
+        assert not (self.cell - np.diag(C)).any()
+        assert (C >= 2 * self.rc2).all()
+        self.numbers = atoms.get_atomic_numbers()
+        Z = self.numbers.reshape((-1, 3))
+        assert (Z[:, 1:] == 1).all() and (Z[:, 0] == 8).all()
+
+        R = self.positions.reshape((nH2O, 3, 3))
+        RO = R[:, 0]
+        
+        self.energy = 0.0
+        self.forces = np.zeros((natoms, 3))
+        
+        if world.size == 1:
+            mya = range(nH2O - 1)
+        else:
+            rank = world.rank
+            size = world.size
+            assert nH2O // (2 * size) == 0
+            mynH2O = nH2O // 2 // size
+            mya = (range(rank * n, (rank + 1) * n) +
+                   range((size - rank - 1) * n, (size - rank) * n))
+
+        q = np.empty(3)
+        q[:] = qH * (units.Hartree * units.Bohr)**0.5
+        q[0] *= -2
+        
+        for a in mya:
+            DOO = (RO[a + 1:] - RO[a] + 0.5 * C) % C - 0.5 * C
+            dOO = (DOO**2).sum(axis=1)**0.5
+            x1 = dOO > self.rc1
+            x2 = dOO < self.rc2
+            f = np.zeros(nH2O - a - 1)
+            f[x2] = 1.0
+            dfdd = np.zeros(nH2O - a - 1)
+            x12 = np.logical_and(x1, x2)
+            d = (dOO[x12] - self.rc1) / (self.rc2 - self.rc1)
+            f[x12] -= d**2 * (3.0 - 2.0 * d)
+            dfdd[x12] -= 6.0 / (self.rc2 - self.rc1) * d * (1.0 - d)
+
+            y = (sigma0 / dOO)**6
+            y2 = y**2
+            e = 4 * epsilon0 * (y2 - y)
+            self.energy += np.dot(e, f)
+            dedd = 24 * epsilon0 * (2 * y2 - y) / dOO * f - e * dfdd
+            F = (dedd / dOO)[:, np.newaxis] * DOO
+            self.forces[(a + 1) * 3::3] += F
+            self.forces[a * 3] -= F.sum(axis=0)
+
+            for i in range(3):
+                D = (R[a + 1:] - R[a, i] + 0.5 * C) % C - 0.5 * C
+                d = (D**2).sum(axis=2)**0.5
+                e = q[i] * q / d
+                self.energy += np.dot(f, e).sum()
+                F = (e / d**2 * f[:, np.newaxis])[:, :, np.newaxis] * D
+                F[:, 0] -= (e.sum(axis=1) * dfdd / dOO)[:, np.newaxis] * DOO 
+                self.forces[(a + 1) * 3:] += F.reshape((-1, 3))
+                self.forces[a * 3 + i] -= F.sum(axis=0).sum(axis=0)
+
+        self.energy = world.sum(self.energy)
+        world.sum(self.forces)
+
+
+class H2OConstraint:
+    """Constraint object for a rigid H2O molecule."""
+    def __init__(self, r=rOH, theta=thetaHOH, iterations=23, masses=None):
+        self.r = r
+        self.theta = theta
+        self.iterations = iterations
+        self.m = masses
+
+    def set_masses(self, masses):
+        self.m = masses
+
+    def adjust_positions(self, old, new):
+        bonds = [(0, 1, self.r), (0, 2, self.r)]
+        if self.theta:
+            bonds.append((1, 2, sin(self.theta / 2) * self.r * 2))
+        for iter in range(self.iterations):
+            for i, j, r in bonds:
+                D = old[i::3] - old[j::3]
+                m1 = self.m[i]
+                m2 = self.m[j]
+                a = new[i::3]
+                b = new[j::3]
+                B = a - b
+                x = (D**2).sum(axis=1)
+                y = (D * B).sum(axis=1)
+                z = (B**2).sum(axis=1) - r**2
+                k = m1 * m2 / (m1 + m2) * ((y**2 - x * z)**0.5 - y) / x
+                k.shape = (-1, 1)
+                a += k / m1 * D
+                b -= k / m2 * D
+
+    def adjust_forces(self, positions, forces):
+        pass
+        
+    def copy(self):
+        return H2OConstraint(self.r, self.theta, self.iterations, self.m)
+
+
+class Verlet(MolecularDynamics):
+    def step(self, f):
+        atoms = self.atoms
+        m = atoms.get_masses()[:, np.newaxis]
+        v = self.atoms.get_velocities()
+        r0 = atoms.get_positions()
+        r = r0 + self.dt * v + self.dt**2 * f / m
+        atoms.set_positions(r)
+        r = atoms.get_positions()
+        v = (r - r0) / self.dt
+        self.atoms.set_velocities(v)
+        return atoms.get_forces()
diff --git a/ase/calculators/turbomole.py b/ase/calculators/turbomole.py
new file mode 100644
index 0000000..2425b0d
--- /dev/null
+++ b/ase/calculators/turbomole.py
@@ -0,0 +1,156 @@
+"""This module defines an ASE interface to Turbomole
+
+http://www.turbomole.com/
+"""
+import os
+import sys
+
+import numpy as np
+
+from ase.units import Hartree, Bohr
+from ase.io.turbomole import write_turbomole
+from ase.calculators.general import Calculator
+
+
+class Turbomole(Calculator):
+    def __init__(self, label='turbomole',
+                 calculate_energy='dscf', calculate_forces='grad',
+                 post_HF = False):
+        self.label = label
+        self.converged = False
+        
+        # set calculators for energy and forces
+        self.calculate_energy = calculate_energy
+        self.calculate_forces = calculate_forces
+
+        # turbomole has no stress
+        self.stress = np.empty((3, 3))
+        
+        # storage for energy and forces
+        self.e_total = None
+        self.forces = None
+        self.updated = False
+        
+        # atoms must be set
+        self.atoms = None
+        
+        # POST-HF method 
+        self.post_HF  = post_HF
+
+    def initialize(self, atoms):
+        self.numbers = atoms.get_atomic_numbers().copy()
+        self.species = []
+        for a, Z in enumerate(self.numbers):
+            self.species.append(Z)
+        self.converged = False
+        
+    def execute(self, command):
+        from subprocess import Popen, PIPE
+        try:
+            # the sub process gets started here
+            proc = Popen([command], shell=True, stderr=PIPE)
+            error = proc.communicate()[1]
+            # check the error output
+            if 'abnormally' in error:
+                raise OSError(error)
+            print 'TM command: ', command, 'successfully executed'
+        except OSError, e:
+            print >> sys.stderr, 'Execution failed:', e
+            sys.exit(1)
+
+    def get_potential_energy(self, atoms):
+        # update atoms
+        self.set_atoms(atoms)
+        # if update of energy is neccessary
+        if self.update_energy:
+            # calculate energy
+            self.execute(self.calculate_energy + ' > ASE.TM.energy.out')
+            # check for convergence of dscf cycle
+            if os.path.isfile('dscf_problem'):
+                print 'Turbomole scf energy calculation did not converge'
+                raise RuntimeError(
+                    'Please run Turbomole define and come thereafter back')
+            # read energy
+            self.read_energy()
+        else:
+            print 'taking old values (E)'
+        self.update_energy = False
+        return self.e_total
+
+    def get_forces(self, atoms):
+        # update atoms
+        self.set_atoms(atoms)
+        # complete energy calculations
+        if self.update_energy:
+            self.get_potential_energy(atoms)
+        # if update of forces is neccessary
+        if self.update_forces:
+            # calculate forces
+            self.execute(self.calculate_forces + ' > ASE.TM.forces.out')
+            # read forces
+            self.read_forces()
+        else:
+            print 'taking old values (F)'
+        self.update_forces = False
+        return self.forces.copy()
+    
+    def get_stress(self, atoms):
+        return self.stress
+        
+    def set_atoms(self, atoms):
+        if self.atoms == atoms:
+            return
+        # performs an update of the atoms 
+        super(Turbomole, self).set_atoms(atoms)
+        write_turbomole('coord', atoms)
+        # energy and forces must be re-calculated
+        self.update_energy = True
+        self.update_forces = True
+        
+    def read_energy(self):
+        """Read Energy from Turbomole energy file."""
+        text = open('energy', 'r').read().lower()
+        lines = iter(text.split('\n'))
+
+        # Energy:
+        for line in lines:
+            if line.startswith('$end'):
+                break
+            elif line.startswith('$'):
+                pass
+            else:
+                energy_tmp = float(line.split()[1])
+                if self.post_HF:
+                    energy_tmp += float(line.split()[4])
+        # update energy units
+        self.e_total = energy_tmp * Hartree
+
+    def read_forces(self):
+        """Read Forces from Turbomole gradient file."""
+        file = open('gradient', 'r')
+        lines = file.readlines()
+        file.close()
+
+        forces = np.array([[0, 0, 0]])
+        
+        nline = len(lines)
+        iline = -1
+        
+        for i in range(nline):
+            if 'cycle' in lines[i]:
+                iline = i
+        
+        if iline < 0:
+            raise RuntimeError('Please check TURBOMOLE gradients')
+
+        # next line
+        iline += len(self.atoms) + 1
+        # $end line
+        nline -= 1
+        # read gradients
+        for i in xrange(iline, nline):
+            line = lines[i].replace('D', 'E')
+            tmp = np.array([[float(f) for f in line.split()[0:3]]])
+            forces = np.concatenate((forces, tmp))  
+        # Note the '-' sign for turbomole, to get forces
+        self.forces = (-np.delete(forces, np.s_[0:1], axis=0)) * Hartree / Bohr
diff --git a/ase/calculators/vasp.py b/ase/calculators/vasp.py
new file mode 100644
index 0000000..f4cd1e9
--- /dev/null
+++ b/ase/calculators/vasp.py
@@ -0,0 +1,1655 @@
+# Copyright (C) 2008 CSC - Scientific Computing Ltd.
+"""This module defines an ASE interface to VASP.
+
+Developed on the basis of modules by Jussi Enkovaara and John
+Kitchin.  The path of the directory containing the pseudopotential
+directories (potpaw,potpaw_GGA, potpaw_PBE, ...) should be set
+by the environmental flag $VASP_PP_PATH.
+
+The user should also set the environmental flag $VASP_SCRIPT pointing
+to a python script looking something like::
+
+   import os
+   exitcode = os.system('vasp')
+
+Alternatively, user can set the environmental flag $VASP_COMMAND pointing
+to the command use the launch vasp e.g. 'vasp' or 'mpirun -n 16 vasp'
+
+http://cms.mpi.univie.ac.at/vasp/
+
+-Jonas Bjork j.bjork at liverpool.ac.uk
+"""
+
+import os
+import sys
+import re
+from general import Calculator
+from os.path import join, isfile, islink
+
+import numpy as np
+
+import ase
+
+# Parameters that can be set in INCAR. The values which are None
+# are not written and default parameters of VASP are used for them.
+
+float_keys = [
+    'aexx',       # Fraction of exact/DFT exchange
+    'aggac',      # Fraction of gradient correction to correlation
+    'aggax',      # Fraction of gradient correction to exchange
+    'aldac',      # Fraction of LDA correlation energy
+    'amin',       #
+    'amix',       #
+    'amix_mag',   #
+    'bmix',       # tags for mixing
+    'bmix_mag',   #
+    'deper',      # relative stopping criterion for optimization of eigenvalue
+    'ebreak',     # absolute stopping criterion for optimization of eigenvalues (EDIFF/N-BANDS/4)
+    'emax',       # energy-range for DOSCAR file
+    'emin',       #
+    'enaug',      # Density cutoff
+    'encut',      # Planewave cutoff
+    'encutfock',  # FFT grid in the HF related routines
+    'hfscreen',   # attribute to change from PBE0 to HSE
+    'potim',      # time-step for ion-motion (fs)
+    'nelect',     # total number of electrons
+    'param1',     # Exchange parameter 
+    'param2',     # Exchange parameter 
+    'pomass',     # mass of ions in am
+    'sigma',      # broadening in eV
+    'time',       # special control tag
+    'weimin',     # maximum weight for a band to be considered empty
+    'zab_vdw',    # vdW-DF parameter
+    'zval',       # ionic valence
+#The next keywords pertain to the VTST add-ons from Graeme Henkelman's group at UT Austin
+    'jacobian',   # Weight of lattice to atomic motion
+    'ddr',        # (DdR) dimer separation
+    'drotmax',    # (DRotMax) number of rotation steps per translation step
+    'dfnmin',     # (DFNMin) rotational force below which dimer is not rotated
+    'dfnmax',     # (DFNMax) rotational force below which dimer rotation stops
+    'stol',       # convergence ratio for minimum eigenvalue
+    'sdr',        # finite difference for setting up Lanczos matrix and step size when translating
+    'maxmove',    # Max step for translation for IOPT > 0
+    'invcurve',   # Initial curvature for LBFGS (IOPT = 1)
+    'timestep',   # Dynamical timestep for IOPT = 3 and IOPT = 7
+    'sdalpha',    # Ratio between force and step size for IOPT = 4
+#The next keywords pertain to IOPT = 7 (i.e. FIRE)
+    'ftimemax',   # Max time step
+    'ftimedec',   # Factor to dec. dt
+    'ftimeinc',   # Factor to inc. dt
+    'falpha',     # Parameter for velocity damping
+    'falphadec',  # Factor to dec. alpha
+]
+
+exp_keys = [
+    'ediff',      # stopping-criterion for electronic upd.
+    'ediffg',     # stopping-criterion for ionic upd.
+    'symprec',    # precession in symmetry routines
+#The next keywords pertain to the VTST add-ons from Graeme Henkelman's group at UT Austin
+    'fdstep',     # Finite diference step for IOPT = 1 or 2
+]
+
+string_keys = [
+    'algo',       # algorithm: Normal (Davidson) | Fast | Very_Fast (RMM-DIIS)
+    'gga',        # xc-type: PW PB LM or 91
+    'prec',       # Precission of calculation (Low, Normal, Accurate)
+    'system',     # name of System
+    'tebeg',      #
+    'teend',      # temperature during run
+]
+
+int_keys = [
+    'ialgo',      # algorithm: use only 8 (CG) or 48 (RMM-DIIS)
+    'ibrion',     # ionic relaxation: 0-MD 1-quasi-New 2-CG
+    'icharg',     # charge: 0-WAVECAR 1-CHGCAR 2-atom 10-const
+    'idipol',     # monopol/dipol and quadropole corrections
+    'iniwav',     # initial electr wf. : 0-lowe 1-rand
+    'isif',       # calculate stress and what to relax
+    'ismear',     # part. occupancies: -5 Blochl -4-tet -1-fermi 0-gaus >0 MP
+    'ispin',      # spin-polarized calculation
+    'istart',     # startjob: 0-new 1-cont 2-samecut
+    'isym',       # symmetry: 0-nonsym 1-usesym 2-usePAWsym
+    'iwavpr',     # prediction of wf.: 0-non 1-charg 2-wave 3-comb
+    'ldauprint',  # 0-silent, 1-occ. matrix written to OUTCAR, 2-1+pot. matrix written
+    'ldautype',   # L(S)DA+U: 1-Liechtenstein 2-Dudarev 4-Liechtenstein(LDAU)
+    'lmaxmix',    #
+    'lorbit',     # create PROOUT
+    'maxmix',     #
+    'ngx',        # FFT mesh for wavefunctions, x
+    'ngxf',       # FFT mesh for charges x
+    'ngy',        # FFT mesh for wavefunctions, y
+    'ngyf',       # FFT mesh for charges y
+    'ngz',        # FFT mesh for wavefunctions, z
+    'ngzf',       # FFT mesh for charges z
+    'nbands',     # Number of bands
+    'nblk',       # blocking for some BLAS calls (Sec. 6.5)
+    'nbmod',      # specifies mode for partial charge calculation
+    'nelm',       # nr. of electronic steps (default 60)
+    'nelmdl',     # nr. of initial electronic steps
+    'nelmin',
+    'nfree',      # number of steps per DOF when calculting Hessian using finitite differences
+    'nkred',      # define sub grid of q-points for HF with nkredx=nkredy=nkredz
+    'nkredx',      # define sub grid of q-points in x direction for HF
+    'nkredy',      # define sub grid of q-points in y direction for HF
+    'nkredz',      # define sub grid of q-points in z direction for HF
+    'npar',       # parallelization over bands
+    'nsim',       # evaluate NSIM bands simultaneously if using RMM-DIIS
+    'nsw',        # number of steps for ionic upd.
+    'nupdown',    # fix spin moment to specified value
+    'nwrite',     # verbosity write-flag (how much is written)
+    'smass',      # Nose mass-parameter (am)
+    'vdwgr',      # extra keyword for Andris program
+    'vdwrn',      # extra keyword for Andris program
+    'voskown',    # use Vosko, Wilk, Nusair interpolation
+#The next keywords pertain to the VTST add-ons from Graeme Henkelman's group at UT Austin
+    'ichain',     # Flag for controlling which method is being used (0=NEB, 1=DynMat, 2=Dimer, 3=Lanczos)
+                  # if ichain > 3, then both IBRION and POTIM are automatically set in the INCAR file
+    'iopt',       # Controls which optimizer to use.  for iopt > 0, ibrion = 3 and potim = 0.0
+    'snl',        # Maximum dimentionality of the Lanczos matrix
+    'lbfgsmem',   # Steps saved for inverse Hessian for IOPT = 1 (LBFGS)
+    'fnmin',      # Max iter. before adjusting dt and alpha for IOPT = 7 (FIRE) 
+]
+
+bool_keys = [
+    'addgrid',    # finer grid for augmentation charge density
+    'laechg',     # write AECCAR0/AECCAR1/AECCAR2
+    'lasph',      # non-spherical contributions to XC energy (and pot for VASP.5.X)
+    'lasync',     # overlap communcation with calculations
+    'lcharg',     #
+    'lcorr',      # Harris-correction to forces
+    'ldau',       # L(S)DA+U
+    'ldiag',      # algorithm: perform sub space rotation
+    'ldipol',     # potential correction mode
+    'lelf',       # create ELFCAR
+    'lhfcalc',    # switch to turn on Hartree Fock calculations
+    'loptics',    # calculate the frequency dependent dielectric matrix
+    'lpard',      # evaluate partial (band and/or k-point) decomposed charge density
+    'lplane',     # parallelisation over the FFT grid
+    'lscalapack', # switch off scaLAPACK
+    'lscalu',     # switch of LU decomposition
+    'lsepb',      # write out partial charge of each band seperately?
+    'lsepk',      # write out partial charge of each k-point seperately?
+    'lthomas',    #
+    'luse_vdw',   # Invoke vdW-DF implementation by Klimes et. al 
+    'lvhar',      # write Hartree potential to LOCPOT (vasp 5.x)
+    'lvtot',      # create WAVECAR/CHGCAR/LOCPOT
+    'lwave',      #
+#The next keywords pertain to the VTST add-ons from Graeme Henkelman's group at UT Austin
+    'lclimb',     # Turn on CI-NEB
+    'ltangentold', # Old central difference tangent
+    'ldneb',      # Turn on modified double nudging
+    'lnebcell',   # Turn on SS-NEB
+    'lglobal',    # Optmizize NEB globally for LBFGS (IOPT = 1)
+    'llineopt',   # Use force based line minimizer for translation (IOPT = 1)
+]
+
+list_keys = [
+    'dipol',      # center of cell for dipol
+    'eint',       # energy range to calculate partial charge for
+    'ferwe',      # Fixed band occupation (spin-paired)
+    'ferdo',      # Fixed band occupation (spin-plarized)
+    'iband',      # bands to calculate partial charge for
+    'magmom',     # initial magnetic moments
+    'kpuse',      # k-point to calculate partial charge for
+    'ropt',       # number of grid points for non-local proj in real space
+    'rwigs',      # Wigner-Seitz radii
+    'ldauu',      # ldau parameters, has potential to redundant w.r.t. dict
+    'ldaul',      # key 'ldau_luj', but 'ldau_luj' can't be read direct from
+    'ldauj',      # the INCAR (since it needs to know information about atomic
+                  # species. In case of conflict 'ldau_luj' gets written out
+                  # when a calculation is set up
+]
+
+special_keys = [
+    'lreal',      # non-local projectors in real space
+]
+
+dict_keys = [
+    'ldau_luj',   # dictionary with L(S)DA+U parameters, e.g. {'Fe':{'L':2, 'U':4.0, 'J':0.9}, ...}
+]
+
+keys = [
+    # 'NBLOCK' and KBLOCK       inner block; outer block
+    # 'NPACO' and APACO         distance and nr. of slots for P.C.
+    # 'WEIMIN, EBREAK, DEPER    special control tags
+]
+
+class Vasp(Calculator):
+    def __init__(self, restart=None, output_template='vasp', track_output=False,
+                 **kwargs):
+        self.name = 'Vasp'
+        self.float_params = {}
+        self.exp_params = {}
+        self.string_params = {}
+        self.int_params = {}
+        self.bool_params = {}
+        self.list_params = {}
+        self.special_params = {}
+        self.dict_params = {}
+        for key in float_keys:
+            self.float_params[key] = None
+        for key in exp_keys:
+            self.exp_params[key] = None
+        for key in string_keys:
+            self.string_params[key] = None
+        for key in int_keys:
+            self.int_params[key] = None
+        for key in bool_keys:
+            self.bool_params[key] = None
+        for key in list_keys:
+            self.list_params[key] = None
+        for key in special_keys:
+            self.special_params[key] = None
+        for key in dict_keys:
+            self.dict_params[key] = None
+
+        self.string_params['prec'] = 'Normal'
+
+        if kwargs.get('xc', None):
+            if kwargs['xc'] not in ['PW91','LDA','PBE']:
+                raise ValueError(
+                    '%s not supported for xc! use one of: PW91, LDA or PBE.' %
+                    kwargs['xc'])
+            self.input_params = {'xc': kwargs['xc']} # exchange correlation functional
+        else:
+            self.input_params = {'xc': 'PW91'} # exchange correlation functional
+        self.input_params.update({
+            'setups': None,    # Special setups (e.g pv, sv, ...)
+            'txt':    '-',     # Where to send information
+            'kpts':   (1,1,1), # k-points
+            'gamma':  False,   # Option to use gamma-sampling instead
+                               # of Monkhorst-Pack
+            })
+
+        self.restart = restart
+        self.track_output = track_output
+        self.output_template = output_template
+        if restart:
+            self.restart_load()
+            return
+
+        self.nbands = self.int_params['nbands']
+        self.atoms = None
+        self.positions = None
+        self.run_counts = 0
+        self.set(**kwargs)
+
+    def set(self, **kwargs):
+        for key in kwargs:
+            if self.float_params.has_key(key):
+                self.float_params[key] = kwargs[key]
+            elif self.exp_params.has_key(key):
+                self.exp_params[key] = kwargs[key]
+            elif self.string_params.has_key(key):
+                self.string_params[key] = kwargs[key]
+            elif self.int_params.has_key(key):
+                self.int_params[key] = kwargs[key]
+            elif self.bool_params.has_key(key):
+                self.bool_params[key] = kwargs[key]
+            elif self.list_params.has_key(key):
+                self.list_params[key] = kwargs[key]
+            elif self.special_params.has_key(key):
+                self.special_params[key] = kwargs[key]
+            elif self.dict_params.has_key(key):
+                self.dict_params[key] = kwargs[key]
+            elif self.input_params.has_key(key):
+                self.input_params[key] = kwargs[key]
+            else:
+                raise TypeError('Parameter not defined: ' + key)
+
+    def update(self, atoms):
+        if self.calculation_required(atoms, ['energy']):
+            if (self.atoms is None or
+                self.atoms.positions.shape != atoms.positions.shape):
+                # Completely new calculation just reusing the same
+                # calculator, so delete any old VASP files found.
+                self.clean()
+            self.calculate(atoms)
+
+    def initialize(self, atoms):
+        """Initialize a VASP calculation
+
+        Constructs the POTCAR file (does not actually write it).
+        User should specify the PATH
+        to the pseudopotentials in VASP_PP_PATH environment variable"""
+
+        p = self.input_params
+
+        self.all_symbols = atoms.get_chemical_symbols()
+        self.natoms = len(atoms)
+        self.spinpol = atoms.get_initial_magnetic_moments().any()
+        atomtypes = atoms.get_chemical_symbols()
+
+        # Determine the number of atoms of each atomic species
+        # sorted after atomic species
+        special_setups = []
+        symbols = {}
+        if self.input_params['setups']:
+            for m in self.input_params['setups']:
+                try :
+                    #special_setup[self.input_params['setups'][m]] = int(m)
+                    special_setups.append(int(m))
+                except:
+                    #print 'setup ' + m + ' is a groups setup'
+                    continue
+            #print 'special_setups' , special_setups
+
+        for m,atom in enumerate(atoms):
+            symbol = atom.symbol
+            if m in special_setups:
+                pass
+            else:
+                if not symbols.has_key(symbol):
+                    symbols[symbol] = 1
+                else:
+                    symbols[symbol] += 1
+
+        # Build the sorting list
+        self.sort = []
+        self.sort.extend(special_setups)
+
+        for symbol in symbols:
+            for m,atom in enumerate(atoms):
+                if m in special_setups:
+                    pass
+                else:
+                    if atom.symbol == symbol:
+                        self.sort.append(m)
+        self.resort = range(len(self.sort))
+        for n in range(len(self.resort)):
+            self.resort[self.sort[n]] = n
+        self.atoms_sorted = atoms[self.sort]
+
+        # Check if the necessary POTCAR files exists and
+        # create a list of their paths.
+        self.symbol_count = []
+        for m in special_setups:
+            self.symbol_count.append([atomtypes[m],1])
+        for m in symbols:
+            self.symbol_count.append([m,symbols[m]])
+        #print 'self.symbol_count',self.symbol_count
+        sys.stdout.flush()
+        xc = '/'
+        #print 'p[xc]',p['xc']
+        if p['xc'] == 'PW91':
+            xc = '_gga/'
+        elif p['xc'] == 'PBE':
+            xc = '_pbe/'
+        if 'VASP_PP_PATH' in os.environ:
+            pppaths = os.environ['VASP_PP_PATH'].split(':')
+        else:
+            pppaths = []
+        self.ppp_list = []
+        # Setting the pseudopotentials, first special setups and
+        # then according to symbols
+        for m in special_setups:
+            name = 'potpaw'+xc.upper() + p['setups'][str(m)] + '/POTCAR'
+            found = False
+            for path in pppaths:
+                filename = join(path, name)
+                #print 'filename', filename
+                if isfile(filename) or islink(filename):
+                    found = True
+                    self.ppp_list.append(filename)
+                    break
+                elif isfile(filename + '.Z') or islink(filename + '.Z'):
+                    found = True
+                    self.ppp_list.append(filename+'.Z')
+                    break
+            if not found:
+                raise RuntimeError('No pseudopotential for %s!' % symbol)
+        #print 'symbols', symbols
+        for symbol in symbols:
+            try:
+                name = 'potpaw'+xc.upper()+symbol + p['setups'][symbol]
+            except (TypeError, KeyError):
+                name = 'potpaw' + xc.upper() + symbol
+            name += '/POTCAR'
+            found = False
+            for path in pppaths:
+                filename = join(path, name)
+                #print 'filename', filename
+                if isfile(filename) or islink(filename):
+                    found = True
+                    self.ppp_list.append(filename)
+                    break
+                elif isfile(filename + '.Z') or islink(filename + '.Z'):
+                    found = True
+                    self.ppp_list.append(filename+'.Z')
+                    break
+            if not found:
+
+                raise RuntimeError('No pseudopotential for %s!' % symbol)
+        self.converged = None
+        self.setups_changed = None
+        
+
+    def calculate(self, atoms):
+        """Generate necessary files in the working directory and run VASP.
+
+        The method first write VASP input files, then calls the method
+        which executes VASP. When the VASP run is finished energy, forces,
+        etc. are read from the VASP output.
+        """
+
+        # Initialize calculations
+        self.initialize(atoms)
+
+        # Write input
+        from ase.io.vasp import write_vasp
+        write_vasp('POSCAR', self.atoms_sorted, symbol_count = self.symbol_count)
+        self.write_incar(atoms)
+        self.write_potcar()
+        self.write_kpoints()
+        self.write_sort_file()
+
+        # Execute VASP
+        self.run()
+        # Read output
+        atoms_sorted = ase.io.read('CONTCAR', format='vasp')
+        if self.int_params['ibrion']>-1 and self.int_params['nsw']>0:
+            # Update atomic positions and unit cell with the ones read from CONTCAR.
+            atoms.positions = atoms_sorted[self.resort].positions
+            atoms.cell = atoms_sorted.cell
+        self.converged = self.read_convergence()
+        self.set_results(atoms)
+
+    def set_results(self, atoms):
+        self.read(atoms)
+        if self.spinpol:
+            self.magnetic_moment = self.read_magnetic_moment()
+            if self.int_params['lorbit']>=10 or (self.int_params['lorbit']!=None and self.list_params['rwigs']):
+                self.magnetic_moments = self.read_magnetic_moments(atoms)
+            else:
+                self.magnetic_moments = None
+        self.old_float_params = self.float_params.copy()
+        self.old_exp_params = self.exp_params.copy()
+        self.old_string_params = self.string_params.copy()
+        self.old_int_params = self.int_params.copy()
+        self.old_input_params = self.input_params.copy()
+        self.old_bool_params = self.bool_params.copy()
+        self.old_list_params = self.list_params.copy()
+        self.old_dict_params = self.dict_params.copy()
+        self.atoms = atoms.copy()
+        self.name = 'vasp'
+        self.version = self.read_version()
+        self.niter = self.read_number_of_iterations()
+        self.sigma = self.read_electronic_temperature()
+        self.nelect = self.read_number_of_electrons()
+
+    def run(self):
+        """Method which explicitely runs VASP."""
+
+        if self.track_output:
+            self.out = self.output_template+str(self.run_counts)+'.out'
+            self.run_counts += 1
+        else:
+            self.out = self.output_template+'.out'
+        stderr = sys.stderr
+        p=self.input_params
+        if p['txt'] is None:
+            sys.stderr = devnull
+        elif p['txt'] == '-':
+            pass
+        elif isinstance(p['txt'], str):
+            sys.stderr = open(p['txt'], 'w')
+        if os.environ.has_key('VASP_COMMAND'):
+            vasp = os.environ['VASP_COMMAND']
+            exitcode = os.system('%s > %s' % (vasp, self.out))
+        elif os.environ.has_key('VASP_SCRIPT'):
+            vasp = os.environ['VASP_SCRIPT']
+            locals={}
+            execfile(vasp, {}, locals)
+            exitcode = locals['exitcode']
+        else:
+            raise RuntimeError('Please set either VASP_COMMAND or VASP_SCRIPT environment variable')
+        sys.stderr = stderr
+        if exitcode != 0:
+            raise RuntimeError('Vasp exited with exit code: %d.  ' % exitcode)
+
+    def restart_load(self):
+        """Method which is called upon restart."""
+
+        # Try to read sorting file
+        if os.path.isfile('ase-sort.dat'):
+            self.sort = []
+            self.resort = []
+            file = open('ase-sort.dat', 'r')
+            lines = file.readlines()
+            file.close()
+            for line in lines:
+                data = line.split()
+                self.sort.append(int(data[0]))
+                self.resort.append(int(data[1]))
+            atoms = ase.io.read('CONTCAR', format='vasp')[self.resort]
+        else:
+            atoms = ase.io.read('CONTCAR', format='vasp')
+            self.sort = range(len(atoms))
+            self.resort = range(len(atoms))
+        self.atoms = atoms.copy()
+        self.read_incar()
+        self.read_outcar()
+        self.set_results(atoms)
+        self.read_kpoints()
+        self.read_potcar()
+#        self.old_incar_params = self.incar_params.copy()
+        self.old_input_params = self.input_params.copy()
+        self.converged = self.read_convergence()
+
+    def clean(self):
+        """Method which cleans up after a calculation.
+
+        The default files generated by Vasp will be deleted IF this
+        method is called.
+
+        """
+        files = ['CHG', 'CHGCAR', 'POSCAR', 'INCAR', 'CONTCAR',
+                 'DOSCAR', 'EIGENVAL', 'IBZKPT', 'KPOINTS', 'OSZICAR',
+                 'OUTCAR', 'PCDAT', 'POTCAR', 'vasprun.xml',
+                 'WAVECAR', 'XDATCAR', 'PROCAR', 'ase-sort.dat',
+                 'LOCPOT', 'AECCAR0', 'AECCAR1', 'AECCAR2']
+        for f in files:
+            try:
+                os.remove(f)
+            except OSError:
+                pass
+
+    def set_atoms(self, atoms):
+        if (atoms != self.atoms):
+            self.converged = None
+        self.atoms = atoms.copy()
+
+    def get_atoms(self):
+        atoms = self.atoms.copy()
+        atoms.set_calculator(self)
+        return atoms
+
+    def get_name(self):
+        return self.name
+
+    def get_version(self):
+        self.update(self.atoms)
+        return self.version
+
+    def read_version(self):
+        version = None
+        for line in open('OUTCAR'):
+            if line.find(' vasp.') != -1: # find the first occurence
+                version = line[len(' vasp.'):].split()[0]
+                break
+        return version
+
+    def get_potential_energy(self, atoms, force_consistent=False):
+        self.update(atoms)
+        if force_consistent:
+            return self.energy_free
+        else:
+            return self.energy_zero
+
+    def get_number_of_iterations(self):
+        self.update(self.atoms)
+        return self.niter
+
+    def read_number_of_iterations(self):
+        niter = None
+        for line in open('OUTCAR'):
+            if line.find('- Iteration') != -1: # find the last iteration number
+                niter = int(line.split(')')[0].split('(')[-1].strip())
+        return niter
+
+    def get_electronic_temperature(self):
+        self.update(self.atoms)
+        return self.sigma
+
+    def read_electronic_temperature(self):
+        sigma = None
+        for line in open('OUTCAR'):
+            if line.find('Fermi-smearing in eV        SIGMA') != -1:
+                sigma = float(line.split('=')[1].strip())
+        return sigma
+
+    def get_default_number_of_electrons(self, filename='POTCAR'):
+        """Get list of tuples (atomic symbol, number of valence electrons)
+        for each atomtype from a POTCAR file.  """
+        return self.read_default_number_of_electrons(filename)
+
+    def read_default_number_of_electrons(self, filename='POTCAR'):
+        nelect = []
+        lines = open(filename).readlines()
+        for n, line in enumerate(lines):
+            if line.find('TITEL') != -1:
+                symbol = line.split('=')[1].split()[1].split('_')[0].strip()
+                valence = float(lines[n+4].split(';')[1].split('=')[1].split()[0].strip())
+                nelect.append((symbol, valence))
+        return nelect
+
+    def get_number_of_electrons(self):
+        self.update(self.atoms)
+        return self.nelect
+
+    def read_number_of_electrons(self):
+        nelect = None
+        for line in open('OUTCAR'):
+            if line.find('total number of electrons') != -1:
+                nelect = float(line.split('=')[1].split()[0].strip())
+        return nelect
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces
+
+    def get_stress(self, atoms):
+        self.update(atoms)
+        return self.stress
+
+    def read_stress(self):
+        stress = None
+        for line in open('OUTCAR'):
+            if line.find(' in kB  ') != -1:
+                stress = -np.array([float(a) for a in line.split()[2:]]) \
+                         [[0, 1, 2, 4, 5, 3]] \
+                         * 1e-1 * ase.units.GPa
+        return stress
+
+    def read_ldau(self):
+        ldau_luj = None
+        ldauprint = None
+        ldau = None
+        ldautype = None
+        atomtypes = []
+        # read ldau parameters from outcar
+        for line in open('OUTCAR'):
+            if line.find('TITEL') != -1:    # What atoms are present
+                atomtypes.append(line.split()[3].split('_')[0].split('.')[0])
+            if line.find('LDAUTYPE') != -1: # Is this a DFT+U calculation
+                ldautype = int(line.split('=')[-1])
+                ldau = True
+                ldau_luj = {}
+            if line.find('LDAUL') != -1:
+                L = line.split('=')[-1].split()
+            if line.find('LDAUU') != -1:
+                U = line.split('=')[-1].split()
+            if line.find('LDAUJ') != -1:
+                J = line.split('=')[-1].split()
+        # create dictionary
+        if ldau:
+            for i,symbol in enumerate(atomtypes):
+                ldau_luj[symbol] = {'L': int(L[i]), 'U': float(U[i]), 'J': float(J[i])}
+            self.dict_params['ldau_luj'] = ldau_luj
+        return ldau, ldauprint, ldautype, ldau_luj
+
+    def calculation_required(self, atoms, quantities):
+        if (self.positions is None or
+            (self.atoms != atoms) or
+            (self.float_params != self.old_float_params) or
+            (self.exp_params != self.old_exp_params) or
+            (self.string_params != self.old_string_params) or
+            (self.int_params != self.old_int_params) or
+            (self.bool_params != self.old_bool_params) or
+            (self.list_params != self.old_list_params) or
+            (self.input_params != self.old_input_params) or
+            (self.dict_params != self.old_dict_params)
+            or not self.converged):
+            return True
+        if 'magmom' in quantities:
+            return not hasattr(self, 'magnetic_moment')
+        return False
+
+    def get_number_of_bands(self):
+        return self.nbands
+
+    def get_k_point_weights(self):
+        self.update(self.atoms)
+        return self.read_k_point_weights()
+
+    def get_number_of_spins(self):
+        return 1 + int(self.spinpol)
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        self.update(self.atoms)
+        return self.read_eigenvalues(kpt, spin)
+
+    def get_occupation_numbers(self, kpt=0, spin=0):
+        self.update(self.atoms)
+        return self.read_occupation_numbers(kpt, spin)
+
+    def get_fermi_level(self):
+        return self.fermi
+
+    def get_number_of_grid_points(self):
+        raise NotImplementedError
+
+    def get_pseudo_density(self):
+        raise NotImplementedError
+
+    def get_pseudo_wavefunction(self, n=0, k=0, s=0, pad=True):
+        raise NotImplementedError
+
+    def get_bz_k_points(self):
+        raise NotImplementedError
+
+    def get_ibz_kpoints(self):
+        self.update(self.atoms)
+        return self.read_ibz_kpoints()
+
+    def get_ibz_k_points(self):
+        return self.get_ibz_kpoints()
+
+    def get_spin_polarized(self):
+        if not hasattr(self, 'spinpol'):
+            self.spinpol = self.atoms.get_initial_magnetic_moments().any()
+        return self.spinpol
+
+    def get_magnetic_moment(self, atoms):
+        self.update(atoms)
+        return self.magnetic_moment
+
+    def get_magnetic_moments(self, atoms):
+        if self.int_params['lorbit']>=10 or self.list_params['rwigs']:
+            self.update(atoms)
+            return self.magnetic_moments
+        else:
+            return None
+        #raise RuntimeError(
+        #        "The combination %s for lorbit with %s for rwigs not supported to calculate magnetic moments" % (p['lorbit'], p['rwigs']))
+
+    def get_dipole_moment(self, atoms):
+        """Returns total dipole moment of the system."""
+        self.update(atoms)
+        return self.dipole
+
+    def get_xc_functional(self):
+        return self.input_params['xc']
+
+    def write_incar(self, atoms, **kwargs):
+        """Writes the INCAR file."""
+        incar = open('INCAR', 'w')
+        incar.write('INCAR created by Atomic Simulation Environment\n')
+        for key, val in self.float_params.items():
+            if val is not None:
+                incar.write(' %s = %5.6f\n' % (key.upper(), val))
+        for key, val in self.exp_params.items():
+            if val is not None:
+                incar.write(' %s = %5.2e\n' % (key.upper(), val))
+        for key, val in self.string_params.items():
+            if val is not None:
+                incar.write(' %s = %s\n' % (key.upper(), val))
+        for key, val in self.int_params.items():
+            if val is not None:
+                incar.write(' %s = %d\n' % (key.upper(), val))
+                if key == 'ichain' and val > 0:
+                    incar.write(' IBRION = 3\n POTIM = 0.0\n')
+                    for key, val in self.int_params.items():
+                        if key == 'iopt' and val == None:
+                            print 'WARNING: optimization is set to LFBGS (IOPT = 1)'
+                            incar.write(' IOPT = 1\n')
+                    for key, val in self.exp_params.items():
+                        if key == 'ediffg' and val == None:
+                            RuntimeError('Please set EDIFFG < 0')
+        for key, val in self.list_params.items():
+            if val is not None:
+                incar.write(' %s = ' % key.upper())
+                if key in ('dipol', 'eint', 'ropt', 'rwigs'):
+                    [incar.write('%.4f ' % x) for x in val]
+                elif key in ('ldauu', 'ldauj', 'ldaul') and \
+                    not self.dict_keys.has('ldau_luj'):
+                    [incar.write('%.4f ' % x) for x in val]
+                elif key in ('ferwe', 'ferdo'):
+                    [incar.write('%.1f ' % x) for x in val]
+                elif key in ('iband', 'kpuse'):
+                    [incar.write('%i ' % x) for x in val]
+                elif key == 'magmom':
+                    list = [[1, val[0]]]
+                    for n in range(1, len(val)):
+                        if val[n] == val[n-1]:
+                            list[-1][0] += 1
+                        else:
+                            list.append([1, val[n]])
+                    [incar.write('%i*%.4f ' % (mom[0], mom[1])) for mom in list]
+                incar.write('\n')
+        for key, val in self.bool_params.items():
+            if val is not None:
+                incar.write(' %s = ' % key.upper())
+                if val:
+                    incar.write('.TRUE.\n')
+                else:
+                    incar.write('.FALSE.\n')
+        for key, val in self.special_params.items():
+            if val is not None:
+                incar.write(' %s = ' % key.upper())
+                if key == 'lreal':
+                    if type(val) == str:
+                        incar.write(val+'\n')
+                    elif type(val) == bool:
+                       if val:
+                           incar.write('.TRUE.\n')
+                       else:
+                           incar.write('.FALSE.\n')
+        for key, val in self.dict_params.items():
+            if val is not None:
+                if key == 'ldau_luj':
+                    llist = ulist = jlist = ''
+                    for symbol in self.symbol_count:
+                        luj = val.get(symbol[0], {'L':-1, 'U': 0.0, 'J': 0.0}) # default: No +U
+                        llist += ' %i' % luj['L']
+                        ulist += ' %.3f' % luj['U']
+                        jlist += ' %.3f' % luj['J']
+                    incar.write(' LDAUL =%s\n' % llist)
+                    incar.write(' LDAUU =%s\n' % ulist)
+                    incar.write(' LDAUJ =%s\n' % jlist)
+        if self.spinpol:
+            if not self.int_params['ispin']:
+                incar.write(' ispin = 2\n'.upper())
+            # Write out initial magnetic moments
+            magmom = atoms.get_initial_magnetic_moments()[self.sort]
+            list = [[1, magmom[0]]]
+            for n in range(1, len(magmom)):
+                if magmom[n] == magmom[n-1]:
+                    list[-1][0] += 1
+                else:
+                    list.append([1, magmom[n]])
+            incar.write(' magmom = '.upper())
+            [incar.write('%i*%.4f ' % (mom[0], mom[1])) for mom in list]
+            incar.write('\n')
+        incar.close()
+
+    def write_kpoints(self, **kwargs):
+        """Writes the KPOINTS file."""
+        p = self.input_params
+        kpoints = open('KPOINTS', 'w')
+        kpoints.write('KPOINTS created by Atomic Simulation Environemnt\n')
+        shape=np.array(p['kpts']).shape
+        if len(shape)==1:
+            kpoints.write('0\n')
+            if p['gamma']:
+                kpoints.write('Gamma\n')
+            else:
+                kpoints.write('Monkhorst-Pack\n')
+            [kpoints.write('%i ' % kpt) for kpt in p['kpts']]
+            kpoints.write('\n0 0 0\n')
+        elif len(shape)==2:
+            kpoints.write('%i \n' % (len(p['kpts'])))
+            kpoints.write('Cartesian\n')
+            for n in range(len(p['kpts'])):
+                [kpoints.write('%f ' % kpt) for kpt in p['kpts'][n]]
+                if shape[1]==4:
+                    kpoints.write('\n')
+                elif shape[1]==3:
+                    kpoints.write('1.0 \n')
+        kpoints.close()
+
+    def write_potcar(self,suffix = ""):
+        """Writes the POTCAR file."""
+        import tempfile
+        potfile = open('POTCAR'+suffix,'w')
+        for filename in self.ppp_list:
+            if filename.endswith('R'):
+                for line in open(filename, 'r'):
+                    potfile.write(line)
+            elif filename.endswith('.Z'):
+                file_tmp = tempfile.NamedTemporaryFile()
+                os.system('gunzip -c %s > %s' % (filename, file_tmp.name))
+                for line in file_tmp.readlines():
+                    potfile.write(line)
+                file_tmp.close()
+        potfile.close()
+
+    def write_sort_file(self):
+        """Writes a sortings file.
+
+        This file contains information about how the atoms are sorted in
+        the first column and how they should be resorted in the second
+        column. It is used for restart purposes to get sorting right
+        when reading in an old calculation to ASE."""
+
+        file = open('ase-sort.dat', 'w')
+        for n in range(len(self.sort)):
+            file.write('%5i %5i \n' % (self.sort[n], self.resort[n]))
+
+    # Methods for reading information from OUTCAR files:
+    def read_energy(self, all=None):
+        [energy_free, energy_zero]=[0, 0]
+        if all:
+            energy_free = []
+            energy_zero = []
+        for line in open('OUTCAR', 'r'):
+            # Free energy
+            if line.lower().startswith('  free  energy   toten'):
+                if all:
+                    energy_free.append(float(line.split()[-2]))
+                else:
+                    energy_free = float(line.split()[-2])
+            # Extrapolated zero point energy
+            if line.startswith('  energy  without entropy'):
+                if all:
+                    energy_zero.append(float(line.split()[-1]))
+                else:
+                    energy_zero = float(line.split()[-1])
+        return [energy_free, energy_zero]
+
+    def read_forces(self, atoms, all=False):
+        """Method that reads forces from OUTCAR file.
+
+        If 'all' is switched on, the forces for all ionic steps
+        in the OUTCAR file be returned, in other case only the
+        forces for the last ionic configuration is returned."""
+
+        file = open('OUTCAR','r')
+        lines = file.readlines()
+        file.close()
+        n=0
+        if all:
+            all_forces = []
+        for line in lines:
+            if line.rfind('TOTAL-FORCE') > -1:
+                forces=[]
+                for i in range(len(atoms)):
+                    forces.append(np.array([float(f) for f in lines[n+2+i].split()[3:6]]))
+                if all:
+                    all_forces.append(np.array(forces)[self.resort])
+            n+=1
+        if all:
+            return np.array(all_forces)
+        else:
+            return np.array(forces)[self.resort]
+
+    def read_fermi(self):
+        """Method that reads Fermi energy from OUTCAR file"""
+        E_f=None
+        for line in open('OUTCAR', 'r'):
+            if line.rfind('E-fermi') > -1:
+                E_f=float(line.split()[2])
+        return E_f
+
+    def read_dipole(self):
+        dipolemoment=np.zeros([1,3])
+        for line in open('OUTCAR', 'r'):
+            if line.rfind('dipolmoment') > -1:
+                dipolemoment=np.array([float(f) for f in line.split()[1:4]])
+        return dipolemoment
+
+    def read_magnetic_moments(self, atoms):
+        magnetic_moments = np.zeros(len(atoms))
+        n = 0
+        lines = open('OUTCAR', 'r').readlines()
+        for line in lines:
+            if line.rfind('magnetization (x)') > -1:
+                for m in range(len(atoms)):
+                    magnetic_moments[m] = float(lines[n + m + 4].split()[4])
+            n += 1
+        return np.array(magnetic_moments)[self.resort]
+
+    def read_magnetic_moment(self):
+        n=0
+        for line in open('OUTCAR','r'):
+            if line.rfind('number of electron  ') > -1:
+                magnetic_moment=float(line.split()[-1])
+            n+=1
+        return magnetic_moment
+
+    def read_nbands(self):
+        for line in open('OUTCAR', 'r'):
+            if line.rfind('NBANDS') > -1:
+                return int(line.split()[-1])
+
+    def read_convergence(self):
+        """Method that checks whether a calculation has converged."""
+        converged = None
+        # First check electronic convergence
+        for line in open('OUTCAR', 'r'):
+            if line.rfind('EDIFF  ') > -1:
+                ediff = float(line.split()[2])
+            if line.rfind('total energy-change')>-1:
+                split = line.split(':')
+                a = float(split[1].split('(')[0])
+                b = float(split[1].split('(')[1][0:-2])
+                if [abs(a), abs(b)] < [ediff, ediff]:
+                    converged = True
+                else:
+                    converged = False
+                    continue
+        # Then if ibrion > 0, check whether ionic relaxation condition been fulfilled
+        if self.int_params['ibrion'] > 0:
+            if not self.read_relaxed():
+                converged = False
+            else:
+                converged = True
+        return converged
+
+    def read_ibz_kpoints(self):
+        lines = open('OUTCAR', 'r').readlines()
+        ibz_kpts = []
+        n = 0
+        i = 0
+        for line in lines:
+            if line.rfind('Following cartesian coordinates')>-1:
+                m = n+2
+                while i==0:
+                    ibz_kpts.append([float(lines[m].split()[p]) for p in range(3)])
+                    m += 1
+                    if lines[m]==' \n':
+                        i = 1
+            if i == 1:
+                continue
+            n += 1
+        ibz_kpts = np.array(ibz_kpts)
+        return np.array(ibz_kpts)
+
+    def read_k_point_weights(self):
+        file = open('IBZKPT')
+        lines = file.readlines()
+        file.close()
+        kpt_weights = []
+        for n in range(3, len(lines)):
+            kpt_weights.append(float(lines[n].split()[3]))
+        kpt_weights = np.array(kpt_weights)
+        kpt_weights /= np.sum(kpt_weights)
+        return kpt_weights
+
+    def read_eigenvalues(self, kpt=0, spin=0):
+        file = open('EIGENVAL', 'r')
+        lines = file.readlines()
+        file.close()
+        eigs = []
+        for n in range(8+kpt*(self.nbands+2), 8+kpt*(self.nbands+2)+self.nbands):
+            eigs.append(float(lines[n].split()[spin+1]))
+        return np.array(eigs)
+
+    def read_occupation_numbers(self, kpt=0, spin=0):
+        lines = open('OUTCAR').readlines()
+        nspins = self.get_number_of_spins()
+        start = 0
+        if nspins == 1:
+            for n, line in enumerate(lines): # find it in the last iteration
+                m = re.search(' k-point *'+str(kpt+1)+' *:', line)
+                if m is not None:
+                    start = n
+        else:
+            for n, line in enumerate(lines):
+                if line.find(' spin component '+str(spin+1)) != -1: # find it in the last iteration
+                    start = n
+            for n2, line2 in enumerate(lines[start:]):
+                m = re.search(' k-point *'+str(kpt+1)+' *:', line2)
+                if m is not None:
+                    start = start + n2
+                    break
+        for n2, line2 in enumerate(lines[start+2:]):
+            if not line2.strip():
+                break
+        occ = []
+        for line in lines[start+2:start+2+n2]:
+            occ.append(float(line.split()[2]))
+        return np.array(occ)
+
+    def read_relaxed(self):
+        for line in open('OUTCAR', 'r'):
+            if line.rfind('reached required accuracy') > -1:
+                return True
+        return False
+
+# The below functions are used to restart a calculation and are under early constructions
+
+    def read_incar(self, filename='INCAR'):
+        """Method that imports settings from INCAR file."""
+
+        self.spinpol = False
+        file=open(filename, 'r')
+        file.readline()
+        lines=file.readlines()
+        for line in lines:
+            try:
+                # Make multiplications easier to spot
+                line = line.replace("*", " * ")
+                data = line.split()
+                # Skip empty and commented lines.
+                if len(data) == 0:
+                    continue
+                elif data[0][0] in ['#', '!']:
+                    continue
+                key = data[0].lower()
+                if key in float_keys:
+                    self.float_params[key] = float(data[2])
+                elif key in exp_keys:
+                    self.exp_params[key] = float(data[2])
+                elif key in string_keys:
+                    self.string_params[key] = str(data[2])
+                elif key in int_keys:
+                    if key == 'ispin':
+                        if int(data[2]) == 2:
+                            self.spinpol = True
+                    else:
+                        self.int_params[key] = int(data[2])
+                elif key in bool_keys:
+                    if 'true' in data[2].lower():
+                        self.bool_params[key] = True
+                    elif 'false' in data[2].lower():
+                        self.bool_params[key] = False
+                elif key in list_keys:
+                    list = []
+                    if key in ('dipol', 'eint', 'ferwe', 'ropt', 'rwigs',
+                               'ldauu', 'ldaul', 'ldauj'):
+                        for a in data[2:]:
+                            if a in ["!", "#"]:
+                               break 
+                            list.append(float(a))
+                    elif key in ('iband', 'kpuse'):
+                        for a in data[2:]:
+                            if a in ["!", "#"]:
+                               break
+                            list.append(int(a))
+                    self.list_params[key] = list
+                    if key == 'magmom':
+                        list = []
+                        i = 2
+                        while i < len(data):
+                            if data[i] in ["#", "!"]:
+                                break
+                            if data[i] == "*":
+                                b = list.pop()
+                                i += 1
+                                for j in range(int(b)):
+                                    list.append(float(data[i]))
+                            else:
+                                list.append(float(data[i]))
+                            i += 1
+                        self.list_params['magmom'] = list
+                        list = np.array(list)
+                        if self.atoms is not None:
+                                self.atoms.set_initial_magnetic_moments(list[self.resort])
+ 
+            except KeyError:
+                raise IOError('Keyword "%s" in INCAR is not known by calculator.' % key)
+            except IndexError:
+                raise IOError('Value missing for keyword "%s".' % key)
+
+    def read_outcar(self):
+        # Spin polarized calculation?
+        file = open('OUTCAR', 'r')
+        lines = file.readlines()
+        file.close()
+        for line in lines:
+            if line.rfind('ISPIN') > -1:
+                if int(line.split()[2])==2:
+                    self.spinpol = True
+                else:
+                    self.spinpol = None
+        self.energy_free, self.energy_zero = self.read_energy()
+        self.forces = self.read_forces(self.atoms)
+        self.dipole = self.read_dipole()
+        self.fermi = self.read_fermi()
+        self.stress = self.read_stress()
+        self.nbands = self.read_nbands()
+        self.read_ldau()
+        p=self.int_params
+        q=self.list_params
+        if self.spinpol:
+            self.magnetic_moment = self.read_magnetic_moment()
+            if p['lorbit']>=10 or (p['lorbit']!=None and q['rwigs']):
+                self.magnetic_moments = self.read_magnetic_moments(self.atoms)
+            else:
+                self.magnetic_moments = None
+        self.set(nbands=self.nbands)
+
+    def read_kpoints(self, filename='KPOINTS'):
+        file = open(filename, 'r')
+        lines = file.readlines()
+        file.close()
+        ktype = lines[2].split()[0].lower()[0]
+        if ktype in ['g', 'm']:
+            if ktype=='g':
+                self.set(gamma=True)
+            kpts = np.array([int(lines[3].split()[i]) for i in range(3)])
+            self.set(kpts=kpts)
+        elif ktype in ['c', 'k']:
+            raise NotImplementedError('Only Monkhorst-Pack and gamma centered grid supported for restart.')
+        else:
+            raise NotImplementedError('Only Monkhorst-Pack and gamma centered grid supported for restart.')
+
+    def read_potcar(self):
+        """ Method that reads the Exchange Correlation functional from POTCAR file.
+        """
+        file = open('POTCAR', 'r')
+        lines = file.readlines()
+        file.close()
+
+        # Search for key 'LEXCH' in POTCAR
+        xc_flag = None
+        for line in lines:
+            key = line.split()[0].upper()
+            if key == 'LEXCH':
+                xc_flag = line.split()[-1].upper()
+                break
+
+        if xc_flag is None:
+            raise ValueError('LEXCH flag not found in POTCAR file.')
+
+        # Values of parameter LEXCH and corresponding XC-functional
+        xc_dict = {'PE':'PBE', '91':'PW91', 'CA':'LDA'}
+
+        if xc_flag not in xc_dict.keys():
+            raise ValueError(
+                'Unknown xc-functional flag found in POTCAR, LEXCH=%s' % xc_flag)
+
+        self.input_params['xc'] = xc_dict[xc_flag]
+
+
+class VaspChargeDensity(object):
+    """Class for representing VASP charge density"""
+
+    def __init__(self, filename='CHG'):
+        # Instance variables
+        self.atoms = []   # List of Atoms objects
+        self.chg = []     # Charge density
+        self.chgdiff = [] # Charge density difference, if spin polarized
+        self.aug = ''     # Augmentation charges, not parsed just a big string
+        self.augdiff = '' # Augmentation charge differece, is spin polarized
+
+        # Note that the augmentation charge is not a list, since they
+        # are needed only for CHGCAR files which store only a single
+        # image.
+        if filename != None:
+            self.read(filename)
+
+    def is_spin_polarized(self):
+        if len(self.chgdiff) > 0:
+            return True
+        return False
+
+    def _read_chg(self, fobj, chg, volume):
+        """Read charge from file object
+
+        Utility method for reading the actual charge density (or
+        charge density difference) from a file object. On input, the
+        file object must be at the beginning of the charge block, on
+        output the file position will be left at the end of the
+        block. The chg array must be of the correct dimensions.
+
+        """
+        # VASP writes charge density as
+        # WRITE(IU,FORM) (((C(NX,NY,NZ),NX=1,NGXC),NY=1,NGYZ),NZ=1,NGZC)
+        # Fortran nested implied do loops; innermost index fastest
+        # First, just read it in
+        for zz in range(chg.shape[2]):
+            for yy in range(chg.shape[1]):
+                chg[:, yy, zz] = np.fromfile(fobj, count = chg.shape[0],
+                                             sep=' ')
+        chg /= volume
+
+    def read(self, filename='CHG'):
+        """Read CHG or CHGCAR file.
+
+        If CHG contains charge density from multiple steps all the
+        steps are read and stored in the object. By default VASP
+        writes out the charge density every 10 steps.
+
+        chgdiff is the difference between the spin up charge density
+        and the spin down charge density and is thus only read for a
+        spin-polarized calculation.
+
+        aug is the PAW augmentation charges found in CHGCAR. These are
+        not parsed, they are just stored as a string so that they can
+        be written again to a CHGCAR format file.
+
+        """
+        import ase.io.vasp as aiv
+        f = open(filename)
+        self.atoms = []
+        self.chg = []
+        self.chgdiff = []
+        self.aug = ''
+        self.augdiff = ''
+        while True:
+            try:
+                atoms = aiv.read_vasp(f)
+            except (IOError, ValueError, IndexError):
+                # Probably an empty line, or we tried to read the
+                # augmentation occupancies in CHGCAR
+                break
+            f.readline()
+            ngr = f.readline().split()
+            ng = (int(ngr[0]), int(ngr[1]), int(ngr[2]))
+            chg = np.empty(ng)
+            self._read_chg(f, chg, atoms.get_volume())
+            self.chg.append(chg)
+            self.atoms.append(atoms)
+            # Check if the file has a spin-polarized charge density part, and
+            # if so, read it in.
+            fl = f.tell()
+            # First check if the file has an augmentation charge part (CHGCAR file.)
+            line1 = f.readline()
+            if line1=='':
+                break
+            elif line1.find('augmentation') != -1:
+                augs = [line1]
+                while True:
+                    line2 = f.readline()
+                    if line2.split() == ngr:
+                        self.aug = ''.join(augs)
+                        augs = []
+                        chgdiff = np.empty(ng)
+                        self._read_chg(f, chgdiff, atoms.get_volume())
+                        self.chgdiff.append(chgdiff)
+                    elif line2 == '':
+                        break
+                    else:
+                        augs.append(line2)
+                if len(self.aug) == 0:
+                    self.aug = ''.join(augs)
+                    augs = []
+                else:
+                    self.augdiff = ''.join(augs)
+                    augs = []
+            elif line1.split() == ngr:
+                chgdiff = np.empty(ng)
+                self._read_chg(f, chgdiff, atoms.get_volume())
+                self.chgdiff.append(chgdiff)
+            else:
+                f.seek(fl)
+        f.close()
+
+    def _write_chg(self, fobj, chg, volume, format='chg'):
+        """Write charge density
+
+        Utility function similar to _read_chg but for writing.
+
+        """
+        # Make a 1D copy of chg, must take transpose to get ordering right
+        chgtmp=chg.T.ravel()
+        # Multiply by volume
+        chgtmp=chgtmp*volume
+        # Must be a tuple to pass to string conversion
+        chgtmp=tuple(chgtmp)
+        # CHG format - 10 columns
+        if format.lower() == 'chg':
+            # Write all but the last row
+            for ii in range((len(chgtmp)-1)/10):
+                fobj.write(' %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\
+ %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\n' % chgtmp[ii*10:(ii+1)*10]
+                           )
+            # If the last row contains 10 values then write them without a newline
+            if len(chgtmp)%10==0:
+                fobj.write(' %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\
+ %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G' % chgtmp[len(chgtmp)-10:len(chgtmp)])
+            # Otherwise write fewer columns without a newline
+            else:
+                for ii in range(len(chgtmp)%10):
+                    fobj.write((' %#11.5G') % chgtmp[len(chgtmp)-len(chgtmp)%10+ii])
+        # Other formats - 5 columns
+        else:
+            # Write all but the last row
+            for ii in range((len(chgtmp)-1)/5):
+                fobj.write(' %17.10E %17.10E %17.10E %17.10E %17.10E\n' % chgtmp[ii*5:(ii+1)*5])
+            # If the last row contains 5 values then write them without a newline
+            if len(chgtmp)%5==0:
+                fobj.write(' %17.10E %17.10E %17.10E %17.10E %17.10E' % chgtmp[len(chgtmp)-5:len(chgtmp)])
+            # Otherwise write fewer columns without a newline
+            else:
+                for ii in range(len(chgtmp)%5):
+                    fobj.write((' %17.10E') % chgtmp[len(chgtmp)-len(chgtmp)%5+ii])
+        # Write a newline whatever format it is
+        fobj.write('\n')
+        # Clean up
+        del chgtmp
+
+    def write(self, filename='CHG', format=None):
+        """Write VASP charge density in CHG format.
+
+        filename: str
+            Name of file to write to.
+        format: str
+            String specifying whether to write in CHGCAR or CHG
+            format.
+
+        """
+        import ase.io.vasp as aiv
+        if format == None:
+            if filename.lower().find('chgcar') != -1:
+                format = 'chgcar'
+            elif filename.lower().find('chg') != -1:
+                format = 'chg'
+            elif len(self.chg) == 1:
+                format = 'chgcar'
+            else:
+                format = 'chg'
+        f = open(filename, 'w')
+        for ii, chg in enumerate(self.chg):
+            if format == 'chgcar' and ii != len(self.chg) - 1:
+                continue # Write only the last image for CHGCAR
+            aiv.write_vasp(f, self.atoms[ii], direct=True, long_format=False)
+            f.write('\n')
+            for dim in chg.shape:
+                f.write(' %4i' % dim)
+            f.write('\n')
+            vol = self.atoms[ii].get_volume()
+            self._write_chg(f, chg, vol, format)
+            if format == 'chgcar':
+                f.write(self.aug)
+            if self.is_spin_polarized():
+                if format == 'chg':
+                    f.write('\n')
+                for dim in chg.shape:
+                    f.write(' %4i' % dim)
+                self._write_chg(f, self.chgdiff[ii], vol, format)
+                if format == 'chgcar':
+                    f.write('\n')
+                    f.write(self.augdiff)
+            if format == 'chg' and len(self.chg) > 1:
+                f.write('\n')
+        f.close()
+
+
+class VaspDos(object):
+    """Class for representing density-of-states produced by VASP
+
+    The energies are in property self.energy
+
+    Site-projected DOS is accesible via the self.site_dos method.
+
+    Total and integrated DOS is accessible as numpy.ndarray's in the
+    properties self.dos and self.integrated_dos. If the calculation is
+    spin polarized, the arrays will be of shape (2, NDOS), else (1,
+    NDOS).
+
+    The self.efermi property contains the currently set Fermi
+    level. Changing this value shifts the energies.
+
+    """
+
+    def __init__(self, doscar='DOSCAR', efermi=0.0):
+        """Initialize"""
+        self._efermi = 0.0
+        self.read_doscar(doscar)
+        self.efermi = efermi
+
+    def _set_efermi(self, efermi):
+        """Set the Fermi level."""
+        ef = efermi - self._efermi
+        self._efermi = efermi
+        self._total_dos[0, :] = self._total_dos[0, :] - ef
+        try:
+            self._site_dos[:, 0, :] = self._site_dos[:, 0, :] - ef
+        except IndexError:
+            pass
+
+    def _get_efermi(self):
+        return self._efermi
+
+    efermi = property(_get_efermi, _set_efermi, None, "Fermi energy.")
+
+    def _get_energy(self):
+        """Return the array with the energies."""
+        return self._total_dos[0, :]
+    energy = property(_get_energy, None, None, "Array of energies")
+
+    def site_dos(self, atom, orbital):
+        """Return an NDOSx1 array with dos for the chosen atom and orbital.
+
+        atom: int
+            Atom index
+        orbital: int or str
+            Which orbital to plot
+
+        If the orbital is given as an integer:
+        If spin-unpolarized calculation, no phase factors:
+        s = 0, p = 1, d = 2
+        Spin-polarized, no phase factors:
+        s-up = 0, s-down = 1, p-up = 2, p-down = 3, d-up = 4, d-down = 5
+        If phase factors have been calculated, orbitals are
+        s, py, pz, px, dxy, dyz, dz2, dxz, dx2
+        double in the above fashion if spin polarized.
+
+        """
+        # Integer indexing for orbitals starts from 1 in the _site_dos array
+        # since the 0th column contains the energies
+        if isinstance(orbital, int):
+            return self._site_dos[atom, orbital + 1, :]
+        n = self._site_dos.shape[1]
+        if n == 4:
+            norb = {'s':1, 'p':2, 'd':3}
+        elif n == 7:
+            norb = {'s+':1, 's-up':1, 's-':2, 's-down':2,
+                    'p+':3, 'p-up':3, 'p-':4, 'p-down':4,
+                    'd+':5, 'd-up':5, 'd-':6, 'd-down':6}
+        elif n == 10:
+            norb = {'s':1, 'py':2, 'pz':3, 'px':4,
+                    'dxy':5, 'dyz':6, 'dz2':7, 'dxz':8,
+                    'dx2':9}
+        elif n == 19:
+            norb = {'s+':1, 's-up':1, 's-':2, 's-down':2,
+                    'py+':3, 'py-up':3, 'py-':4, 'py-down':4,
+                    'pz+':5, 'pz-up':5, 'pz-':6, 'pz-down':6,
+                    'px+':7, 'px-up':7, 'px-':8, 'px-down':8,
+                    'dxy+':9, 'dxy-up':9, 'dxy-':10, 'dxy-down':10,
+                    'dyz+':11, 'dyz-up':11, 'dyz-':12, 'dyz-down':12,
+                    'dz2+':13, 'dz2-up':13, 'dz2-':14, 'dz2-down':14,
+                    'dxz+':15, 'dxz-up':15, 'dxz-':16, 'dxz-down':16,
+                    'dx2+':17, 'dx2-up':17, 'dx2-':18, 'dx2-down':18}
+        return self._site_dos[atom, norb[orbital.lower()], :]
+
+    def _get_dos(self):
+        if self._total_dos.shape[0] == 3:
+            return self._total_dos[1, :]
+        elif self._total_dos.shape[0] == 5:
+            return self._total_dos[1:3, :]
+    dos = property(_get_dos, None, None, 'Average DOS in cell')
+
+    def _get_integrated_dos(self):
+        if self._total_dos.shape[0] == 3:
+            return self._total_dos[2, :]
+        elif self._total_dos.shape[0] == 5:
+            return self._total_dos[3:5, :]
+    integrated_dos = property(_get_integrated_dos, None, None,
+                              'Integrated average DOS in cell')
+
+    def read_doscar(self, fname="DOSCAR"):
+        """Read a VASP DOSCAR file"""
+        f = open(fname)
+        natoms = int(f.readline().split()[0])
+        [f.readline() for nn in range(4)]  # Skip next 4 lines.
+        # First we have a block with total and total integrated DOS
+        ndos = int(f.readline().split()[2])
+        dos = []
+        for nd in xrange(ndos):
+            dos.append(np.array([float(x) for x in f.readline().split()]))
+        self._total_dos = np.array(dos).T
+        # Next we have one block per atom, if INCAR contains the stuff
+        # necessary for generating site-projected DOS
+        dos = []
+        for na in xrange(natoms):
+            line = f.readline()
+            if line == '':
+                # No site-projected DOS
+                break
+            ndos = int(line.split()[2])
+            line = f.readline().split()
+            cdos = np.empty((ndos, len(line)))
+            cdos[0] = np.array(line)
+            for nd in xrange(1, ndos):
+                line = f.readline().split()
+                cdos[nd] = np.array([float(x) for x in line])
+            dos.append(cdos.T)
+        self._site_dos = np.array(dos)
+
+
+import pickle
+
+class xdat2traj:
+    def __init__(self, trajectory=None, atoms=None, poscar=None,
+                 xdatcar=None, sort=None, calc=None):
+        if not poscar:
+            self.poscar = 'POSCAR'
+        else:
+            self.poscar = poscar
+        if not atoms:
+            self.atoms = ase.io.read(self.poscar, format='vasp')
+        else:
+            self.atoms = atoms
+        if not xdatcar:
+            self.xdatcar = 'XDATCAR'
+        else:
+            self.xdatcar = xdatcar
+        if not trajectory:
+            self.trajectory = 'out.traj'
+        else:
+            self.trajectory = trajectory
+        if not calc:
+            self.calc = Vasp()
+        else:
+            self.calc = calc
+        if not sort:
+            if not hasattr(self.calc, 'sort'):
+                self.calc.sort = range(len(self.atoms))
+        else:
+            self.calc.sort = sort
+        self.calc.resort = range(len(self.calc.sort))
+        for n in range(len(self.calc.resort)):
+            self.calc.resort[self.calc.sort[n]] = n
+        self.out = ase.io.trajectory.PickleTrajectory(self.trajectory, mode='w')
+        self.energies = self.calc.read_energy(all=True)[1]
+        self.forces = self.calc.read_forces(self.atoms, all=True)
+
+    def convert(self):
+        lines = open(self.xdatcar).readlines()
+        if len(lines[7].split())==0:
+            del(lines[0:8])
+        elif len(lines[5].split())==0:
+            del(lines[0:6])
+        elif len(lines[4].split())==0:
+            del(lines[0:5])
+        step = 0
+        iatom = 0
+        scaled_pos = []
+        for line in lines:
+            if iatom == len(self.atoms):
+                if step == 0:
+                    self.out.write_header(self.atoms[self.calc.resort])
+                scaled_pos = np.array(scaled_pos)
+                self.atoms.set_scaled_positions(scaled_pos)
+                d = {'positions': self.atoms.get_positions()[self.calc.resort],
+                     'cell': self.atoms.get_cell(),
+                     'momenta': None,
+                     'energy': self.energies[step],
+                     'forces': self.forces[step],
+                     'stress': None}
+                pickle.dump(d, self.out.fd, protocol=-1)
+                scaled_pos = []
+                iatom = 0
+                step += 1
+            else:
+
+                iatom += 1
+                scaled_pos.append([float(line.split()[n]) for n in range(3)])
+
+        # Write also the last image
+        # I'm sure there is also more clever fix...
+        scaled_pos = np.array(scaled_pos)
+        self.atoms.set_scaled_positions(scaled_pos)
+        d = {'positions': self.atoms.get_positions()[self.calc.resort],
+             'cell': self.atoms.get_cell(),
+             'momenta': None,
+             'energy': self.energies[step],
+             'forces': self.forces[step],
+             'stress': None}
+        pickle.dump(d, self.out.fd, protocol=-1)
+
+        self.out.fd.close()
diff --git a/ase/calculators/vdwcorrection.py b/ase/calculators/vdwcorrection.py
new file mode 100644
index 0000000..24d3b2b
--- /dev/null
+++ b/ase/calculators/vdwcorrection.py
@@ -0,0 +1,219 @@
+"""van der Waals correction schemes for DFT"""
+
+import numpy as np
+from ase.units import Bohr, Hartree
+from gpaw.mpi import rank
+
+# dipole polarizabilities and C6 values from 
+# X. Chu and A. Dalgarno, J. Chem. Phys. 129 (2004) 4083
+# atomic units, a_0^3
+vdWDB_Chu04jcp = {
+    # Element: [alpha, C6]; units [Bohr^3, Hartree * Bohr^6]
+    'H'  : [4.5, 6.5], # [exact, Tkatchenko PRL]
+    'He' : [1.38, 1.42],
+    'Li' : [164, 1392],
+    'Be' : [38, 227],
+    'B'  : [21, 99.5],
+    'C'  : [12, 46.6],
+    'N'  : [7.4, 24.2],
+    'O'  : [5.4, 15.6],
+    'F'  : [3.8, 9.52],
+    'Ne' : [2.67, 6.20],
+    'Na' : [163, 1518],
+    'Mg' : [71, 626],
+    'Al' : [60, 528],
+    'Si' : [37, 305],
+    'Cl' : [15, 94.6],
+    'Ar' : [11.1, 64.2],
+    'Ca' : [160, 2163],
+    'Fe' : [56, 482],
+    'Br' : [20, 162],
+    'Kr' : [16.7, 130],
+    'Sr' : [199, 3175],
+    'I'  : [35, 385],
+}
+
+# C6 values and vdW radii from 
+# S. Grimme, J Comput Chem 27 (2006) 1787-1799
+vdWDB_Grimme06jcc = {
+    # Element: [C6, R0]; units [J nm^6 mol^{-1}, Angstrom]
+    'H'  : [0.14, 1.001],
+    'He' : [0.08, 1.012],
+    'Li' : [1.61, 0.825],
+    'Be' : [1.61, 1.408],
+    'B'  : [3.13, 1.485],
+    'C'  : [1.75, 1.452],
+    'N'  : [1.23, 1.397],
+    'O'  : [0.70, 1.342],
+    'F'  : [0.75, 1.287],
+    'Ne' : [0.63, 1.243],
+    'Na' : [5.71, 1.144],
+    'Mg' : [5.71, 1.364],
+    'Al' : [10.79, 1.639],
+    'Si' : [9.23, 1.716],
+    'P'  : [7.84, 1.705],
+    'S'  : [5.57, 1.683],
+    'Cl' : [5.07, 1.639],
+    'Ar' : [4.61, 1.595],
+    'K'  : [10.80, 1.485],
+    'Ca' : [10.80, 1.474],
+    'Sc' : [10.80, 1.562],
+    'Ti' : [10.80, 1.562],
+    'V'  : [10.80, 1.562],
+    'Cr'  : [10.80, 1.562],
+    'Mn'  : [10.80, 1.562],
+    'Fe'  : [10.80, 1.562],
+    'Co'  : [10.80, 1.562],
+    'Ni'  : [10.80, 1.562],
+    'Cu'  : [10.80, 1.562],
+    'Zn' : [10.80, 1.562],
+    'Ga' : [16.99, 1.650],
+    'Ge' : [17.10, 1.727],
+    'As' : [16.37, 1.760],
+    'Se' : [12.64, 1.771],
+    'Br' : [12.47, 1.749],
+    'Kr' : [12.01, 1.727],
+    'Rb' : [24.67, 1.628],
+    'Sr' : [24.67, 1.606],
+    'Y-Cd' : [24.67, 1.639],
+    'In' : [37.32, 1.672],
+    'Sn' : [38.71, 1.804],
+    'Sb' : [38.44, 1.881],
+    'Te' : [31.74, 1.892],
+    'I'  : [31.50, 1.892],
+    'Xe' : [29.99, 1.881],
+    }
+
+class vdWTkatchenko09prl:
+    """vdW correction after Tkatchenko and Scheffler PRL 102 (2009) 073005.
+
+    hirshfeld: the Hirshfeld partitioning object
+    calculator: the calculator to get the PBE energy
+    missing: Missing elements do not contribute to the vdW-Energy by default
+    """
+    def __init__(self,                  
+                 hirshfeld=None, vdwradii=None, calculator=None,
+                 Rmax = 10, # maximal radius for periodic calculations
+                 missing='zero'):
+        self.hirshfeld = hirshfeld
+        if calculator is None:
+            self.calculator = self.hirshfeld.get_calculator()
+        else:
+            self.calculator = calculator
+        self.vdwradii = vdwradii
+        self.Rmax = Rmax
+        self.missing = missing
+        self.atoms = None
+
+        self.sR = 0.94
+        self.d = 20
+
+    def update(self, atoms=None):
+        if atoms is None:
+            atoms = self.calculator.get_atoms()
+        if (self.atoms and 
+            (self.atoms.get_positions() == atoms.get_positions()).all()):
+            return
+        self.energy = self.calculator.get_potential_energy(atoms)
+        self.forces = self.calculator.get_forces(atoms)
+        self.atoms = atoms.copy()
+
+        if self.vdwradii is not None:
+            # external vdW radii
+            vdwradii = self.vdwradii
+            assert(len(atoms) == len(vdwradii))
+        else:
+            vdwradii = []
+            for atom in atoms:
+                self.vdwradii.append(vdWDB_Grimme06jcc[atom.symbol][1])
+ 
+        if self.hirshfeld == None:
+            volume_ratios = [1.] * len(atoms)
+        elif hasattr(self.hirshfeld,'__len__'): # a list
+            assert(len(atoms) == len(self.hirshfeld))
+            volume_ratios = self.hirshfeld
+        else: # sould be an object
+            self.hirshfeld.initialize()
+            volume_ratios = self.hirshfeld.get_effective_volume_ratios()
+
+        # correction for effective C6
+        na = len(atoms)
+        C6eff_a = np.empty((na))
+        alpha_a = np.empty((na))
+        R0eff_a = np.empty((na))
+        for a, atom in enumerate(atoms):
+            # free atom values
+            alpha_a[a], C6eff_a[a] = vdWDB_Chu04jcp[atom.symbol]
+            # correction for effective C6
+            C6eff_a[a] *= Hartree * volume_ratios[a]**2 * Bohr**6
+            R0eff_a[a] = vdwradii[a] * volume_ratios[a]**(1./3.)
+        C6eff_aa = np.empty((na, na))
+        for a in range(na):
+            for b in range(a, na):
+                C6eff_aa[a, b] = (2 * C6eff_a[a] * C6eff_a[b] /
+                                  (alpha_a[b] / alpha_a[a] * C6eff_a[a] +
+                                   alpha_a[a] / alpha_a[b] * C6eff_a[b]   ))
+                C6eff_aa[b, a] = C6eff_aa[a, b]
+
+        # PBC
+        pbc_c = atoms.get_pbc()
+        cell_c = atoms.get_cell()
+        Rcell_c = np.sqrt(np.sum(cell_c**2, axis=1))
+        ncells_c = np.ceil(np.where(pbc_c, 1. + self.Rmax / Rcell_c, 1))
+        ncells_c = np.array(ncells_c, dtype=int)
+
+        positions = atoms.get_positions()
+        EvdW = 0.0
+        forces = 0. * self.forces
+        # loop over all atoms in the cell
+        for ia, posa in enumerate(positions):
+            # loop over all atoms in the cell (and neighbour cells for PBC)
+            for ib, posb in enumerate(positions):
+                # loops over neighbour cells
+                for ix in range(-ncells_c[0] + 1, ncells_c[0]):
+                    for iy in range(-ncells_c[1] + 1, ncells_c[1]):
+                        for iz in range(-ncells_c[2] + 1, ncells_c[2]):
+                            i_c = np.array([ix, iy, iz])
+                            diff = posb + np.dot(i_c, cell_c) - posa
+                            r2 = np.dot(diff, diff)
+                            r6 = r2**3
+                            r = np.sqrt(r2)
+                            if r > 1.e-10 and r < self.Rmax:
+                                Edamp, Fdamp = self.damping(r, 
+                                                            R0eff_a[ia],
+                                                            R0eff_a[ib],
+                                                            d=self.d, 
+                                                            sR=self.sR)
+                                EvdW -= (Edamp *
+                                         C6eff_aa[ia, ib] / r6 )
+                                # we neglect the C6eff contribution to the
+                                # forces
+                                forces[ia] -= ((Fdamp - 6 * Edamp / r) *
+                                               C6eff_aa[ia, ib] / r6 *
+                                               diff / r                 )
+        self.energy += EvdW / 2. # double counting
+        self.forces += forces / 2. # double counting
+        
+    def damping(self, RAB, R0A, R0B,
+                d = 20,   # steepness of the step function
+                sR = 0.94 # for PBE
+                ):
+        """Damping factor.
+
+        Standard values for d and sR as given in 
+        Tkatchenko and Scheffler PRL 102 (2009) 073005."""
+        scale = 1.0 / (sR * (R0A + R0B))
+        x = RAB * scale
+        chi = np.exp(-d * (x - 1.0))
+        return 1.0 / (1.0 + chi), d * scale * chi / (1.0 + chi)**2
+ 
+    def get_potential_energy(self, atoms=None):
+        self.update(atoms)
+        return self.energy
+
+    def get_forces(self, atoms):
+        self.update(atoms)
+        return self.forces
+
+    def get_stress(self, atoms):
+        return np.zeros((3, 3))
diff --git a/ase/cluster/__init__.py b/ase/cluster/__init__.py
new file mode 100644
index 0000000..cfb837e
--- /dev/null
+++ b/ase/cluster/__init__.py
@@ -0,0 +1,12 @@
+"Module for creating clusters."
+
+from ase.cluster.cluster import Cluster
+from ase.cluster.wulff import wulff_construction
+
+from ase.cluster.cubic import SimpleCubic, BodyCenteredCubic,\
+                              FaceCenteredCubic
+from ase.cluster.octahedron import Octahedron
+from ase.cluster.hexagonal import Hexagonal, HexagonalClosedPacked
+from ase.cluster.icosahedron import Icosahedron
+from ase.cluster.decahedron import Decahedron
+
diff --git a/ase/cluster/base.py b/ase/cluster/base.py
new file mode 100644
index 0000000..c02b96f
--- /dev/null
+++ b/ase/cluster/base.py
@@ -0,0 +1,62 @@
+import numpy as np
+
+class ClusterBase:
+    def get_layer_distance(self, miller, layers=1):
+        """Returns the distance between planes defined by the given miller
+        index.
+        """
+        n = self.miller_to_direction(miller)
+        d1 = d2 = 0.0
+
+        d = np.abs(np.sum(n * self.lattice_basis, axis=1))
+        mask = np.greater(d, 1e-10)
+        if mask.sum() > 0:
+            d1 = np.min(d[mask])
+
+        if len(self.atomic_basis) > 1:
+            atomic_basis = np.dot(self.atomic_basis, self.lattice_basis)
+            d = np.sum(n * atomic_basis, axis=1)
+            s = np.sign(d)
+            d = np.abs(d)
+            mask = np.greater(d, 1e-10)
+            if mask.sum() > 0:
+                d2 = np.min(d[mask])
+                s2 = s[mask][np.argmin(d[mask])]
+
+        if d2 > 1e-10:
+            if s2 < 0 and d1 - d2 > 1e-10:
+                d2 = d1 - d2
+            elif s2 < 0 and d2 - d1 > 1e-10:
+                d2 = 2 * d1 - d2
+            elif s2 > 0 and d2 - d1 > 1e-10:
+                d2 = d2 - d1
+
+            if np.abs(d1 - d2) < 1e-10:
+                ld = np.array([d1])
+            elif np.abs(d1 - 2 * d2) < 1e-10:
+                ld = np.array([d2])
+            else:
+                assert d1 > d2, "Something is wrong with the layer distance."
+                ld = np.array([d2, d1 - d2])
+        else:
+            ld = np.array([d1])
+
+        if len(ld) > 1:
+            if layers < 0:
+                ld = np.array([-ld[1], -ld[0]])
+                layers *= -1
+
+            map = np.arange(layers - (layers % 1), dtype=int) % len(ld)
+            r = ld[map].sum() + (layers % 1) * ld[np.abs(map[-1] - 1)]
+        else:
+            r = ld[0] * layers
+
+        return r
+
+    def miller_to_direction(self, miller, norm=True):
+        """Returns the direction corresponding to a given Miller index."""
+        d = np.dot(miller, self.resiproc_basis)
+        if norm:
+            d = d / np.linalg.norm(d)
+        return d
+
diff --git a/ase/cluster/cluster.py b/ase/cluster/cluster.py
new file mode 100644
index 0000000..c1ca792
--- /dev/null
+++ b/ase/cluster/cluster.py
@@ -0,0 +1,131 @@
+import os
+import math
+import numpy as np
+import cPickle as pickle
+
+from ase import Atoms
+from ase.data import chemical_symbols
+from ase.cluster.base import ClusterBase
+
+
+class Cluster(Atoms, ClusterBase):
+    symmetry = None
+    surfaces = None
+    lattice_basis = None
+    resiproc_basis = None
+    atomic_basis = None
+
+    def copy(self):
+        cluster = Atoms.copy(self)
+        cluster.symmetry = self.symmetry
+        cluster.surfaces = self.surfaces.copy()
+        cluster.lattice_basis = self.lattice_basis.copy()
+        cluster.atomic_basis = self.atomic_basis.copy()
+        cluster.resiproc_basis = self.resiproc_basis.copy()
+        return cluster
+
+    def get_surfaces(self):
+        """Returns the miller indexs of the stored surfaces of the cluster."""
+        if not self.surfaces is None:
+            return self.surfaces.copy()
+        else:
+            return None
+
+    def get_layers(self):
+        """Return number of atomic layers in stored surfaces directions."""
+
+        layers = []
+
+        for s in self.surfaces:
+            n = self.miller_to_direction(s)
+            c = self.get_positions().mean(axis=0)
+            r = np.dot(self.get_positions() - c, n).max()
+            d = self.get_layer_distance(s, 2)
+            l = 2 * np.round(r / d).astype(int)
+
+            ls = np.arange(l - 1, l + 2)
+            ds = np.array([self.get_layer_distance(s, i) for i in ls])
+
+            mask = (np.abs(ds - r) < 1e-10)
+
+            layers.append(ls[mask][0])
+
+        return np.array(layers, int)
+
+    def get_diameter(self, method='volume'):
+        """Returns an estimate of the cluster diameter based on two different
+        methods.
+
+        method = 'volume': Returns the diameter of a sphere with the
+                           same volume as the atoms. (Default)
+        
+        method = 'shape': Returns the averaged diameter calculated from the
+                          directions given by the defined surfaces.
+        """
+
+        if method == 'shape':
+            cen = self.get_positions().mean(axis=0)
+            pos = self.get_positions() - cen
+            d = 0.0
+            for s in self.surfaces:
+                n = self.miller_to_direction(s)
+                r = np.dot(pos, n)
+                d += r.max() - r.min()
+            return d / len(self.surfaces)
+        elif method == 'volume':
+            V_cell = np.abs(np.linalg.det(self.lattice_basis))
+            N_cell = len(self.atomic_basis)
+            N = len(self)
+            return 2.0 * (3.0 * N * V_cell /
+                          (4.0 * math.pi * N_cell)) ** (1.0 / 3.0)
+        else:
+            return 0.0
+
+    #Functions to store the cluster
+    def write(self, filename=None):
+        if not isinstance(filename, str):
+            raise Warning('You must specify a valid filename.')
+
+        if os.path.isfile(filename):
+            os.rename(filename, filename + '.bak')
+
+        d = {'symmetry': self.symmetry,
+             'surfaces': self.surfaces,
+             'lattice_basis': self.lattice_basis,
+             'resiproc_basis': self.resiproc_basis,
+             'atomic_basis': self.atomic_basis,
+             'cell': self.get_cell(),
+             'pbc': self.get_pbc()}
+
+        f = open(filename, 'w')
+        f.write('Cluster')
+        pickle.dump(d, f)
+        pickle.dump(self.arrays, f)
+        f.close()
+
+    def read(self, filename):
+        if not os.path.isfile(filename):
+            raise Warning('The file specified do not exist.')
+
+        f = open(filename, 'r')
+
+        try:
+            if f.read(len('Cluster')) != 'Cluster':
+                raise Warning('This is not a compatible file.')
+            d = pickle.load(f)
+            self.arrays = pickle.load(f)
+        except EOFError:
+            raise Warinig('Bad file.')
+
+        f.close()
+
+        self.symmetry = d['symmetry']
+        self.surfaces = d['surfaces']
+        self.lattice_basis = d['lattice_basis']
+        self.resiproc_basis = d['resiproc_basis']
+        self.atomic_basis = d['atomic_basis']
+        self.set_cell(d['cell'])
+        self.set_pbc(d['pbc'])
+        self.set_constraint()
+        self.adsorbate_info = {}
+        self.calc = None
diff --git a/ase/cluster/compounds.py b/ase/cluster/compounds.py
new file mode 100644
index 0000000..9b84549
--- /dev/null
+++ b/ase/cluster/compounds.py
@@ -0,0 +1,18 @@
+import numpy as np
+
+from ase.cluster.cubic import SimpleCubicFactory
+
+# The L1_2 structure is "based on FCC", but is really simple cubic
+# with a basis.
+class AuCu3Factory(SimpleCubicFactory):
+    "A factory for creating AuCu3 (L1_2) lattices."
+
+    atomic_basis = np.array([[0., 0., 0.],
+                             [0., .5, .5],
+                             [.5, 0., .5],
+                             [.5, .5, 0.]])
+
+    element_basis = [0, 1, 1, 1]
+
+AuCu3 = L1_2 = AuCu3Factory()
+
diff --git a/ase/cluster/cubic.py b/ase/cluster/cubic.py
new file mode 100644
index 0000000..6222cbb
--- /dev/null
+++ b/ase/cluster/cubic.py
@@ -0,0 +1,54 @@
+"""
+Function-like objects that creates cubic clusters.
+"""
+
+import numpy as np
+
+from ase.data import reference_states as _refstate
+from ase.cluster.factory import ClusterFactory
+
+class SimpleCubicFactory(ClusterFactory):
+    spacegroup = 221
+
+    xtal_name = 'sc'
+
+    def get_lattice_constant(self):
+        "Get the lattice constant of an element with cubic crystal structure."
+        symmetry = _refstate[self.atomic_numbers[0]]['symmetry']
+        if symmetry != self.xtal_name:
+            raise ValueError, ("Cannot guess the %s " % (self.xtal_name,) +
+                               "lattice constant of an element with crystal " +
+                               "structure %s." % (symmetry,))
+        return _refstate[self.atomic_numbers[0]]['a']
+
+    def set_basis(self):
+        a = self.lattice_constant
+        if not isinstance(a, (int, float)):
+            raise ValueError("Improper lattice constant for %s crystal." % (xtal_name,))
+
+        self.lattice_basis = np.array([[a, 0., 0.],
+                                       [0., a, 0.],
+                                       [0., 0., a]])
+
+        self.resiproc_basis = self.get_resiproc_basis(self.lattice_basis)
+
+SimpleCubic = SimpleCubicFactory()
+
+class BodyCenteredCubicFactory(SimpleCubicFactory):
+    xtal_name = 'bcc'
+
+    atomic_basis = np.array([[0., 0., 0.],
+                             [.5, .5, .5]])
+
+BodyCenteredCubic = BodyCenteredCubicFactory()
+
+class FaceCenteredCubicFactory(SimpleCubicFactory):
+    xtal_name = 'fcc'
+
+    atomic_basis = np.array([[0., 0., 0.],
+                             [0., .5, .5],
+                             [.5, 0., .5],
+                             [.5, .5, 0.]])
+
+FaceCenteredCubic = FaceCenteredCubicFactory()
+
diff --git a/ase/cluster/data/__init__.py b/ase/cluster/data/__init__.py
new file mode 100644
index 0000000..5d4e748
--- /dev/null
+++ b/ase/cluster/data/__init__.py
@@ -0,0 +1,13 @@
+from ase.data import atomic_numbers, chemical_symbols, reference_states
+from ase.units import *
+
+import fcc
+import hcp
+import au
+
+lattice = {'fcc': fcc.data,
+           'hcp': hcp.data,
+          }
+
+element = {79: au.data, #Au
+          }
diff --git a/ase/cluster/data/au.py b/ase/cluster/data/au.py
new file mode 100644
index 0000000..9c63086
--- /dev/null
+++ b/ase/cluster/data/au.py
@@ -0,0 +1,26 @@
+"""Element data - 79 Au Gold"""
+
+name = 'Gold'
+symbol = 'Au'
+symmetry = 'fcc'
+
+energy_slope = -0.090
+energy_intersection = -1.537
+
+energy_types = {1: -2.618, #bulk
+                2: -2.237, #100 surface
+                3: -2.343, #110 surface (top) or 111-111 edge
+                4: -2.369, #110 surface (bottom)
+                5: -2.352, #111 surface
+                6: -2.028, #100-110 edge
+                7: -2.215, #100-111 edge
+                }
+
+data = {'name': name,
+        'symbol': symbol,
+        'symmetry': symmetry,
+        'energy_slope': energy_slope,
+        'energy_intersection': energy_intersection,
+        'energy_types': energy_types,
+        }
+
diff --git a/ase/cluster/data/fcc.py b/ase/cluster/data/fcc.py
new file mode 100644
index 0000000..5789263
--- /dev/null
+++ b/ase/cluster/data/fcc.py
@@ -0,0 +1,277 @@
+"""Lattice data - Face Centered Cubic"""
+
+import numpy as np
+from math import sqrt
+from symmetry import *
+
+#Definition of symmetries
+basesymmetries = [np.matrix([[-1, 0, 0],  #Mirror x-axis
+                             [0, 1, 0],
+                             [0, 0, 1]]),
+                  np.matrix([[1, 0, 0],   #Mirror y-axis
+                             [0, -1, 0],
+                             [0, 0, 1]]),
+                  np.matrix([[1, 0, 0],   #Mirror z-axis
+                             [0, 1, 0],
+                             [0, 0, -1]]),
+                  np.matrix([[1, 0, 0],   #Rotation x-axis (4-fold)
+                             [0, 0, -1],
+                             [0, 1, 0]]),
+                  np.matrix([[0, 0, -1],  #Rotation y-axis (4-fold)
+                             [0, 1, 0],
+                             [1, 0, 0]]),
+                  np.matrix([[0, 1, 0],   #Rotation z-axis (4-fold)
+                             [-1, 0, 0],
+                             [0, 0, 1]]),
+                  np.matrix([[0, 0, 1],   #Rotation (111)-axis (3-fold)
+                             [1, 0, 0],
+                             [0, 1, 0]]),
+                  np.matrix([[0, 0, -1],  #Rotation (11-1)-axis (3-fold)
+                             [1, 0, 0],
+                             [0, -1, 0]]),
+                  np.matrix([[0, 0, 1],   #Rotation (1-11)-axis (3-fold)
+                             [-1, 0, 0],
+                             [0, -1, 0]]),
+                  np.matrix([[0, 0, -1],  #Rotation (-111)-axis (3-fold)
+                             [-1, 0, 0],
+                             [0, 1, 0]])]
+
+symmetries = get_all_symmetries(basesymmetries, 48)
+
+#Definition of used surfaces
+surface_names = [(1,0,0), (-1,0,0),
+                 (0,1,0), (0,-1,0),
+                 (0,0,1), (0,0,-1),
+                 (1,1,0), (-1,-1,0),
+                 (1,0,1), (-1,0,-1),
+                 (0,1,1), (0,-1,-1),
+                 (1,-1,0), (-1,1,0),
+                 (1,0,-1), (-1,0,1),
+                 (0,1,-1), (0,-1,1),
+                 (1,1,1), (-1,-1,-1),
+                 (-1,1,1), (1,-1,-1),
+                 (1,-1,1), (-1,1,-1),
+                 (1,1,-1), (-1,-1,1)]
+
+surface_numbers = {}
+for i, s in enumerate(surface_names):
+    surface_numbers[s] = i
+
+surface_count = len(surface_names)
+
+surface_mapping = {0: 1, 1: 0, 2: 3, 3: 2, 4: 5, 5: 4,
+                   6: 7, 7: 6, 8: 9, 9: 8, 10: 11, 11: 10,
+                   12: 13, 13: 12, 14: 15, 15: 14, 16: 17, 17: 16,
+                   18: 19, 19: 18, 20: 21, 21: 20, 22: 23, 23: 22,
+                   24: 25, 25: 24}
+
+surface_data = ([{'l': 1.0, 'd': 0.5}] * 6 +
+                [{'l': 1.5, 'd': 1.0 / 4.0}] * 12 +
+                [{'l': 1.0, 'd': 1.0 / 3.0}] * 8)
+
+surface_symmetries = get_surface_symmetries(symmetries, surface_names, surface_numbers)
+
+def surface_fitting(surfaces):
+    for i, n1 in enumerate(np.array(surface_names)):
+        d1 = surface_data[i]['d']
+        a1 = surfaces[i] * d1
+        for j, n2 in enumerate(np.array(surface_names)):
+            d2 = surface_data[j]['d']
+            a2 = surfaces[j] * d2
+            nd = np.dot(n1, n2) / (np.linalg.norm(n1) * np.linalg.norm(n2))
+            if a2 * nd > a1:
+                surfaces[j] = int(round(a1 / (nd * d2)))
+
+    return surfaces
+
+def surface_centering(surfaces, basis='100', debug=0):
+    if basis == '100':
+        #Centering within the basis {[1,0,0], [0,1,0], [0,0,1]}
+        dx = (surfaces[0] - surfaces[1]) // 2
+        dy = (surfaces[2] - surfaces[3]) // 2
+        dz = (surfaces[4] - surfaces[5]) // 2
+
+        if (dx + dy + dz) % 2 == 1:
+            dx -= 1
+
+        if debug:
+            print '(%i, %i, %i)' % (dx, dy, dz)
+    elif basis == '110':
+        #Centering within the basis {[1,1,0], [1,0,1], [0,1,1]}
+        dl1 = ((surfaces[6] - surfaces[7]) // 4) * 2
+        dl2 = ((surfaces[8] - surfaces[9]) // 4) * 2
+        dl3 = ((surfaces[10] - surfaces[11]) // 4) * 2
+
+        #Correction for the none orthogonality of the basis
+        t1 = (dl1 != 0 and dl2 != 0 and dl3 == 0)
+        t2 = (dl1 != 0 and dl2 == 0 and dl3 != 0)
+        t3 = (dl1 != 0 and dl2 == 0 and dl3 != 0)
+
+        if t1 or t2 or t3:
+            d1 = (3 * dl1) // 2 - dl2 // 2 - dl3 // 2
+            d2 = (3 * dl2) // 2 - dl1 // 2 - dl3 // 2
+            d3 = (3 * dl3) // 2 - dl1 // 2 - dl2 // 2
+        else:
+            d1, d2, d3 = 0, 0, 0
+
+        #Converting to '100' basis
+        dx = (d1 + d2) // 2
+        dy = (d1 + d3) // 2
+        dz = (d2 + d3) // 2
+
+        if debug:
+            print ('(%i, %i, %i) -> (%i, %i, %i) -> (%i, %i, %i)' %
+                   (dl1, dl2, dl3, d1, d2, d3, dx, dy, dz))
+    else:
+        dx, dy, dz = 0
+
+    s = np.array(surfaces, int)
+    ds = np.array([- dx, dx,
+                   - dy, dy,
+                   - dz, dz,
+                   - dx - dy, dx + dy,
+                   - dx - dz, dx + dz,
+                   - dy - dz, dy + dz,
+                   - dx + dy, dx - dy,
+                   - dx + dz, dx - dz,
+                   - dy + dz, dy - dz,
+                   (-dx - dy - dz) // 2, (dx + dy + dz) // 2,
+                   (dx - dy - dz) // 2, (-dx + dy + dz) // 2,
+                   (-dx + dy - dz) // 2, (dx - dy + dz) // 2,
+                   (-dx - dy + dz) // 2, (dx + dy - dz) // 2], int)
+
+    if (s + ds >= 0).all():
+        surfaces = s + ds
+
+    return surfaces
+
+#Definition of the neighbor environment
+neighbor_names = [(0.5, 0.5, 0), (-0.5, -0.5, 0),
+                  (0.5, 0, 0.5), (-0.5, 0, -0.5),
+                  (0, 0.5, 0.5), (0, -0.5, -0.5),
+                  (0.5, -0.5, 0), (-0.5, 0.5, 0),
+                  (0.5, 0, -0.5), (-0.5, 0, 0.5),
+                  (0, 0.5, -0.5), (0, -0.5, 0.5)]
+
+neighbor_numbers = {}
+for i, n in enumerate(neighbor_names):
+    neighbor_numbers[n] = i
+
+neighbor_positions = np.array(neighbor_names, dtype=float)
+
+neighbor_cutoff = 0.8
+neighbor_count = 12
+
+neighbor_mapping = {0: 1, 1: 0, 2: 3, 3: 2, 4: 5, 5: 4,
+                    6: 7, 7: 6, 8: 9, 9: 8, 10: 11, 11: 10}
+
+neighbor_symmetries = get_neighbor_symmetries(symmetries,
+                                              neighbor_positions,
+                                              neighbor_numbers)
+
+#Definition of the atom types that is used based on the neighborlist
+basetype_names = [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+                  (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+                  (0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1),
+                  (0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1),
+                  (0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
+                  (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1),
+                  (0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1),
+                  (0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1),
+                  (0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1),
+                  (0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0),
+                  (1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0),
+                  (0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0),
+                  (1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0),
+                  (0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1),
+                  (1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1),
+                  (0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1),
+                  ]
+
+basetype_data = [{'type': 0,
+                  'coordination': 0,
+                  'name': 'Free atom, Unknown'},
+                 {'type': 1,
+                  'coordination': 12,
+                  'name': 'bulk'},
+                 {'type': 2,
+                  'coordination': 8,
+                  'name': '100 surface'},
+                 {'type': 3,
+                  'coordination': 7,
+                  'name': '110 surface (top), 111-111 edge, 110-111 edge'},
+                 {'type': 4,
+                  'coordination': 11,
+                  'name': '110 surface (bottom)'},
+                 {'type': 5,
+                  'coordination': 9,
+                  'name': '111 surface'},
+                 {'type': 6,
+                  'coordination': 6,
+                  'name': '100-110 edge'},
+                 {'type': 7,
+                  'coordination': 7,
+                  'name': '100-111 edge'},
+                 {'type': 8,
+                  'coordination': 6,
+                  'name': '111-111-100 corner'},
+                 {'type': 9,
+                  'coordination': 4,
+                  'name': '100 surface ad-atom'},
+                 {'type': 10,
+                  'coordination': 5,
+                  'name': '110 surface ad-atom (bottom), A5 site'},
+                 {'type': 11,
+                  'coordination': 3,
+                  'name': '111 surface ad-atom'},
+                 {'type': 12,
+                  'coordination': 9,
+                  'name': '100 surface with ad-atom'},
+                 {'type': 13,
+                  'coordination': 8,
+                  'name': '110 surface with ad-atom'},
+                 {'type': 14,
+                  'coordination': 10,
+                  'name': '111 surface with ad-atom'},
+                 {'type': 15,
+                  'coordination': 5,
+                  'name': 'B5 site'},
+                 ]
+
+type_count = len(basetype_names)
+
+type_names = []
+type_data = []
+for i, n in enumerate(basetype_names):
+    type_names.append(n)
+    type_data.append(basetype_data[i])
+
+    for sym in neighbor_symmetries:
+        new_type = apply_neighbor_symmetry(n, sym)
+
+        if not new_type in type_names:
+            type_names.append(new_type)
+            type_data.append(basetype_data[i])
+
+type_numbers = {}
+for i, n in enumerate(type_names):
+    type_numbers[n] = i
+
+data = {'symmetries': symmetries,
+        'surface_names': surface_names,
+        'surface_numbers': surface_numbers,
+        'surface_data': surface_data,
+        'surface_count': surface_count,
+        'surface_mapping': surface_mapping,
+        'surface_symmetries': surface_symmetries,
+        'neighbor_positions': neighbor_positions,
+        'neighbor_numbers': neighbor_numbers,
+        'neighbor_count': neighbor_count,
+        'neighbor_cutoff': neighbor_cutoff,
+        'neighbor_mapping': neighbor_mapping,
+        'neighbor_symmetries': neighbor_symmetries,
+        'type_names': type_names,
+        'type_numbers': type_numbers,
+        'type_data': type_data,
+        'type_count': type_count,
+       }
diff --git a/ase/cluster/data/hcp.py b/ase/cluster/data/hcp.py
new file mode 100644
index 0000000..bb7465f
--- /dev/null
+++ b/ase/cluster/data/hcp.py
@@ -0,0 +1,126 @@
+"""Structure data - Hexagonal Closed Packed"""
+
+import numpy as np
+from math import sqrt
+from symmetry import *
+
+#Definition of symmetries (in hcp basis)
+basesymmetries = [np.matrix([[1, 0, 0],   #Mirror y-axis
+                             [0, 1, 0],
+                             [0, 0, -1]]),
+                  np.matrix([[0, 1, 0],   #Rotation z-axix (3-fold)
+                             [-1, -1, 0],
+                             [0, 0, 1]]),
+                  np.matrix([[1, 0, 0],   #Rotation a-axis (2-fold)
+                             [-1, -1, 0],
+                             [0, 0, -1]]),
+                  np.matrix([[-1, -1, 0], #Rotation b-axis (2-fold)
+                             [0, 1, 0],
+                             [0, 0, -1]]),
+                  np.matrix([[0, 1, 0],   #Rotation ab-axis (2-fold)
+                             [1, 0, 0],
+                             [0, 0, -1]]),
+                 ]
+
+symmetries = get_all_symmetries(basesymmetries, 12)
+
+#Definition of surfaces
+surface_names = [(0,0,1), (0,0,-1), #(001)
+                 (1,-1,0), (-1,1,0), #
+                 (2,1,0), (-2,-1,0),
+                 (1,2,0), (-1,-2,0),
+                 (5,-5,3), (5,-5,-3), #
+                 (-5,5,3), (-5,5,-3),
+                 (5,10,3), (5,10,-3),
+                 (10,5,3), (10,5,-3),
+                 (-5,-10,3), (-5,-10,-3),
+                 (-10,-5,3), (-10,-5,-3),
+                 ]
+
+surface_numbers = {}
+for i, s in enumerate(surface_names):
+    surface_numbers[s] = i
+
+surface_count = len(surface_names)
+
+surface_mapping = {0:1, 1:0, 2:3, 3:2, 4:5, 5:4, 6:7, 7:6,
+                   }
+
+surface_data = ([{'d': 0.5}] * 2 +
+                [{'d': 0.5}] * 6 +
+                [{'d': 1.0/13.0}] * 12)
+
+surface_symmetries = get_surface_symmetries(symmetries, surface_names, surface_numbers)
+
+#Definition of neighbor environment
+neighbor_names = [(1,0,0), (-1,0,0),
+                  (0,1,0), (0,-1,0),
+                  (1,1,0), (-1,-1,0),
+                  (1.0/3.0,2.0/3.0,1.0/2.0), (1.0/3.0,-1.0/3.0,1.0/2.0), (-2.0/3.0,-1.0/3.0,1.0/2.0),
+                  (1.0/3.0,2.0/3.0,-1.0/2.0), (1.0/3.0,-1.0/3.0,-1.0/2.0), (-2.0/3.0,-1.0/3.0,-1.0/2.0),
+                  (2.0/3.0,1.0/3.0,1.0/2.0), (-1.0/3.0,-2.0/3.0,1.0/2.0), (-1.0/3.0,1.0/3.0,1.0/2.0),
+                  (2.0/3.0,1.0/3.0,-1.0/2.0), (-1.0/3.0,-2.0/3.0,-1.0/2.0), (-1.0/3.0,1.0/3.0,-1.0/2.0),
+                  ] 
+
+neighbor_numbers = {}
+for i, n in enumerate(neighbor_names):
+    neighbor_numbers[n] = i
+
+neighbor_positions = np.array(neighbor_names, dtype=float)
+
+neighbor_cutoff = 1.2
+neighbor_count = 16
+
+neighbor_mapping = {0:1, 1:0, 2:3, 3:2, 4:5, 5:4, 
+                    6:16, 7:17, 8:15, 9:13, 10:14, 11:12,
+                    12:11, 13:9, 14:10, 15:8, 16:6, 17:7,
+                    }
+
+neighbor_symmetries = get_neighbor_symmetries(symmetries,
+                                              neighbor_positions,
+                                              neighbor_numbers)
+
+#Definition of the atom types that is used based on the neighborlist
+basetype_names = []
+
+basetype_data = []
+
+type_count = len(basetype_names)
+
+type_names = []
+type_data = []
+for i, n in enumerate(basetype_names):
+    type_names.append(n)
+    type_data.append(basetype_data[i])
+
+    for sym in neighbor_symmetries:
+        new_type = apply_neighbor_symmetry(n, sym)
+
+        if not new_type in type_names:
+            type_names.append(new_type)
+            type_data.append(basetype_data[i])
+
+type_numbers = {}
+for i, n in enumerate(type_names):
+    type_numbers[n] = i
+
+#Collect all data
+data = {'symmetries': symmetries,
+        'surface_names': surface_names,
+        'surface_numbers': surface_numbers,
+        'surface_data': surface_data,
+        'surface_count': surface_count,
+        'surface_mapping': surface_mapping,
+        'surface_symmetries': surface_symmetries,
+        'neighbor_positions': neighbor_positions,
+        'neighbor_numbers': neighbor_numbers,
+        'neighbor_count': neighbor_count,
+        'neighbor_cutoff': neighbor_cutoff,
+        'neighbor_mapping': neighbor_mapping,
+        'neighbor_symmetries': neighbor_symmetries,
+        'type_names': type_names,
+        'type_numbers': type_numbers,
+        'type_data': type_data,
+        'type_count': type_count,
+       }
+
diff --git a/ase/cluster/data/symmetry.py b/ase/cluster/data/symmetry.py
new file mode 100644
index 0000000..db46a49
--- /dev/null
+++ b/ase/cluster/data/symmetry.py
@@ -0,0 +1,87 @@
+from fcc import *
+import numpy as np
+
+def get_all_symmetries(symmetries=None, max=99):
+    if symmetries is None:
+        raise Warning('Some unique symmetries are needed to start.')
+
+    symmetries_all = symmetries[:]
+
+    for i, l in enumerate(symmetries):
+        for j, m, in enumerate(symmetries):
+            if len(symmetries_all) == max: break
+            v = l * m
+
+            exist = False
+            for w in symmetries_all:
+                if (v == w).all():
+                    exist = True
+                    break
+
+            if not exist:
+                #print 'New added: %i x %i' % (i, j)
+                symmetries_all.append(v)
+
+    for i, l in enumerate(symmetries):
+        for j, m, in enumerate(symmetries):
+            for k, n in enumerate(symmetries):
+                if len(symmetries_all) == max: break
+                v = l * m * n
+
+                exist = False
+                for w in symmetries_all:
+                    if (v == w).all():
+                        exist = True
+                        break
+
+                if not exist:
+                    #print 'New added: %i x %i x %i' % (i, j, k)
+                    symmetries_all.append(v)
+
+    #print 'There are %i symmetry operations.' % len(symmetries_all)
+    return symmetries_all
+
+def get_neighbor_symmetries(symmetries=None, neighbor_positions=None, neighbor_numbers=None):
+    if symmetries is None or neighbor_positions is None or neighbor_numbers is None:
+        raise Warning('Both symmetries, positions and numbers for the neighbors are needed.')
+
+    neighbor_symmetries = []
+
+    for s in symmetries:
+        neighbor_symmetry = []
+        
+        for p in neighbor_positions:
+            new_p = np.array(np.matrix(p) * s)
+            neighbor_symmetry.append(neighbor_numbers[tuple(new_p[0])])
+
+        neighbor_symmetries.append(neighbor_symmetry)
+
+    return neighbor_symmetries
+
+def get_surface_symmetries(symmetries=None, surface_names=None, surface_numbers=None):
+    if symmetries is None or surface_names is None or surface_numbers is None:
+        raise Warning('Both symmetries, names and numbers for the surfaces are needed.')
+
+    surface_symmetries = []
+
+    for sym in symmetries:
+        surface_symmetry = []
+
+        for s in surface_names:
+            ns = np.array(np.matrix(s) * sym)
+            surface_symmetry.append(surface_numbers[tuple(ns[0])])
+
+        surface_symmetries.append(surface_symmetry)
+
+    return np.array(surface_symmetries, int)
+
+def apply_neighbor_symmetry(neighbors=None, symmetry=None):
+    if neighbors is None or symmetry is None:
+        raise Warning('Both neighbor list and symmetry list are needed.')
+
+    new_neighbors = [0] * len(symmetry)
+    
+    for i, n in enumerate(symmetry):
+        new_neighbors[i] = neighbors[n]
+
+    return tuple(new_neighbors)
diff --git a/ase/cluster/decahedron.py b/ase/cluster/decahedron.py
new file mode 100644
index 0000000..41df8d9
--- /dev/null
+++ b/ase/cluster/decahedron.py
@@ -0,0 +1,101 @@
+import numpy as np
+
+from ase import Atoms
+from ase.data import atomic_numbers, reference_states
+
+def Decahedron(symbol, p, q, r, latticeconstant=None):
+    """
+    Returns a cluster in the decahedra class.
+
+    Prameters
+    ---------
+    symbol: Chemical symbol (or atomic number) of the element.
+
+    p: Number of atoms on the (100) facets perpendicular to the five
+    fold axis.
+    
+    q: Number of atoms on the (100) facets parallel to the five fold
+    axis. q = 1 corresponds to no visible (100) facets.
+
+    r: Depth of the Marks re-entrence at the pentagon corners. r = 0
+    corresponds to no re-entrence.
+
+    latticeconstant (optional): The lattice constant. If not given,
+    then it is extracted form ase.data.
+    """
+
+    # Interpret symbol
+    if isinstance(symbol, str):
+        atomic_number = atomic_numbers[symbol]
+    else:
+        atomic_number = symbol
+
+    # Interpret lattice constant
+    if latticeconstant is None:
+        if reference_states[atomic_number]['symmetry'] in ['fcc', 'bcc', 'sc']:
+            lattice_constant = reference_states[atomic_number]['a']
+        else:
+            raise NotImplementedError(("Cannot guess lattice constant of a %s element." %
+                                      (reference_states[atomic_number]['symmetry'],)))
+    else:
+        if isinstance(latticeconstant, (int, float)):
+            lattice_constant = latticeconstant
+        else:
+            raise ValueError("Lattice constant must be of type int or float.")
+
+    # Check values of p, q, r
+    if p < 1 or q < 1:
+        raise ValueError("p and q must be greater than 0.")
+
+    if r < 0:
+        raise ValueError("r must be greater than or equal to 0.")
+
+    # Defining constants
+    t = 2.0*np.pi/5.0
+    b = lattice_constant/np.sqrt(2.0)
+    a = b*np.sqrt(3.0)/2.0
+
+    verticies = a * np.array([[np.cos(np.pi/2.), np.sin(np.pi/2.), 0.],
+                              [np.cos(t*1. + np.pi/2.), np.sin(t*1. + np.pi/2.), 0.],
+                              [np.cos(t*2. + np.pi/2.), np.sin(t*2. + np.pi/2.), 0.],
+                              [np.cos(t*3. + np.pi/2.), np.sin(t*3. + np.pi/2.), 0.],
+                              [np.cos(t*4. + np.pi/2.), np.sin(t*4. + np.pi/2.), 0.]])
+
+    # Number of atoms on the five fold axis and a nice constant
+    h = p + q + 2*r - 1
+    g = h - q + 1 # p + 2*r
+
+    positions = []
+    # Make the five fold axis
+    for j in range(h):
+        pos = np.array([0.0, 0.0, j*b - (h-1)*b/2.0])
+        positions.append(pos)
+
+    # Make pentagon rings around the five fold axis
+    for n in range(1, h):
+        # Condition for (100)-planes
+        if n < g:
+            for m in range(5):
+                v1 = verticies[m-1]
+                v2 = verticies[m]
+                for i in range(n):
+                    # Condition for marks re-entrence
+                    if n - i < g - r and i < g - r:
+                        for j in range(h-n):
+                            pos = (n-i)*v1 + i*v2
+                            pos += np.array([0.0, 0.0, j*b - (h-n-1)*b/2.0])
+                            positions.append(pos)
+
+    # Fit the cell, so it only just consist the atoms
+    min = np.zeros(3)
+    max = np.zeros(3)
+    axes = np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
+    for i in range(3):
+        r = np.dot(positions, axes[i])
+        min[i] = r.min()
+        max[i] = r.max()
+    cell = max - min
+    positions = np.array(positions) - min
+
+    symbols = [atomic_number] * len(positions)
+    return Atoms(symbols=symbols, positions=positions, cell=cell)
diff --git a/ase/cluster/factory.py b/ase/cluster/factory.py
new file mode 100644
index 0000000..1e0ccaf
--- /dev/null
+++ b/ase/cluster/factory.py
@@ -0,0 +1,239 @@
+import numpy as np
+
+from ase.data import atomic_numbers as ref_atomic_numbers
+from ase.lattice.spacegroup import Spacegroup
+from ase.cluster.base import ClusterBase
+from ase.cluster.cluster import Cluster
+
+class ClusterFactory(ClusterBase):
+    directions = [[1, 0, 0],
+                  [0, 1, 0],
+                  [0, 0, 1]]
+
+    atomic_basis = np.array([[0., 0., 0.]])
+
+    element_basis = None
+
+    def __call__(self, symbols, surfaces, layers, latticeconstant=None,
+                 center=None, vacuum=0.0, debug=0):
+        self.debug = debug
+
+        # Interpret symbol
+        self.set_atomic_numbers(symbols)
+
+        # Interpret lattice constant
+        if latticeconstant is None:
+            if self.element_basis is None:
+                self.lattice_constant = self.get_lattice_constant()
+            else:
+                raise ValueError("A lattice constant must be specified for a compound")
+        else:
+            self.lattice_constant = latticeconstant
+
+        self.set_basis()
+
+        if self.debug:
+            print "Lattice constant(s):", self.lattice_constant
+            print "Lattice basis:\n", self.lattice_basis
+            print "Resiprocal basis:\n", self.resiproc_basis
+            print "Atomic basis:\n", self.atomic_basis
+
+        self.set_surfaces_layers(surfaces, layers)
+        self.set_lattice_size(center)
+
+        if self.debug:
+            print "Center position:", self.center.round(2)
+            print "Base lattice size:", self.size
+
+        cluster = self.make_cluster(vacuum)
+        cluster.symmetry = self.xtal_name
+        cluster.surfaces = self.surfaces.copy()
+        cluster.lattice_basis = self.lattice_basis.copy()
+        cluster.atomic_basis = self.atomic_basis.copy()
+        cluster.resiproc_basis = self.resiproc_basis.copy()
+        return cluster
+
+    def make_cluster(self, vacuum):
+        # Make the base crystal by repeating the unit cell
+        size = np.array(self.size)
+        translations = np.zeros((size.prod(), 3))
+        for h in range(size[0]):
+            for k in range(size[1]):
+                for l in range(size[2]):
+                    i = h * (size[1] * size[2]) + k * size[2] + l
+                    translations[i] = np.dot([h, k, l], self.lattice_basis)
+
+        atomic_basis = np.dot(self.atomic_basis, self.lattice_basis)
+        positions = np.zeros((len(translations) * len(atomic_basis), 3))
+        numbers = np.zeros(len(positions))
+        n = len(atomic_basis)
+        for i, trans in enumerate(translations):
+            positions[n*i:n*(i+1)] = atomic_basis + trans
+            numbers[n*i:n*(i+1)] = self.atomic_numbers
+
+        # Remove all atoms that is outside the defined surfaces
+        for s, l in zip(self.surfaces, self.layers):
+            n = self.miller_to_direction(s)
+            rmax = self.get_layer_distance(s, l + 0.1)
+
+            r = np.dot(positions - self.center, n)
+            mask = np.less(r, rmax)
+
+            if self.debug > 1:
+                print "Cutting %s at %i layers ~ %.3f A" % (s, l, rmax)
+
+            positions = positions[mask]
+            numbers = numbers[mask]
+
+        # Fit the cell, so it only just consist the atoms
+        min = np.zeros(3)
+        max = np.zeros(3)
+        for i in range(3):
+            v = self.directions[i]
+            r = np.dot(positions, v)
+            min[i] = r.min()
+            max[i] = r.max()
+
+        cell = max - min + vacuum
+        positions = positions - min + vacuum / 2.0
+        self.center = self.center - min + vacuum / 2.0
+
+        return Cluster(symbols=numbers, positions=positions, cell=cell)
+
+    def set_atomic_numbers(self, symbols):
+        "Extract atomic number from element"
+        # The types that can be elements: integers and strings
+        atomic_numbers = []
+        if self.element_basis is None:
+            if isinstance(symbols, str):
+                atomic_numbers.append(ref_atomic_numbers[symbols])
+            elif isinstance(symbols, int):
+                atomic_numbers.append(symbols)
+            else:
+                raise TypeError("The symbol argument must be a " +
+                                "string or an atomic number.")
+            element_basis = [0] * len(self.atomic_basis)
+        else:
+            if isinstance(symbols, (list, tuple)):
+                nsymbols = len(symbols)
+            else:
+                nsymbols = 0
+
+            nelement_basis = max(self.element_basis) + 1
+            if nsymbols != nelement_basis:
+                raise TypeError("The symbol argument must be a sequence " +
+                                "of length %d" % (nelement_basis,) +
+                                " (one for each kind of lattice position")
+
+            for s in symbols:
+                if isinstance(s, str):
+                    atomic_numbers.append(ref_atomic_numbers[s])
+                elif isinstance(s, int):
+                    atomic_numbers.append(s)
+                else:
+                    raise TypeError("The symbol argument must be a " +
+                                    "string or an atomic number.")
+            element_basis = self.element_basis
+
+        self.atomic_numbers = [atomic_numbers[n] for n in element_basis]
+        assert len(self.atomic_numbers) == len(self.atomic_basis)
+
+    def set_lattice_size(self, center):
+        if center is None:
+            offset = np.zeros(3)
+        else:
+            offset = np.array(center)
+            if (offset > 1.0).any() or (offset < 0.0).any():
+                raise ValueError("Center offset must lie within the lattice unit \
+                                  cell.")
+
+        max = np.ones(3)
+        min = -np.ones(3)
+        v = np.linalg.inv(self.lattice_basis.T)
+        for s, l in zip(self.surfaces, self.layers):
+            n = self.miller_to_direction(s) * self.get_layer_distance(s, l)
+            k = np.round(np.dot(v, n), 2)
+            for i in range(3):
+                if k[i] > 0.0:
+                    k[i] = np.ceil(k[i])
+                elif k[i] < 0.0:
+                    k[i] = np.floor(k[i])
+
+            if self.debug > 1:
+                print "Spaning %i layers in %s in lattice basis ~ %s" % (l, s, k)
+
+            max[k > max] = k[k > max]
+            min[k < min] = k[k < min]
+
+        self.center = np.dot(offset - min, self.lattice_basis)
+        self.size = (max - min + np.ones(3)).astype(int)
+
+    def set_surfaces_layers(self, surfaces, layers):
+        if len(surfaces) != len(layers):
+            raise ValueError("Improper size of surface and layer arrays: %i != %i"
+                             % (len(surfaces), len(layers)))
+
+        sg = Spacegroup(self.spacegroup)
+        surfaces = np.array(surfaces)
+        layers = np.array(layers)
+
+        for i, s in enumerate(surfaces):
+            s = reduce_miller(s)
+            surfaces[i] = s
+
+        surfaces_full = surfaces.copy()
+        layers_full = layers.copy()
+
+        for s, l in zip(surfaces, layers):
+            equivalent_surfaces = sg.equivalent_reflections(s.reshape(-1, 3))
+
+            for es in equivalent_surfaces:
+                # If the equivalent surface (es) is not in the surface list,
+                # then append it.
+                if not np.equal(es, surfaces_full).all(axis=1).any():
+                    surfaces_full = np.append(surfaces_full, es.reshape(1, 3), axis=0)
+                    layers_full = np.append(layers_full, l)
+
+        self.surfaces = surfaces_full.copy()
+        self.layers = layers_full.copy()
+
+    def get_resiproc_basis(self, basis):
+        """Returns the resiprocal basis to a given lattice (crystal) basis"""
+        k = 1 / np.dot(basis[0], cross(basis[1], basis[2]))
+
+        # The same as the inversed basis matrix transposed
+        return k * np.array([cross(basis[1], basis[2]),
+                             cross(basis[2], basis[0]),
+                             cross(basis[0], basis[1])])
+
+# Helping functions
+def cross(a, b):
+    """The cross product of two vectors."""
+    return np.array([a[1]*b[2] - b[1]*a[2],
+                     a[2]*b[0] - b[2]*a[0],
+                     a[0]*b[1] - b[0]*a[1]])
+
+def GCD(a,b):
+    """Greatest Common Divisor of a and b."""
+    #print "--"
+    while a != 0:
+        #print a,b,">",
+        a,b = b%a,a
+        #print a,b
+    return b
+
+def reduce_miller(hkl):
+    """Reduce Miller index to the lowest equivalent integers."""
+    hkl = np.array(hkl)
+    old = hkl.copy()
+
+    d = GCD(GCD(hkl[0], hkl[1]), hkl[2])
+    while d != 1:
+        hkl = hkl / d
+        d = GCD(GCD(hkl[0], hkl[1]), hkl[2])
+
+    if np.dot(old, hkl) > 0:
+        return hkl
+    else:
+        return -hkl
+
diff --git a/ase/cluster/hexagonal.py b/ase/cluster/hexagonal.py
new file mode 100644
index 0000000..d27e49f
--- /dev/null
+++ b/ase/cluster/hexagonal.py
@@ -0,0 +1,78 @@
+"""
+Function-like objects that creates cubic clusters.
+"""
+
+import numpy as np
+from ase.cluster.factory import ClusterFactory
+from ase.data import reference_states as _refstate
+
+class HexagonalFactory(ClusterFactory):
+    spacegroup = 191
+
+    xtal_name = 'hexagonal'
+
+    def get_lattice_constant(self, latticeconstant):
+        "Get the lattice constant of an element with cubic crystal structure."
+        symmetry = _refstate[self.atomic_numbers[0]]['symmetry']
+        if symmetry != self.xtal_name:
+            raise ValueError, ("Cannot guess the %s " % (self.xtal_name,) +
+                               "lattice constant of an element with crystal " +
+                               "structure %s." % (symmetry,))
+        return _refstate[self.atomic_numbers[0]].copy()
+
+    def set_basis(self):
+        lattice = self.lattice_constant
+        if isinstance(lattice, dict):
+            a = lattice['a']
+            try:
+                c = lattice['c']
+            except KeyError:
+                c = a * lattice['c/a']
+        else:
+            if len(lattice) == 2:
+                (a, c) = lattice
+            else:
+                raise ValueError("Improper lattice constants for %s crystal." % (self.xtal_name,))
+        
+        self.lattice_constant = (a, c)
+        self.lattice_basis = np.array([[a, 0., 0.],
+                                       [-a/2., a*np.sqrt(3.)/2., 0.],
+                                       [0., 0., c]])
+        self.resiproc_basis = self.get_resiproc_basis(self.lattice_basis)
+
+    def set_surfaces_layers(self, surfaces, layers):
+        for i, s in enumerate(surfaces):
+            if len(s) == 4:
+                (a, b, c, d) = s
+                if a + b + c != 0:
+                    raise ValueError(("(%d,%d,%d,%d) is not a valid hexagonal Miller " +
+                                      "index, as the sum of the first three numbers " +
+                                      "should be zero.") % (a,b,c,d))
+                surfaces[i] = [a, b, d]
+
+        ClusterFactory.set_surfaces_layers(self, surfaces, layers)
+
+Hexagonal = HexagonalFactory()
+
+class HexagonalClosedPackedFactory(HexagonalFactory):
+    """A factory for creating HCP clusters."""
+    spacegroup = 194
+
+    xtal_name = 'hcp'
+
+    atomic_basis = np.array([[0., 0., 0.],
+                             [2./3., 1./3., .5]])
+
+HexagonalClosedPacked = HexagonalClosedPackedFactory()
+
+class GraphiteFactory(HexagonalFactory):
+    """A factory for creating graphite clusters."""
+    xtal_name = "graphite"
+
+    atomic_basis = np.array([[0., 0., 0.],
+                             [1./3., 2./3., 0.],
+                             [1./3., 2./3., .5],
+                             [2./3., 1./3., .5]])
+
+Graphite = GraphiteFactory()
+
diff --git a/ase/cluster/icosahedron.py b/ase/cluster/icosahedron.py
new file mode 100644
index 0000000..ef7c206
--- /dev/null
+++ b/ase/cluster/icosahedron.py
@@ -0,0 +1,128 @@
+import numpy as np
+
+from ase import Atoms
+from ase.data import atomic_numbers, reference_states
+
+def Icosahedron(symbol, noshells, latticeconstant=None):
+    """
+    Returns a cluster with the icosahedra symmetry.
+
+    Parameters
+    ----------
+    symbol: The chemical symbol (or atomic number) of the element.
+
+    noshells: The number of shells (>= 1).
+
+    latticeconstant (optional): The lattice constant. If not given,
+    then it is extracted form ase.data.
+    """
+
+    # Interpret symbol
+    if isinstance(symbol, str):
+        atomic_number = atomic_numbers[symbol]
+    else:
+        atomic_number = symbol
+
+    # Interpret noshells
+    if noshells < 1:
+        raise ValueError("The number of shells must be equal to or greater than one.")
+
+    # Interpret lattice constant
+    if latticeconstant is None:
+        if reference_states[atomic_number]['symmetry'] in ['fcc', 'bcc', 'sc']:
+            lattice_constant = reference_states[atomic_number]['a']
+        else:
+            raise NotImplementedError(("Cannot guess lattice constant of a %s element." %
+                                      (reference_states[atomic_number]['symmetry'],)))
+    else:
+        if isinstance(latticeconstant, (int, float)):
+            lattice_constant = latticeconstant
+        else:
+            raise ValueError("Lattice constant must be of type int or float.")
+
+    t = 0.5 + np.sqrt(5)/2.0
+
+    verticies = np.array([[t, 0., 1.],
+                          [t, 0., -1.],
+                          [-t, 0., 1.],
+                          [-t, 0., -1.],
+                          [1., t, 0.],
+                          [-1., t, 0.],
+                          [1., -t, 0.],
+                          [-1., -t, 0.],
+                          [0., 1., t],
+                          [0., -1., t],
+                          [0., 1., -t],
+                          [0., -1., -t]])
+
+    positions = []
+    tags = []
+    positions.append(np.zeros(3))
+    tags.append(1)
+
+    for n in range(1, noshells):
+        #Construct square edges (6)
+        for k in range(0, 12, 2):
+            v1 = verticies[k]
+            v2 = verticies[k+1]
+            for i in range(n+1):
+                pos = i*v1 + (n-i)*v2
+                positions.append(pos)
+                tags.append(n + 1)
+
+        #Construct triangle planes (12)
+        if n > 1:
+            map = {0: (8, 9), 1: (10, 11),
+                   2: (8, 9), 3: (10, 11),
+                   4: (0, 1), 5: (2, 3),
+                   6: (0, 1), 7: (2, 3),
+                   8: (4, 5), 9: (6, 7),
+                   10: (4, 5), 11: (6, 7)}
+
+            for k in range(0, 12):
+                v0 = n*verticies[k]
+                v1 = (verticies[map[k][0]] - verticies[k])
+                v2 = (verticies[map[k][1]] - verticies[k])
+                for i in range(n):
+                    for j in range(n-i):
+                        if i == 0 and j == 0:
+                            continue
+                        pos = v0 + i*v1 + j*v2
+                        positions.append(pos)
+                        tags.append(n + 1)
+
+        #Fill missing triangle planes (8)
+        if n > 2:
+            map = {0: (9, 6, 8, 4,),
+                   1: (11, 6, 10, 4),
+                   2: (9, 7, 8, 5,),
+                   3: (11, 7, 10, 5)}
+
+            for k in range(0, 4):
+                v0 = n*verticies[k]
+                v1 = (verticies[map[k][0]] - verticies[k])
+                v2 = (verticies[map[k][1]] - verticies[k])
+                v3 = (verticies[map[k][2]] - verticies[k])
+                v4 = (verticies[map[k][3]] - verticies[k])
+                for i in range(1, n):
+                    for j in range(1, n-i):
+                        pos = v0 + i*v1 + j*v2
+                        positions.append(pos)
+                        tags.append(n + 1)
+                        pos = v0 + i*v3 + j*v4
+                        positions.append(pos)
+                        tags.append(n + 1)
+
+    # Scale the positions
+    scaling_factor = lattice_constant / np.sqrt(2*(1 + t**2))
+    positions = np.array(positions) * scaling_factor
+
+    # Fit the cell, so it only just consist the atoms
+    min = positions.min(axis=0)
+    max = positions.max(axis=0)
+    cell = max - min
+    positions = positions - min
+
+    symbols = [atomic_number] * len(positions)
+    return Atoms(symbols=symbols, positions=positions, tags=tags, cell=cell)
+
diff --git a/ase/cluster/octahedron.py b/ase/cluster/octahedron.py
new file mode 100644
index 0000000..26f2c52
--- /dev/null
+++ b/ase/cluster/octahedron.py
@@ -0,0 +1,56 @@
+"""
+Function-like objects that creates cubic clusters.
+"""
+
+import numpy as np
+
+from ase.cluster.cubic import FaceCenteredCubic
+from ase.cluster.compounds import L1_2
+
+def Octahedron(symbol, length, cutoff=0, latticeconstant=None, alloy=False):
+    """
+    Returns Face Centered Cubic clusters of the octahedral class depending
+    on the choice of cutoff.
+
+    Type                            Condition
+    ----                            ---------
+    Regular octahedron              cutoff = 0
+    Truncated octahedron            cutoff > 0
+    Regular truncated octahedron    length = 3 * cutoff + 1
+    Cuboctahedron                   length = 2 * cutoff + 1
+
+    Parameters
+    ----------
+    symbol: The chemical symbol or atomic number of the element(s).
+
+    length: Number of atoms on the square edges of the complete octahedron.
+
+    cutoff (optional): Number of layers cut at each vertex.
+
+    latticeconstant (optional): The lattice constant. If not given,
+    then it is extracted form ase.data.
+
+    alloy (optional): If true the L1_2 structure is used. Default is False.
+    """
+
+    # Check length and cutoff
+    if length < 2:
+        raise ValueError("The lenght must be greater than one.")
+
+    if cutoff < 0 or length < 2 * cutoff + 1:
+        raise ValueError("The cutoff must fullfill: > 0 and <= (length - 1) / 2.")
+
+    # Create cluster
+    surfaces = [(1,1,1), (1,0,0)]
+    if length % 2 == 0:
+        center = np.array([0.5, 0.5, 0.5])
+        layers = [length/2, length - 1 - cutoff]
+    else:
+        center = np.array([0.0, 0.0, 0.0])
+        layers = [(length - 1)/2, length - 1 - cutoff]
+
+    if not alloy:
+        return FaceCenteredCubic(symbol, surfaces, layers, latticeconstant, center)
+    else:
+        return L1_2(symbol, surfaces, layers, latticeconstant, center)
+
diff --git a/ase/cluster/wulff.py b/ase/cluster/wulff.py
new file mode 100644
index 0000000..47b4879
--- /dev/null
+++ b/ase/cluster/wulff.py
@@ -0,0 +1,194 @@
+import numpy as np
+
+import ase.cluster
+from ase.cluster.cubic import FaceCenteredCubic
+from ase.data import atomic_numbers, reference_states
+
+delta = 1e-10
+_debug = None
+
+def wulff_construction(symbol, surfaces, energies, size, structure,
+                       rounding="closest", latticeconstant=None, debug=0):
+    """Create a cluster using the Wulff construction.
+
+    A cluster is created with approximately the number of atoms
+    specified, following the Wulff construction, i.e. minimizing the
+    surface energy of the cluster.
+
+    Parameters:
+    -----------
+
+    symbol: The chemical symbol (or atomic number) of the desired element.
+
+    surfaces: A list of surfaces.
+
+    energies: A list of surface energies for the surfaces.
+
+    size: The desired number of atoms.
+
+    structure: The desired crystal structure.  Either one of the strings
+    "fcc", "bcc", "sc", "hcp", "graphite"; or one of the cluster factory
+    objects from the ase.cluster.XXX modules.
+    
+    rounding (optional): Specifies what should be done if no Wulff
+    construction corresponds to exactly the requested number of atoms.
+    Should be a string, either "above", "below" or "closest" (the
+    default), meaning that the nearest cluster above or below - or the
+    closest one - is created instead.
+
+    latticeconstant (optional): The lattice constant.  If not given,
+    extracted from ase.data.
+
+    debug (optional): If non-zero, information about the iteration towards
+    the right cluster size is printed.
+    """
+
+    global _debug
+    _debug = debug
+
+    if debug:
+        print "Wulff: Aiming for cluster with %i atoms (%s)" % (size, rounding)
+        
+    if rounding not in ["above", "below", "closest"]:
+        raise ValueError("Invalid rounding: "+rounding)
+    
+    # Interpret symbol
+    if isinstance(symbol, str):
+        atomic_number = atomic_numbers[symbol]
+    else:
+        atomic_number = symbol
+
+    # Interpret structure, if it is a string.
+    if isinstance(structure, str):
+        if structure == 'fcc':
+            from ase.cluster.cubic import FaceCenteredCubic as structure
+        elif structure == 'bcc':
+            from ase.cluster.cubic import BodyCenteredCubic as structure
+        elif structure == 'sc':
+            from ase.cluster.cubic import SimpleCubic as structure
+        elif structure == 'hcp':
+            from ase.cluster.hexagonal import HexagonalClosedPacked as structure
+        elif structure == 'graphite':
+            from ase.cluster.hexagonal import Graphite as structure
+        else:
+            raise NotImplementedError("Crystal structure "+structure+
+                                      " is not supported.")
+
+    # Check number of surfaces
+    nsurf = len(surfaces)
+    if len(energies) != nsurf:
+        raise ValueError("The energies array should contain %d values."
+                         % (nsurf,))
+
+    # We should check that for each direction, the surface energy plus
+    # the energy in the opposite direction is positive.  But this is
+    # very difficult in the general case!
+
+    # Before starting, make a fake cluster just to extract the
+    # interlayer distances in the relevant directions, and use these
+    # to "renormalize" the surface energies such that they can be used
+    # to convert to number of layers instead of to distances.
+    atoms = structure(symbol, surfaces, 5*np.ones(len(surfaces), int),
+                      latticeconstant=latticeconstant)
+    for i, s in enumerate(surfaces):
+        d = atoms.get_layer_distance(s)
+        energies[i] /= d
+        
+    # First guess a size that is not too large.
+    wanted_size = size**(1.0/3.0)
+    max_e = max(energies)
+    factor = wanted_size / max_e
+    #layers = np.array([int(round(factor * e)) for e in energies])
+    atoms, layers = make_atoms(symbol, surfaces, energies, factor, structure,
+                               latticeconstant)
+    if len(atoms) == 0:
+        # Probably the cluster is very flat
+        if debug:
+            print "First try made an empty cluster, trying again."
+        factor = 1 / energies_sum.min()
+        atoms, layers = make_atoms(symbol, surfaces, energies, factor,
+                                   structure, latticeconstant)
+        if len(atoms) == 0:
+            raise RuntimeError("Failed to create a finite cluster.")
+
+    # Second guess: scale to get closer.
+    old_factor = factor
+    old_layers = layers
+    old_atoms = atoms
+    factor *= (size / len(atoms))**(1.0/3.0)
+    atoms, layers = make_atoms(symbol, surfaces, energies, factor,
+                               structure, latticeconstant)
+    if len(atoms) == 0:
+        print "Second guess gave an empty cluster, discarding it."
+        atoms = old_atoms
+        factor = old_factor
+        layers = old_layers
+    else:
+        del old_atoms
+
+    # Find if the cluster is too small or too large (both means perfect!)
+    below = above = None
+    if len(atoms) <= size:
+        below = atoms
+    if len(atoms) >= size:
+        above = atoms
+
+    # Now iterate towards the right cluster
+    iter = 0
+    while (below is None or above is None):
+        if len(atoms) < size:
+            # Find a larger cluster
+            if debug:
+                print "Making a larger cluster."
+            factor = ((layers + 0.5 + delta) / energies).min()
+            atoms, new_layers = make_atoms(symbol, surfaces, energies, factor,
+                                           structure, latticeconstant)
+            assert (new_layers - layers).max() == 1
+            assert (new_layers - layers).min() >= 0
+            layers = new_layers
+        else:
+            # Find a smaller cluster
+            if debug:
+                print "Making a smaller cluster."
+            factor = ((layers - 0.5 - delta) / energies).max()
+            atoms, new_layers = make_atoms(symbol, surfaces, energies, factor,
+                                           structure, latticeconstant)
+            assert (new_layers - layers).max() <= 0
+            assert (new_layers - layers).min() == -1
+            layers = new_layers
+        if len(atoms) <= size:
+            below = atoms
+        if len(atoms) >= size:
+            above = atoms
+        iter += 1
+        if iter == 100:
+            raise RuntimeError("Runaway iteration.")
+    if rounding == "below":
+        if debug:
+            print "Choosing smaller cluster with %i atoms" % (len(below),)
+        return below
+    elif rounding == "above":
+        if debug:
+            print "Choosing larger cluster with %i atoms" % (len(above),)
+        return above
+    else:
+        assert rounding == "closest"
+        if (len(above) - size) < (size - len(below)):
+            atoms = above
+        else:
+            atoms = below
+        if debug:
+            print "Choosing closest cluster with %i atoms" % (len(atoms),)
+        return atoms
+
+def make_atoms(symbol, surfaces, energies, factor, structure, latticeconstant):
+    layers1 = factor * np.array(energies)
+    layers = np.round(layers1).astype(int)
+    #layers = np.array([int(round(factor * e)) for e in energies])
+    atoms = structure(symbol, surfaces, layers,
+                      latticeconstant=latticeconstant)
+    if _debug:
+        print "Created a cluster with %i atoms: %s" % (len(atoms),
+                                                       str(layers))
+        #print layers1
+    return (atoms, layers)
diff --git a/ase/constraints.py b/ase/constraints.py
new file mode 100644
index 0000000..36d89de
--- /dev/null
+++ b/ase/constraints.py
@@ -0,0 +1,1131 @@
+from math import sqrt
+
+import numpy as np
+
+__all__ = ['FixCartesian', 'FixBondLength', 'FixedMode', 'FixConstraintSingle',
+           'FixAtoms', 'UnitCellFilter', 'FixScaled', 'StrainFilter',
+           'FixedPlane', 'Filter', 'FixConstraint', 'FixedLine',
+           'FixBondLengths', 'FixInternals']
+
+
+def slice2enlist(s):
+    """Convert a slice object into a list of (new, old) tuples."""
+    if isinstance(s, (list, tuple)):
+        return enumerate(s)
+    if s.step == None:
+        step = 1
+    else:
+        step = s.step
+    if s.start == None:
+        start = 0
+    else:
+        start = s.start
+    return enumerate(range(start, s.stop, step))
+
+
+class FixConstraint:
+    """Base class for classes that fix one or more atoms in some way."""
+
+    def index_shuffle(self, ind):
+        """Change the indices.
+
+        When the ordering of the atoms in the Atoms object changes,
+        this method can be called to shuffle the indices of the
+        constraints.
+
+        ind -- List or tuple of indices.
+
+        """
+        raise NotImplementedError
+
+    def repeat(self, m, n):
+        """ basic method to multiply by m, needs to know the length
+        of the underlying atoms object for the assignment of
+        multiplied constraints to work.
+        """
+        raise NotImplementedError
+
+
+class FixConstraintSingle(FixConstraint):
+    """Base class for classes that fix a single atom."""
+
+    def index_shuffle(self, ind):
+        """The atom index must be stored as self.a."""
+        newa = -1   # Signal error
+        for new, old in slice2enlist(ind):
+            if old == self.a:
+                newa = new
+                break
+        if newa == -1:
+            raise IndexError('Constraint not part of slice')
+        self.a = newa
+
+
+class FixAtoms(FixConstraint):
+    """Constraint object for fixing some chosen atoms."""
+    def __init__(self, indices=None, mask=None):
+        """Constrain chosen atoms.
+
+        Parameters
+        ----------
+        indices : list of int
+           Indices for those atoms that should be constrained.
+        mask : list of bool
+           One boolean per atom indicating if the atom should be
+           constrained or not.
+
+        Examples
+        --------
+        Fix all Copper atoms:
+
+        >>> c = FixAtoms(mask=[s == 'Cu' for s in atoms.get_chemical_symbols()])
+        >>> atoms.set_constraint(c)
+
+        Fix all atoms with z-coordinate less than 1.0 Angstrom:
+
+        >>> c = FixAtoms(mask=atoms.positions[:, 2] < 1.0)
+        >>> atoms.set_constraint(c)
+        """
+
+        if indices is None and mask is None:
+            raise ValueError('Use "indices" or "mask".')
+        if indices is not None and mask is not None:
+            raise ValueError('Use only one of "indices" and "mask".')
+
+        if mask is not None:
+            self.index = np.asarray(mask, bool)
+        else:
+            # Check for duplicates
+            srt = np.sort(indices)
+            for i in range(len(indices) - 1):
+                if srt[i] == srt[i+1]:
+                    raise ValueError(
+                        'FixAtoms: The indices array contained duplicates. '
+                        'Perhaps you wanted to specify a mask instead, but '
+                        'forgot the mask= keyword.')
+            self.index = np.asarray(indices, int)
+
+        if self.index.ndim != 1:
+            raise ValueError('Wrong argument to FixAtoms class!')
+
+    def adjust_positions(self, old, new):
+        new[self.index] = old[self.index]
+
+    def adjust_forces(self, positions, forces):
+        forces[self.index] = 0.0
+
+    def index_shuffle(self, ind):
+        # See docstring of superclass
+        if self.index.dtype == bool:
+            self.index = self.index[ind]
+        else:
+            index = []
+            for new, old in slice2enlist(ind):
+                if old in self.index:
+                    index.append(new)
+            if len(index) == 0:
+                raise IndexError('All indices in FixAtoms not part of slice')
+            self.index = np.asarray(index, int)
+
+    def copy(self):
+        if self.index.dtype == bool:
+            return FixAtoms(mask=self.index.copy())
+        else:
+            return FixAtoms(indices=self.index.copy())
+
+    def __repr__(self):
+        if self.index.dtype == bool:
+            return 'FixAtoms(mask=%s)' % ints2string(self.index.astype(int))
+        return 'FixAtoms(indices=%s)' % ints2string(self.index)
+
+    def repeat(self, m, n):
+        i0 = 0
+        l = len(self.index)
+        natoms = 0
+        if isinstance(m, int):
+            m = (m, m, m)
+        index_new = []
+        for m2 in range(m[2]):
+            for m1 in range(m[1]):
+                for m0 in range(m[0]):
+                    i1 = i0 + n
+                    if self.index.dtype == bool:
+                        index_new.extend(self.index)
+                    else:
+                        index_new += [i+natoms for i in self.index]
+                    i0 = i1
+                    natoms += n
+        if self.index.dtype == bool:
+            self.index = np.asarray(index_new, bool)
+        else:
+            self.index = np.asarray(index_new, int)
+        return self
+
+    def delete_atom(self, ind):
+        """ Removes atom number ind from the index array, if present.
+        Required for removing atoms with existing FixAtoms constraints.
+        """
+        if self.index.dtype == bool:
+            self.index = np.delete(self.index, ind)
+        else:
+            if ind in self.index:
+                i = list(self.index).index(ind)
+                self.index = np.delete(self.index, i)
+            for i in range(len(self.index)):
+                if self.index[i] >= ind:
+                    self.index[i] -= 1
+
+def ints2string(x, threshold=10):
+    """Convert ndarray of ints to string."""
+    if len(x) <= threshold:
+        return str(x.tolist())
+    return str(x[:threshold].tolist())[:-1] + ', ...]'
+
+class FixBondLengths(FixConstraint):
+    def __init__(self, pairs, iterations=10):
+        self.constraints = [FixBondLength(a1, a2)
+                            for a1, a2 in pairs]
+        self.iterations = iterations
+
+    def adjust_positions(self, old, new):
+        for i in range(self.iterations):
+            for constraint in self.constraints:
+                constraint.adjust_positions(old, new)
+
+    def adjust_forces(self, positions, forces):
+        for i in range(self.iterations):
+            for constraint in self.constraints:
+                constraint.adjust_forces(positions, forces)
+
+    def copy(self):
+        return FixBondLengths([constraint.indices
+                               for constraint in self.constraints])
+
+class FixBondLength(FixConstraint):
+    """Constraint object for fixing a bond length."""
+    def __init__(self, a1, a2):
+        """Fix distance between atoms with indices a1 and a2."""
+        self.indices = [a1, a2]
+
+    def adjust_positions(self, old, new):
+        p1, p2 = old[self.indices]
+        d = p2 - p1
+        p = sqrt(np.dot(d, d))
+        q1, q2 = new[self.indices]
+        d = q2 - q1
+        q = sqrt(np.dot(d, d))
+        d *= 0.5 * (p - q) / q
+        new[self.indices] = (q1 - d, q2 + d)
+
+    def adjust_forces(self, positions, forces):
+        d = np.subtract.reduce(positions[self.indices])
+        d2 = np.dot(d, d)
+        d *= 0.5 * np.dot(np.subtract.reduce(forces[self.indices]), d) / d2
+        forces[self.indices] += (-d, d)
+
+    def index_shuffle(self, ind):
+        'Shuffle the indices of the two atoms in this constraint'
+        newa = [-1, -1] # Signal error
+        for new, old in slice2enlist(ind):
+            for i, a in enumerate(self.indices):
+                if old == a:
+                    newa[i] = new
+        if newa[0] == -1 or newa[1] == -1:
+            raise IndexError('Constraint not part of slice')
+        self.indices = newa
+
+    def copy(self):
+        return FixBondLength(*self.indices)
+
+    def __repr__(self):
+        return 'FixBondLength(%d, %d)' % tuple(self.indices)
+
+class FixedMode(FixConstraint):
+    """Constrain atoms to move along directions orthogonal to
+    a given mode only."""
+
+    def __init__(self, indices, mode):
+        if indices is None:
+            raise ValueError('Use "indices".')
+        if indices is not None:
+            self.index = np.asarray(indices, int)
+        self.mode = (np.asarray(mode) / np.sqrt((mode **2).sum())).reshape(-1)
+
+    def adjust_positions(self, oldpositions, newpositions):
+        newpositions = newpositions.ravel()
+        oldpositions = oldpositions.ravel()
+        step = newpositions - oldpositions
+        newpositions -= self.mode * np.dot(step, self.mode)
+        newpositions = newpositions.reshape(-1, 3)
+        oldpositions = oldpositions.reshape(-1, 3)
+
+    def adjust_forces(self, positions, forces):
+        forces = forces.ravel()
+        forces -= self.mode * np.dot(forces, self.mode)
+        forces = forces.reshape(-1, 3)
+
+    def copy(self):
+        return FixedMode(self.index.copy(), self.mode)
+
+    def __repr__(self):
+        return 'FixedMode(%s, %s)' % (ints2string(self.index),
+                                      self.mode.tolist())
+
+class FixedPlane(FixConstraintSingle):
+    """Constrain an atom *a* to move in a given plane only.
+
+    The plane is defined by its normal: *direction*."""
+
+    def __init__(self, a, direction):
+        self.a = a
+        self.dir = np.asarray(direction) / sqrt(np.dot(direction, direction))
+
+    def adjust_positions(self, oldpositions, newpositions):
+        step = newpositions[self.a] - oldpositions[self.a]
+        newpositions[self.a] -= self.dir * np.dot(step, self.dir)
+
+    def adjust_forces(self, positions, forces):
+        forces[self.a] -= self.dir * np.dot(forces[self.a], self.dir)
+
+    def copy(self):
+        return FixedPlane(self.a, self.dir)
+
+    def __repr__(self):
+        return 'FixedPlane(%d, %s)' % (self.a, self.dir.tolist())
+
+
+class FixedLine(FixConstraintSingle):
+    """Constrain an atom *a* to move on a given line only.
+
+    The line is defined by its *direction*."""
+
+    def __init__(self, a, direction):
+        self.a = a
+        self.dir = np.asarray(direction) / sqrt(np.dot(direction, direction))
+
+    def adjust_positions(self, oldpositions, newpositions):
+        step = newpositions[self.a] - oldpositions[self.a]
+        x = np.dot(step, self.dir)
+        newpositions[self.a] = oldpositions[self.a] + x * self.dir
+
+    def adjust_forces(self, positions, forces):
+        forces[self.a] = self.dir * np.dot(forces[self.a], self.dir)
+
+    def copy(self):
+        return FixedLine(self.a, self.dir)
+
+    def __repr__(self):
+        return 'FixedLine(%d, %s)' % (self.a, self.dir.tolist())
+
+class FixCartesian(FixConstraintSingle):
+    "Fix an atom in the directions of the cartesian coordinates."
+    def __init__(self, a, mask=(1, 1, 1)):
+        self.a = a
+        self.mask = -(np.array(mask) - 1)
+
+    def adjust_positions(self, old, new):
+        step = new[self.a] - old[self.a]
+        step *= self.mask
+        new[self.a] = old[self.a] + step
+
+    def adjust_forces(self, positions, forces):
+        forces[self.a] *= self.mask
+
+    def copy(self):
+        return FixCartesian(self.a, 1 - self.mask)
+
+    def __repr__(self):
+        return 'FixCartesian(indice=%s mask=%s)' % (self.a, self.mask)
+
+class fix_cartesian(FixCartesian):
+    "Backwards compatibility for FixCartesian."
+    def __init__(self, a, mask=(1, 1, 1)):
+        import warnings
+        super(fix_cartesian, self).__init__(a, mask)
+        warnings.warn('fix_cartesian is deprecated. Please use FixCartesian'
+                      ' instead.', DeprecationWarning, stacklevel=2)
+
+class FixScaled(FixConstraintSingle):
+    "Fix an atom in the directions of the unit vectors."
+    def __init__(self, cell, a, mask=(1, 1, 1)):
+        self.cell = cell
+        self.a = a
+        self.mask = np.array(mask)
+
+    def adjust_positions(self, old, new):
+        scaled_old = np.linalg.solve(self.cell.T, old.T).T
+        scaled_new = np.linalg.solve(self.cell.T, new.T).T
+        for n in range(3):
+            if self.mask[n]:
+                scaled_new[self.a, n] = scaled_old[self.a, n]
+        new[self.a] = np.dot(scaled_new, self.cell)[self.a]
+
+    def adjust_forces(self, positions, forces):
+        scaled_forces = np.linalg.solve(self.cell.T, forces.T).T
+        scaled_forces[self.a] *= -(self.mask - 1)
+        forces[self.a] = np.dot(scaled_forces, self.cell)[self.a]
+
+    def copy(self):
+        return FixScaled(self.cell, self.a, self.mask)
+
+    def __repr__(self):
+        return 'FixScaled(%s, %d, %s)' % (repr(self.cell),
+                                          self.a,
+                                          repr(self.mask))
+
+class fix_scaled(FixScaled):
+    "Backwards compatibility for FixScaled."
+    def __init__(self, cell, a, mask=(1, 1, 1)):
+        import warnings
+        super(fix_scaled, self).__init__(cell, a, mask)
+        warnings.warn('fix_scaled is deprecated. Please use FixScaled '
+                      'instead.', DeprecationWarning, stacklevel=2)
+
+class FixInternals(FixConstraint):
+    """Constraint object for fixing multiple internal coordinates.
+
+    Allows fixing bonds, angles, and dihedrals."""
+    def __init__(self, atoms=None, bonds=None, angles=None, dihedrals=None,
+             epsilon=1.e-7, _copy_init=None):
+        if _copy_init is None:
+            if atoms is None:
+                raise ValueError('Atoms object has to be defined.')
+            masses = atoms.get_masses()
+            if bonds is None:
+                bonds = []
+            if angles is None:
+                angles = []
+            if dihedrals is None:
+                dihedrals = []
+            self.n = len(bonds) + len(angles) + len(dihedrals)
+            self.constraints = []
+            for bond in bonds:
+                masses_bond = masses[bond[1]]
+                self.constraints.append(self.FixBondLengthAlt(bond[0], bond[1],
+                                                              masses_bond))
+            for angle in angles:
+                masses_angle = masses[angle[1]]
+                self.constraints.append(self.FixAngle(angle[0], angle[1],
+                                                      masses_angle))
+            for dihedral in dihedrals:
+                masses_dihedral = masses[dihedral[1]]
+                self.constraints.append(self.FixDihedral(dihedral[0],
+                                                         dihedral[1],
+                                                         masses_dihedral))
+            self.epsilon = epsilon
+
+        #copy case for __init__
+        else:
+            self.constraints = _copy_init
+            self.n = len(self.constraints)
+            self.epsilon = epsilon
+
+    def adjust_positions(self, old, new):
+        for constraint in self.constraints:
+            constraint.set_h_vectors(old)
+        for j in range(50):
+            maxerr = 0.0
+            for constraint in self.constraints:
+                constraint.adjust_positions(old, new)
+                maxerr = max(abs(constraint.sigma), maxerr)
+            if maxerr < self.epsilon:
+                return
+        raise ValueError('Shake did not converge.')
+
+    def adjust_forces(self, positions, forces):
+        #Project out translations and rotations and all other constraints
+        N = len(forces)
+        list2_constraints = list(np.zeros((6, N, 3)))
+        tx, ty, tz, rx, ry, rz = list2_constraints
+
+        list_constraints = [r.ravel() for r in list2_constraints]
+        
+        tx[:, 0] = 1.0
+        ty[:, 1] = 1.0
+        tz[:, 2] = 1.0
+        ff = forces.ravel()
+        
+        #Calculate the center of mass
+        center = positions.sum(axis=0) / N
+
+        rx[:, 1] = -(positions[:, 2] - center[2])
+        rx[:, 2] = positions[:, 1] - center[1]
+        ry[:, 0] = positions[:, 2] - center[2]
+        ry[:, 2] = -(positions[:, 0] - center[0])
+        rz[:, 0] = -(positions[:, 1] - center[1])
+        rz[:, 1] = positions[:, 0] - center[0]
+        
+        #Normalizing transl., rotat. constraints
+        for r in list2_constraints:
+            r /= np.linalg.norm(r.ravel())
+        
+        #Add all angle, etc. constraint vectors
+        for constraint in self.constraints:
+            constraint.adjust_forces(positions, forces)
+            list_constraints.insert(0, constraint.h)
+        #QR DECOMPOSITION - GRAM SCHMIDT
+
+        list_constraints = [r.ravel() for r in list_constraints]
+        aa = np.column_stack(list_constraints)
+        (aa, bb) = np.linalg.qr(aa, mode = 'full')
+        #Projektion
+        hh = []
+        for i, constraint in enumerate(self.constraints):
+            hh.append(aa[:, i] * np.row_stack(aa[:, i]))
+
+        txx = aa[:, self.n] * np.row_stack(aa[:, self.n])
+        tyy = aa[:, self.n+1] * np.row_stack(aa[:, self.n+1])
+        tzz = aa[:, self.n+2] * np.row_stack(aa[:, self.n+2])
+        rxx = aa[:, self.n+3] * np.row_stack(aa[:, self.n+3])
+        ryy = aa[:, self.n+4] * np.row_stack(aa[:, self.n+4])
+        rzz = aa[:, self.n+5] * np.row_stack(aa[:, self.n+5])
+        T = txx + tyy + tzz + rxx + ryy + rzz
+        for vec in hh:
+            T += vec
+        ff = np.dot(T, np.row_stack(ff))
+        forces[:, :] -= np.dot(T, np.row_stack(ff)).reshape(-1, 3)
+
+    def copy(self):
+        return FixInternals(epsilon=self.epsilon, _copy_init=self.constraints)
+
+    def __repr__(self):
+        constraints = repr(self.constraints)
+        return 'FixInternals(_copy_init=%s, epsilon=%s)' % (constraints,
+                                                            repr(self.epsilon))
+    
+    def __str__(self):
+        return '\n'.join([repr(c) for c in self.constraints])
+
+    #Classes for internal use in FixInternals
+    class FixBondLengthAlt:
+        """Constraint subobject for fixing bond length within FixInternals."""
+        def __init__(self, bond, indices, masses, maxstep=0.01):
+            """Fix distance between atoms with indices a1, a2."""
+            self.indices = indices
+            self.bond = bond
+            self.h1, self.h2 = None
+            self.masses = masses
+            self.h = []
+            self.sigma = 1.
+
+        def set_h_vectors(self, pos):
+            dist1 = pos[self.indices[0]] - pos[self.indices[1]]
+            self.h1 = 2 * dist1
+            self.h2 = -self.h1
+
+        def adjust_positions(self, old, new):
+            h1 = self.h1 / self.masses[0]
+            h2 = self.h2 / self.masses[1]
+            dist1 = new[self.indices[0]] - new[self.indices[1]]
+            dist = np.dot(dist1, dist1)
+            self.sigma = dist - self.bond**2
+            lamda = -self.sigma / (2 * np.dot(dist1, (h1 - h2)))
+            new[self.indices[0]] += lamda * h1
+            new[self.indices[1]] += lamda * h2
+
+        def adjust_forces(self, positions, forces):
+            self.h1 = 2 * (positions[self.indices[0]] -
+                           positions[self.indices[1]])
+            self.h2 = -self.h1
+            self.h = np.zeros([len(forces)*3])
+            self.h[(self.indices[0])*3]   = self.h1[0]
+            self.h[(self.indices[0])*3+1] = self.h1[1]
+            self.h[(self.indices[0])*3+2] = self.h1[2]
+            self.h[(self.indices[1])*3]   = self.h2[0]
+            self.h[(self.indices[1])*3+1] = self.h2[1]
+            self.h[(self.indices[1])*3+2] = self.h2[2]
+            self.h /= np.linalg.norm(self.h)
+
+        def __repr__(self):
+            return 'FixBondLengthAlt(%d, %d, %d)' % \
+                tuple(self.bond, self.indices)
+
+    class FixAngle:
+        """Constraint object for fixing an angle within
+        FixInternals."""
+        def __init__(self, angle, indices, masses):
+            """Fix atom movement to construct a constant angle."""
+            self.indices = indices
+            self.a1m, self.a2m, self.a3m = masses
+            self.angle = np.cos(angle)
+            self.h1 = self.h2 = self.h3 = None
+            self.h = []
+            self.sigma = 1.
+
+        def set_h_vectors(self, pos):
+            r21 = pos[self.indices[0]] - pos[self.indices[1]]
+            r21_len = np.linalg.norm(r21)
+            e21 = r21 / r21_len
+            r23 = pos[self.indices[2]] - pos[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            angle = np.dot(e21, e23)
+            self.h1 = -2 * angle * ((angle * e21 - e23) / (r21_len))
+            self.h3 = -2 * angle * ((angle * e23 - e21) / (r23_len))
+            self.h2 = -(self.h1 + self.h3)
+
+        def adjust_positions(self, oldpositions, newpositions):
+            r21 = newpositions[self.indices[0]] - newpositions[self.indices[1]]
+            r21_len = np.linalg.norm(r21)
+            e21 = r21 / r21_len
+            r23 = newpositions[self.indices[2]] - newpositions[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            angle = np.dot(e21, e23)
+            self.sigma = (angle - self.angle) * (angle + self.angle)
+            h1 = self.h1 / self.a1m
+            h3 = self.h3 / self.a3m
+            h2 = self.h2 / self.a2m
+            h21 = h1 - h2
+            h23 = h3 - h2
+            # Calculating new positions
+            deriv = (((np.dot(r21, h23) + np.dot(r23, h21))
+                      / (r21_len * r23_len))
+                     - (np.dot(r21, h21) / (r21_len * r21_len)
+                        + np.dot(r23, h23) / (r23_len * r23_len)) * angle)
+            deriv *= 2 * angle
+            lamda = -self.sigma / deriv
+            newpositions[self.indices[0]] += lamda * h1
+            newpositions[self.indices[1]] += lamda * h2
+            newpositions[self.indices[2]] += lamda * h3
+
+        def adjust_forces(self, positions, forces):
+            r21 = positions[self.indices[0]] - positions[self.indices[1]]
+            r21_len = np.linalg.norm(r21)
+            e21 = r21 / r21_len
+            r23 = positions[self.indices[2]] - positions[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            angle = np.dot(e21, e23)
+            self.h1 = -2 * angle * (angle * e21 - e23) / r21_len
+            self.h3 = -2 * angle * (angle * e23 - e21) / r23_len
+            self.h2 = -(self.h1 + self.h3)
+            self.h = np.zeros([len(positions)*3])
+            self.h[(self.indices[0])*3]   = self.h1[0]
+            self.h[(self.indices[0])*3+1] = self.h1[1]
+            self.h[(self.indices[0])*3+2] = self.h1[2]
+            self.h[(self.indices[1])*3]   = self.h2[0]
+            self.h[(self.indices[1])*3+1] = self.h2[1]
+            self.h[(self.indices[1])*3+2] = self.h2[2]
+            self.h[(self.indices[2])*3]   = self.h3[0]
+            self.h[(self.indices[2])*3+1] = self.h3[1]
+            self.h[(self.indices[2])*3+2] = self.h3[2]
+            self.h /= np.linalg.norm(self.h)
+
+        def __repr__(self):
+            return 'FixAngle(%s, %f)' % (tuple(self.indices),
+                                         np.arccos(self.angle))
+                
+
+    class FixDihedral:
+        """Constraint object for fixing an dihedral using
+        the shake algorithm. This one allows also other constraints."""
+        def __init__(self, angle, indices, masses):
+            """Fix atom movement to construct a constant dihedral angle."""
+            self.indices = indices
+            self.a1m, self.a2m, self.a3m, self.a4m = masses
+            self.angle = np.cos(angle)
+            self.h1 = self.h2 = self.h3 = self.h4 = None
+            self.h = []
+            self.sigma = 1.
+
+        def set_h_vectors(self, pos):
+            r12 = pos[self.indices[1]] - pos[self.indices[0]]
+            r12_len = np.linalg.norm(r12)
+            e12 = r12 / r12_len
+            r23 = pos[self.indices[2]] - pos[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            r34 = pos[self.indices[3]] - pos[self.indices[2]]
+            r34_len = np.linalg.norm(r34)
+            e34 = r34 / r34_len
+            a = -r12 - np.dot(-r12, e23) * e23
+            a_len = np.linalg.norm(a)
+            ea = a / a_len
+            b = r34 - np.dot(r34, e23) * e23
+            b_len = np.linalg.norm(b)
+            eb = b / b_len
+            angle = np.dot(ea, eb).clip(-1.0, 1.0)
+            self.h1 = (eb - angle * ea) / a_len
+            self.h4 = (ea - angle * eb) / b_len
+            self.h2 = self.h1 * (np.dot(-r12, e23) / r23_len -1)
+            self.h2 += np.dot(r34, e23) / r23_len * self.h4
+            self.h3 = -self.h4 * (np.dot(r34, e23) / r23_len + 1)
+            self.h3 += np.dot(r12, e23) / r23_len * self.h1
+
+        def adjust_positions(self, oldpositions, newpositions):
+            r12 = newpositions[self.indices[1]] - newpositions[self.indices[0]]
+            r12_len = np.linalg.norm(r12)
+            e12 = r12 / r12_len
+            r23 = newpositions[self.indices[2]] - newpositions[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            r34 = newpositions[self.indices[3]] - newpositions[self.indices[2]]
+            r34_len = np.linalg.norm(r34)
+            e34 = r34 / r34_len
+            n1 = np.cross(r12, r23)
+            n1_len = np.linalg.norm(n1)
+            n1e = n1 / n1_len
+            n2 = np.cross(r23, r34)
+            n2_len = np.linalg.norm(n2)
+            n2e = n2 / n2_len
+            angle = np.dot(n1e, n2e).clip(-1.0, 1.0)
+            self.sigma = (angle - self.angle) * (angle + self.angle)
+            h1 = self.h1 / self.a1m
+            h2 = self.h2 / self.a2m
+            h3 = self.h3 / self.a3m
+            h4 = self.h4 / self.a4m
+            h12 = h2 - h1
+            h23 = h3 - h2
+            h34 = h4 - h3
+            deriv = ((np.dot(n1, np.cross(r34, h23) + np.cross(h34, r23))
+                      + np.dot(n2, np.cross(r23, h12) + np.cross(h23, r12)))
+                     / (n1_len * n2_len))
+            deriv -= (((np.dot(n1, np.cross(r23, h12) + np.cross(h23, r12))
+                        / n1_len**2)
+                       + (np.dot(n2, np.cross(r34, h23) + np.cross(h34, r23))
+                          / n2_len**2)) * angle)
+            deriv *= -2 * angle
+            lamda = -self.sigma / deriv
+            newpositions[self.indices[0]] += lamda * h1
+            newpositions[self.indices[1]] += lamda * h2
+            newpositions[self.indices[2]] += lamda * h3
+            newpositions[self.indices[3]] += lamda * h4
+
+        def adjust_forces(self, positions, forces):
+            r12 = positions[self.indices[1]] - positions[self.indices[0]]
+            r12_len = np.linalg.norm(r12)
+            e12 = r12 / r12_len
+            r23 = positions[self.indices[2]] - positions[self.indices[1]]
+            r23_len = np.linalg.norm(r23)
+            e23 = r23 / r23_len
+            r34 = positions[self.indices[3]] - positions[self.indices[2]]
+            r34_len = np.linalg.norm(r34)
+            e34 = r34 / r34_len
+            a = -r12 - np.dot(-r12, e23) * e23
+            a_len = np.linalg.norm(a)
+            ea = a / a_len
+            b = r34 - np.dot(r34, e23) * e23
+            b_len = np.linalg.norm(b)
+            eb = b / b_len
+            angle = np.dot(ea, eb).clip(-1.0, 1.0)
+            self.h1 = (eb - angle * ea) / a_len
+            self.h4 = (ea - angle * eb) / b_len
+            self.h2 = self.h1 * (np.dot(-r12, e23) / r23_len - 1)
+            self.h2 += np.dot(r34, e23) / r23_len * self.h4
+            self.h3 = -self.h4 * (np.dot(r34, e23) / r23_len + 1)
+            self.h3 -= np.dot(-r12, e23) / r23_len * self.h1
+
+            self.h = np.zeros([len(positions)*3])
+            self.h[(self.indices[0])*3]   = self.h1[0]
+            self.h[(self.indices[0])*3+1] = self.h1[1]
+            self.h[(self.indices[0])*3+2] = self.h1[2]
+            self.h[(self.indices[1])*3]   = self.h2[0]
+            self.h[(self.indices[1])*3+1] = self.h2[1]
+            self.h[(self.indices[1])*3+2] = self.h2[2]
+            self.h[(self.indices[2])*3]   = self.h3[0]
+            self.h[(self.indices[2])*3+1] = self.h3[1]
+            self.h[(self.indices[2])*3+2] = self.h3[2]
+            self.h[(self.indices[3])*3]   = self.h4[0]
+            self.h[(self.indices[3])*3+1] = self.h4[1]
+            self.h[(self.indices[3])*3+2] = self.h4[2]
+            self.h /= np.linalg.norm(self.h)
+        
+        def __repr__(self):
+            return 'FixDihedral(%s, %f)' % (tuple(self.indices), self.angle)
+
+class BondSpring(FixConstraint):
+    """Forces two atoms to stay close together by applying no force if they
+    are below threshhold_length, and applying a Hookian force when the
+    distance between them exceeds the thresshhold_length.
+    
+    a1, a2 : indices of atoms 1 and 2
+    a2 can alternately be a position in space to tether a1 to
+    threshhold_length (float) : the length below which there is no force
+    springconstant (integer) : Hook's law constant to apply when distance
+        between the two atoms exceeds threshhold_length, dimensions of 
+        (force / length)
+    """
+    def __init__(self, a1, a2, threshhold_length, springconstant):
+        if type(a2) == int:
+            self._type = 2 # two atoms tethered together
+            self.indices = [a1, a2]
+        else:
+            self._type = 1 # one atom tethered to a point in space
+            self.index = a1
+            self.origin = np.array(a2)
+        self.threshhold = threshhold_length
+        self.spring = springconstant
+
+    def adjust_positions(self, oldpositions, newpositions):
+        pass
+
+    def adjust_forces(self, positions, forces):
+        if self._type == 2:
+            p1, p2 = positions[self.indices]
+        else:
+            p1 = positions[self.index]
+            p2 = self.origin
+        displace = p2 - p1
+        bondlength = np.linalg.norm(displace)
+        if bondlength > self.threshhold:
+            magnitude = self.spring * (bondlength - self.threshhold)
+            direction = displace / np.linalg.norm(displace)
+            if self._type == 2:
+                forces[self.indices[0]] += direction * magnitude / 2.
+                forces[self.indices[1]] -= direction * magnitude / 2.
+            else:
+                forces[self.index] += direction * magnitude
+
+    def __repr__(self):
+        if self._type == 2:
+            return 'BondSpring(%d, %d)' % tuple(self.indices)
+        else:
+            return 'BondSpring(%d) to cartesian' % self.index
+
+    def copy(self):
+        if self._type == 2:
+            return BondSpring(a1=self.indices[0], a2=self.indices[1],
+                              threshhold_length=self.threshhold,
+                              springconstant=self.spring)
+        else:
+            return BondSpring(a1=self.index, a2=self.origin,
+                              threshhold_length=self.threshhold,
+                              springconstant=self.spring)
+
+
+class Filter:
+    """Subset filter class."""
+    def __init__(self, atoms, indices=None, mask=None):
+        """Filter atoms.
+
+        This filter can be used to hide degrees of freedom in an Atoms
+        object.
+
+        Parameters
+        ----------
+        indices : list of int
+           Indices for those atoms that should remain visible.
+        mask : list of bool
+           One boolean per atom indicating if the atom should remain
+           visible or not.
+        """
+
+        self.atoms = atoms
+        self.constraints = []
+
+        if indices is None and mask is None:
+            raise ValueError('Use "indices" or "mask".')
+        if indices is not None and mask is not None:
+            raise ValueError('Use only one of "indices" and "mask".')
+
+        if mask is not None:
+            self.index = np.asarray(mask, bool)
+            self.n = self.index.sum()
+        else:
+            self.index = np.asarray(indices, int)
+            self.n = len(self.index)
+
+    def get_cell(self):
+        """Returns the computational cell.
+
+        The computational cell is the same as for the original system.
+        """
+        return self.atoms.get_cell()
+
+    def get_pbc(self):
+        """Returns the periodic boundary conditions.
+
+        The boundary conditions are the same as for the original system.
+        """
+        return self.atoms.get_pbc()
+
+    def get_positions(self):
+        "Return the positions of the visible atoms."
+        return self.atoms.get_positions()[self.index]
+
+    def set_positions(self, positions):
+        "Set the positions of the visible atoms."
+        pos = self.atoms.get_positions()
+        pos[self.index] = positions
+        self.atoms.set_positions(pos)
+
+    positions = property(get_positions, set_positions, 
+                         doc='Positions of the atoms')
+
+    def get_momenta(self):
+        "Return the momenta of the visible atoms."
+        return self.atoms.get_momenta()[self.index]
+
+    def set_momenta(self, momenta):
+        "Set the momenta of the visible atoms."
+        mom = self.atoms.get_momenta()
+        mom[self.index] = momenta
+        self.atoms.set_momenta(mom)
+
+    def get_atomic_numbers(self):
+        "Return the atomic numbers of the visible atoms."
+        return self.atoms.get_atomic_numbers()[self.index]
+
+    def set_atomic_numbers(self, atomic_numbers):
+        "Set the atomic numbers of the visible atoms."
+        z = self.atoms.get_atomic_numbers()
+        z[self.index] = atomic_numbers
+        self.atoms.set_atomic_numbers(z)
+
+    def get_tags(self):
+        "Return the tags of the visible atoms."
+        return self.atoms.get_tags()[self.index]
+
+    def set_tags(self, tags):
+        "Set the tags of the visible atoms."
+        tg = self.atoms.get_tags()
+        tg[self.index] = tags
+        self.atoms.set_tags(tg)
+
+    def get_forces(self, *args, **kwargs):
+        return self.atoms.get_forces(*args, **kwargs)[self.index]
+
+    def get_stress(self):
+        return self.atoms.get_stress()
+
+    def get_stresses(self):
+        return self.atoms.get_stresses()[self.index]
+
+    def get_masses(self):
+        return self.atoms.get_masses()[self.index]
+
+    def get_potential_energy(self):
+        """Calculate potential energy.
+
+        Returns the potential energy of the full system.
+        """
+        return self.atoms.get_potential_energy()
+
+    def get_chemical_symbols(self):
+        return self.atoms.get_chemical_symbols()
+
+    def get_initial_magnetic_moments(self):
+        return self.atoms.get_initial_magnetic_moments()
+
+    def get_calculator(self):
+        """Returns the calculator.
+
+        WARNING: The calculator is unaware of this filter, and sees a
+        different number of atoms.
+        """
+        return self.atoms.get_calculator()
+
+    def has(self, name):
+        """Check for existance of array."""
+        return self.atoms.has(name)
+
+    def __len__(self):
+        "Return the number of movable atoms."
+        return self.n
+
+    def __getitem__(self, i):
+        "Return an atom."
+        return self.atoms[self.index[i]]
+
+
+class StrainFilter(Filter):
+    """Modify the supercell while keeping the scaled positions fixed.
+
+    Presents the strain of the supercell as the generalized positions,
+    and the global stress tensor (times the volume) as the generalized
+    force.
+
+    This filter can be used to relax the unit cell until the stress is
+    zero.  If MDMin is used for this, the timestep (dt) to be used
+    depends on the system size. 0.01/x where x is a typical dimension
+    seems like a good choice.
+
+    The stress and strain are presented as 6-vectors, the order of the
+    components follow the standard engingeering practice: xx, yy, zz,
+    yz, xz, xy.
+
+    """
+    def __init__(self, atoms, mask=None):
+        """Create a filter applying a homogeneous strain to a list of atoms.
+
+        The first argument, atoms, is the atoms object.
+
+        The optional second argument, mask, is a list of six booleans,
+        indicating which of the six independent components of the
+        strain that are allowed to become non-zero.  It defaults to
+        [1,1,1,1,1,1].
+
+        """
+
+        self.atoms = atoms
+        self.strain = np.zeros(6)
+
+        if mask is None:
+            self.mask = np.ones(6)
+        else:
+            self.mask = np.array(mask)
+
+        self.index = np.arange(len(atoms))
+        self.n = self.index.sum()
+
+        self.origcell = atoms.get_cell()
+
+    def get_positions(self):
+        return self.strain.reshape((2, 3))
+
+    def set_positions(self, new):
+        new = new.ravel() * self.mask
+        eps = np.array([[1.0 + new[0], 0.5 * new[5], 0.5 * new[4]],
+                        [0.5 * new[5], 1.0 + new[1], 0.5 * new[3]],
+                        [0.5 * new[4], 0.5 * new[3], 1.0 + new[2]]])
+
+        self.atoms.set_cell(np.dot(self.origcell, eps), scale_atoms=True)
+        self.strain[:] = new
+
+    def get_forces(self):
+        stress = self.atoms.get_stress()
+        return -self.atoms.get_volume() * (stress * self.mask).reshape((2, 3))
+
+    def get_potential_energy(self):
+        return self.atoms.get_potential_energy()
+
+    def has(self, x):
+        return self.atoms.has(x)
+
+    def __len__(self):
+        return 2
+
+class UnitCellFilter(Filter):
+    """Modify the supercell and the atom positions. """
+    def __init__(self, atoms, mask=None):
+        """Create a filter that returns the atomic forces and unit
+        cell stresses together, so they can simultaneously be
+        minimized.
+
+        The first argument, atoms, is the atoms object.
+
+        The optional second argument, mask, is a list of booleans,
+        indicating which of the six independent
+        components of the strain are relaxed.
+        1, True = relax to zero
+        0, False = fixed, ignore this component
+
+        use atom Constraints, e.g. FixAtoms, to control relaxation of
+        the atoms.
+
+        #this should be equivalent to the StrainFilter
+        >>> atoms = Atoms(...)
+        >>> atoms.set_constraint(FixAtoms(mask=[True for atom in atoms]))
+        >>> ucf = UCFilter(atoms)
+
+        You should not attach this UCFilter object to a
+        trajectory. Instead, create a trajectory for the atoms, and
+        attach it to an optimizer like this:
+
+        >>> atoms = Atoms(...)
+        >>> ucf = UCFilter(atoms)
+        >>> qn = QuasiNewton(ucf)
+        >>> traj = PickleTrajectory('TiO2.traj','w',atoms)
+        >>> qn.attach(traj)
+        >>> qn.run(fmax=0.05)
+
+        Helpful conversion table
+        ========================
+        0.05 eV/A^3   = 8 GPA
+        0.003 eV/A^3  = 0.48 GPa
+        0.0006 eV/A^3 = 0.096 GPa
+        0.0003 eV/A^3 = 0.048 GPa
+        0.0001 eV/A^3 = 0.02 GPa
+        """
+
+        Filter.__init__(self, atoms, indices=range(len(atoms)))
+
+        self.atoms = atoms
+        self.strain = np.zeros(6)
+
+        if mask is None:
+            self.mask = np.ones(6)
+        else:
+            self.mask = np.array(mask)
+
+        self.origcell = atoms.get_cell()
+
+    def get_positions(self):
+        '''
+        this returns an array with shape (natoms + 2,3).
+
+        the first natoms rows are the positions of the atoms, the last
+        two rows are the strains associated with the unit cell
+        '''
+
+        atom_positions = self.atoms.get_positions()
+        strains = self.strain.reshape((2, 3))
+
+        natoms = len(self.atoms)
+        all_pos = np.zeros((natoms + 2, 3), np.float)
+        all_pos[0:natoms, :] = atom_positions
+        all_pos[natoms:, :] = strains
+
+        return all_pos
+
+    def set_positions(self, new):
+        '''
+        new is an array with shape (natoms+2,3).
+
+        the first natoms rows are the positions of the atoms, the last
+        two rows are the strains used to change the cell shape.
+
+        The atom positions are set first, then the unit cell is
+        changed keeping the atoms in their scaled positions.
+        '''
+
+        natoms = len(self.atoms)
+
+        atom_positions = new[0:natoms, :]
+        self.atoms.set_positions(atom_positions)
+
+        new = new[natoms:, :] #this is only the strains
+        new = new.ravel() * self.mask
+        eps = np.array([[1.0 + new[0], 0.5 * new[5], 0.5 * new[4]],
+                        [0.5 * new[5], 1.0 + new[1], 0.5 * new[3]],
+                        [0.5 * new[4], 0.5 * new[3], 1.0 + new[2]]])
+
+        self.atoms.set_cell(np.dot(self.origcell, eps), scale_atoms=True)
+        self.strain[:] = new
+
+    def get_forces(self, apply_constraint=False):
+        '''
+        returns an array with shape (natoms+2,3) of the atomic forces
+        and unit cell stresses.
+
+        the first natoms rows are the forces on the atoms, the last
+        two rows are the stresses on the unit cell, which have been
+        reshaped to look like "atomic forces". i.e.,
+
+        f[-2] = -vol*[sxx,syy,szz]*mask[0:3]
+        f[-1] = -vol*[syz, sxz, sxy]*mask[3:]
+
+        apply_constraint is an argument expected by ase
+        '''
+
+        stress = self.atoms.get_stress()
+        atom_forces = self.atoms.get_forces()
+
+        natoms = len(self.atoms)
+        all_forces = np.zeros((natoms+2, 3), np.float)
+        all_forces[0:natoms, :] = atom_forces
+
+        vol = self.atoms.get_volume()
+        stress_forces = -vol * (stress * self.mask).reshape((2, 3))
+        all_forces[natoms:, :] = stress_forces
+        return all_forces
+
+    def get_potential_energy(self):
+        return self.atoms.get_potential_energy()
+
+    def has(self, x):
+        return self.atoms.has(x)
+
+    def __len__(self):
+        return (2 + len(self.atoms))
diff --git a/ase/data/__init__.py b/ase/data/__init__.py
new file mode 100644
index 0000000..a1c4d6e
--- /dev/null
+++ b/ase/data/__init__.py
@@ -0,0 +1,488 @@
+# -*- coding: utf-8 -*-
+import numpy as np
+import os.path
+from ase.data.vdw import vdw_radii
+
+chemical_symbols = ['X',  'H',  'He', 'Li', 'Be',
+                    'B',  'C',  'N',  'O',  'F',
+                    'Ne', 'Na', 'Mg', 'Al', 'Si',
+                    'P',  'S',  'Cl', 'Ar', 'K',
+                    'Ca', 'Sc', 'Ti', 'V',  'Cr',
+                    'Mn', 'Fe', 'Co', 'Ni', 'Cu',
+                    'Zn', 'Ga', 'Ge', 'As', 'Se',
+                    'Br', 'Kr', 'Rb', 'Sr', 'Y',
+                    'Zr', 'Nb', 'Mo', 'Tc', 'Ru',
+                    'Rh', 'Pd', 'Ag', 'Cd', 'In',
+                    'Sn', 'Sb', 'Te', 'I',  'Xe',
+                    'Cs', 'Ba', 'La', 'Ce', 'Pr',
+                    'Nd', 'Pm', 'Sm', 'Eu', 'Gd',
+                    'Tb', 'Dy', 'Ho', 'Er', 'Tm',
+                    'Yb', 'Lu', 'Hf', 'Ta', 'W',
+                    'Re', 'Os', 'Ir', 'Pt', 'Au',
+                    'Hg', 'Tl', 'Pb', 'Bi', 'Po',
+                    'At', 'Rn', 'Fr', 'Ra', 'Ac',
+                    'Th', 'Pa', 'U',  'Np', 'Pu',
+                    'Am', 'Cm', 'Bk', 'Cf', 'Es',
+                    'Fm', 'Md', 'No', 'Lr']
+
+atomic_numbers = {}
+for Z, symbol in enumerate(chemical_symbols):
+    atomic_numbers[symbol] = Z
+
+atomic_names = [
+    '', 'Hydrogen', 'Helium', 'Lithium', 'Beryllium', 'Boron',
+    'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine', 'Neon', 'Sodium',
+    'Magnesium', 'Aluminium', 'Silicon', 'Phosphorus', 'Sulfur',
+    'Chlorine', 'Argon', 'Potassium', 'Calcium', 'Scandium',
+    'Titanium', 'Vanadium', 'Chromium', 'Manganese', 'Iron',
+    'Cobalt', 'Nickel', 'Copper', 'Zinc', 'Gallium', 'Germanium',
+    'Arsenic', 'Selenium', 'Bromine', 'Krypton', 'Rubidium',
+    'Strontium', 'Yttrium', 'Zirconium', 'Niobium', 'Molybdenum',
+    'Technetium', 'Ruthenium', 'Rhodium', 'Palladium', 'Silver',
+    'Cadmium', 'Indium', 'Tin', 'Antimony', 'Tellurium',
+    'Iodine', 'Xenon', 'Caesium', 'Barium', 'Lanthanum',
+    'Cerium', 'Praseodymium', 'Neodymium', 'Promethium',
+    'Samarium', 'Europium', 'Gadolinium', 'Terbium',
+    'Dysprosium', 'Holmium', 'Erbium', 'Thulium', 'Ytterbium',
+    'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten', 'Rhenium',
+    'Osmium', 'Iridium', 'Platinum', 'Gold', 'Mercury',
+    'Thallium', 'Lead', 'Bismuth', 'Polonium', 'Astatine',
+    'Radon', 'Francium', 'Radium', 'Actinium', 'Thorium',
+    'Protactinium', 'Uranium', 'Neptunium', 'Plutonium',
+    'Americium', 'Curium', 'Berkelium', 'Californium',
+    'Einsteinium', 'Fermium', 'Mendelevium', 'Nobelium',
+    'Lawrencium', 'Unnilquadium', 'Unnilpentium', 'Unnilhexium']
+
+atomic_masses = np.array([
+   0.00000, # X
+   1.00794, # H
+   4.00260, # He
+   6.94100, # Li
+   9.01218, # Be
+  10.81100, # B
+  12.01100, # C
+  14.00670, # N
+  15.99940, # O
+  18.99840, # F
+  20.17970, # Ne
+  22.98977, # Na
+  24.30500, # Mg
+  26.98154, # Al
+  28.08550, # Si
+  30.97376, # P
+  32.06600, # S
+  35.45270, # Cl
+  39.94800, # Ar
+  39.09830, # K
+  40.07800, # Ca
+  44.95590, # Sc
+  47.88000, # Ti
+  50.94150, # V
+  51.99600, # Cr
+  54.93800, # Mn
+  55.84700, # Fe
+  58.93320, # Co
+  58.69340, # Ni
+  63.54600, # Cu
+  65.39000, # Zn
+  69.72300, # Ga
+  72.61000, # Ge
+  74.92160, # As
+  78.96000, # Se
+  79.90400, # Br
+  83.80000, # Kr
+  85.46780, # Rb
+  87.62000, # Sr
+  88.90590, # Y
+  91.22400, # Zr
+  92.90640, # Nb
+  95.94000, # Mo
+    np.nan, # Tc
+ 101.07000, # Ru
+ 102.90550, # Rh
+ 106.42000, # Pd
+ 107.86800, # Ag
+ 112.41000, # Cd
+ 114.82000, # In
+ 118.71000, # Sn
+ 121.75700, # Sb
+ 127.60000, # Te
+ 126.90450, # I
+ 131.29000, # Xe
+ 132.90540, # Cs
+ 137.33000, # Ba
+ 138.90550, # La
+ 140.12000, # Ce
+ 140.90770, # Pr
+ 144.24000, # Nd
+    np.nan, # Pm
+ 150.36000, # Sm
+ 151.96500, # Eu
+ 157.25000, # Gd
+ 158.92530, # Tb
+ 162.50000, # Dy
+ 164.93030, # Ho
+ 167.26000, # Er
+ 168.93420, # Tm
+ 173.04000, # Yb
+ 174.96700, # Lu
+ 178.49000, # Hf
+ 180.94790, # Ta
+ 183.85000, # W
+ 186.20700, # Re
+ 190.20000, # Os
+ 192.22000, # Ir
+ 195.08000, # Pt
+ 196.96650, # Au
+ 200.59000, # Hg
+ 204.38300, # Tl
+ 207.20000, # Pb
+ 208.98040, # Bi
+    np.nan, # Po
+    np.nan, # At
+    np.nan, # Rn
+    np.nan, # Fr
+ 226.02540, # Ra
+    np.nan, # Ac
+ 232.03810, # Th
+ 231.03590, # Pa
+ 238.02900, # U
+ 237.04820, # Np
+    np.nan, # Pu
+    np.nan, # Am
+    np.nan, # Cm
+    np.nan, # Bk
+    np.nan, # Cf
+    np.nan, # Es
+    np.nan, # Fm
+    np.nan, # Md
+    np.nan, # No
+    np.nan])# Lw
+
+# Covalent radii from:
+#
+#  Covalent radii revisited,
+#  Beatriz Cordero, Verónica Gómez, Ana E. Platero-Prats, Marc Revés,
+#  Jorge Echeverría, Eduard Cremades, Flavia Barragán and Santiago Alvarez,
+#  Dalton Trans., 2008, 2832-2838 DOI:10.1039/B801115J 
+missing = 0.2
+covalent_radii = np.array([
+    missing,  # X
+    0.31,  # H
+    0.28,  # He
+    1.28,  # Li
+    0.96,  # Be
+    0.84,  # B
+    0.76,  # C
+    0.71,  # N
+    0.66,  # O
+    0.57,  # F
+    0.58,  # Ne
+    1.66,  # Na
+    1.41,  # Mg
+    1.21,  # Al
+    1.11,  # Si
+    1.07,  # P
+    1.05,  # S
+    1.02,  # Cl
+    1.06,  # Ar
+    2.03,  # K
+    1.76,  # Ca
+    1.70,  # Sc
+    1.60,  # Ti
+    1.53,  # V
+    1.39,  # Cr
+    1.39,  # Mn
+    1.32,  # Fe
+    1.26,  # Co
+    1.24,  # Ni
+    1.32,  # Cu
+    1.22,  # Zn
+    1.22,  # Ga
+    1.20,  # Ge
+    1.19,  # As
+    1.20,  # Se
+    1.20,  # Br
+    1.16,  # Kr
+    2.20,  # Rb
+    1.95,  # Sr
+    1.90,  # Y
+    1.75,  # Zr
+    1.64,  # Nb
+    1.54,  # Mo
+    1.47,  # Tc
+    1.46,  # Ru
+    1.42,  # Rh
+    1.39,  # Pd
+    1.45,  # Ag
+    1.44,  # Cd
+    1.42,  # In
+    1.39,  # Sn
+    1.39,  # Sb
+    1.38,  # Te
+    1.39,  # I
+    1.40,  # Xe
+    2.44,  # Cs
+    2.15,  # Ba
+    2.07,  # La
+    2.04,  # Ce
+    2.03,  # Pr
+    2.01,  # Nd
+    1.99,  # Pm
+    1.98,  # Sm
+    1.98,  # Eu
+    1.96,  # Gd
+    1.94,  # Tb
+    1.92,  # Dy
+    1.92,  # Ho
+    1.89,  # Er
+    1.90,  # Tm
+    1.87,  # Yb
+    1.87,  # Lu
+    1.75,  # Hf
+    1.70,  # Ta
+    1.62,  # W
+    1.51,  # Re
+    1.44,  # Os
+    1.41,  # Ir
+    1.36,  # Pt
+    1.36,  # Au
+    1.32,  # Hg
+    1.45,  # Tl
+    1.46,  # Pb
+    1.48,  # Bi
+    1.40,  # Po
+    1.50,  # At
+    1.50,  # Rn
+    2.60,  # Fr
+    2.21,  # Ra
+    2.15,  # Ac
+    2.06,  # Th
+    2.00,  # Pa
+    1.96,  # U
+    1.90,  # Np
+    1.87,  # Pu
+    1.80,  # Am
+    1.69,  # Cm
+    missing,  # Bk
+    missing,  # Cf
+    missing,  # Es
+    missing,  # Fm
+    missing,  # Md
+    missing,  # No
+    missing,  # Lr
+    ])
+
+# This data is from Ashcroft and Mermin.
+reference_states = [\
+    None, #X
+    {'symmetry': 'diatom', 'd': 0.74}, #H
+    {'symmetry': 'atom'}, #He
+    {'symmetry': 'bcc', 'a': 3.49}, #Li
+    {'symmetry': 'hcp', 'c/a': 1.567, 'a': 2.29}, #Be
+    {'symmetry': 'tetragonal', 'c/a': 0.576, 'a': 8.73}, #B
+    {'symmetry': 'diamond', 'a': 3.57},#C
+    {'symmetry': 'diatom', 'd': 1.10},#N
+    {'symmetry': 'diatom', 'd': 1.21},#O
+    {'symmetry': 'diatom', 'd': 1.42},#F
+    {'symmetry': 'fcc', 'a': 4.43},#Ne
+    {'symmetry': 'bcc', 'a': 4.23},#Na
+    {'symmetry': 'hcp', 'c/a': 1.624, 'a': 3.21},#Mg
+    {'symmetry': 'fcc', 'a': 4.05},#Al
+    {'symmetry': 'diamond', 'a': 5.43},#Si
+    {'symmetry': 'cubic', 'a': 7.17},#P
+    {'symmetry': 'orthorhombic', 'c/a': 2.339, 'a': 10.47,'b/a': 1.229},#S
+    {'symmetry': 'orthorhombic', 'c/a': 1.324, 'a': 6.24, 'b/a': 0.718},#Cl
+    {'symmetry': 'fcc', 'a': 5.26},#Ar
+    {'symmetry': 'bcc', 'a': 5.23},#K
+    {'symmetry': 'fcc', 'a': 5.58},#Ca
+    {'symmetry': 'hcp', 'c/a': 1.594, 'a': 3.31},#Sc
+    {'symmetry': 'hcp', 'c/a': 1.588, 'a': 2.95},#Ti
+    {'symmetry': 'bcc', 'a': 3.02},#V
+    {'symmetry': 'bcc', 'a': 2.88},#Cr
+    {'symmetry': 'cubic', 'a': 8.89},#Mn
+    {'symmetry': 'bcc', 'a': 2.87},#Fe
+    {'symmetry': 'hcp', 'c/a': 1.622, 'a': 2.51},#Co
+    {'symmetry': 'fcc', 'a': 3.52},#Ni
+    {'symmetry': 'fcc', 'a': 3.61},#Cu
+    {'symmetry': 'hcp', 'c/a': 1.856, 'a': 2.66},#Zn
+    {'symmetry': 'orthorhombic', 'c/a': 1.695, 'a': 4.51, 'b/a': 1.001},#Ga
+    {'symmetry': 'diamond', 'a': 5.66},#Ge
+    {'symmetry': 'rhombohedral', 'a': 4.13, 'alpha': 54.10},#As
+    {'symmetry': 'hcp', 'c/a': 1.136, 'a': 4.36},#Se
+    {'symmetry': 'orthorhombic', 'c/a': 1.307, 'a': 6.67, 'b/a': 0.672},#Br
+    {'symmetry': 'fcc', 'a': 5.72},#Kr
+    {'symmetry': 'bcc', 'a': 5.59},#Rb
+    {'symmetry': 'fcc', 'a': 6.08},#Sr
+    {'symmetry': 'hcp', 'c/a': 1.571, 'a': 3.65},#Y
+    {'symmetry': 'hcp', 'c/a': 1.593, 'a': 3.23},#Zr
+    {'symmetry': 'bcc', 'a': 3.30},#Nb
+    {'symmetry': 'bcc', 'a': 3.15},#Mo
+    {'symmetry': 'hcp', 'c/a': 1.604, 'a': 2.74},#Tc
+    {'symmetry': 'hcp', 'c/a': 1.584, 'a': 2.70},#Ru
+    {'symmetry': 'fcc', 'a': 3.80},#Rh
+    {'symmetry': 'fcc', 'a': 3.89},#Pd
+    {'symmetry': 'fcc', 'a': 4.09},#Ag
+    {'symmetry': 'hcp', 'c/a': 1.886, 'a': 2.98},#Cd
+    {'symmetry': 'tetragonal', 'c/a': 1.076, 'a': 4.59},#In
+    {'symmetry': 'tetragonal', 'c/a': 0.546, 'a': 5.82},#Sn
+    {'symmetry': 'rhombohedral', 'a': 4.51, 'alpha': 57.60},#Sb
+    {'symmetry': 'hcp', 'c/a': 1.330, 'a': 4.45},#Te
+    {'symmetry': 'orthorhombic', 'c/a': 1.347, 'a': 7.27, 'b/a': 0.659},#I
+    {'symmetry': 'fcc', 'a': 6.20},#Xe
+    {'symmetry': 'bcc', 'a': 6.05},#Cs
+    {'symmetry': 'bcc', 'a': 5.02},#Ba
+    {'symmetry': 'hcp', 'c/a': 1.619, 'a': 3.75},#La
+    {'symmetry': 'fcc', 'a': 5.16},#Ce
+    {'symmetry': 'hcp', 'c/a': 1.614, 'a': 3.67},#Pr
+    {'symmetry': 'hcp', 'c/a': 1.614, 'a': 3.66},#Nd
+    None,#Pm
+    {'symmetry': 'rhombohedral', 'a': 9.00, 'alpha': 23.13},#Sm
+    {'symmetry': 'bcc', 'a': 4.61},#Eu
+    {'symmetry': 'hcp', 'c/a': 1.588, 'a': 3.64},#Gd
+    {'symmetry': 'hcp', 'c/a': 1.581, 'a': 3.60},#Th
+    {'symmetry': 'hcp', 'c/a': 1.573, 'a': 3.59},#Dy
+    {'symmetry': 'hcp', 'c/a': 1.570, 'a': 3.58},#Ho
+    {'symmetry': 'hcp', 'c/a': 1.570, 'a': 3.56},#Er
+    {'symmetry': 'hcp', 'c/a': 1.570, 'a': 3.54},#Tm
+    {'symmetry': 'fcc', 'a': 5.49},#Yb
+    {'symmetry': 'hcp', 'c/a': 1.585, 'a': 3.51},#Lu
+    {'symmetry': 'hcp', 'c/a': 1.582, 'a': 3.20},#Hf
+    {'symmetry': 'bcc', 'a': 3.31},#Ta
+    {'symmetry': 'bcc', 'a': 3.16},#W
+    {'symmetry': 'hcp', 'c/a': 1.615, 'a': 2.76},#Re
+    {'symmetry': 'hcp', 'c/a': 1.579, 'a': 2.74},#Os
+    {'symmetry': 'fcc', 'a': 3.84},#Ir
+    {'symmetry': 'fcc', 'a': 3.92},#Pt
+    {'symmetry': 'fcc', 'a': 4.08},#Au
+    {'symmetry': 'rhombohedral', 'a': 2.99, 'alpha': 70.45},#Hg
+    {'symmetry': 'hcp', 'c/a': 1.599, 'a': 3.46},#Tl
+    {'symmetry': 'fcc', 'a': 4.95},#Pb
+    {'symmetry': 'rhombohedral', 'a': 4.75, 'alpha': 57.14},#Bi
+    {'symmetry': 'sc', 'a': 3.35},#Po
+    None,#At
+    None,#Rn
+    None,#Fr
+    None,#Ra
+    {'symmetry': 'fcc', 'a': 5.31},#Ac
+    {'symmetry': 'fcc', 'a': 5.08},#Th
+    {'symmetry': 'tetragonal', 'c/a': 0.825, 'a': 3.92},#Pa
+    {'symmetry': 'orthorhombic', 'c/a': 2.056, 'a': 2.85, 'b/a': 1.736},#U
+    {'symmetry': 'orthorhombic', 'c/a': 1.411, 'a': 4.72, 'b/a': 1.035},#Np
+    {'symmetry': 'monoclinic'},#Pu
+    None,#Am
+    None,#Cm
+    None,#Bk
+    None,#Cf
+    None,#Es
+    None,#Fm
+    None,#Md
+    None,#No
+    None]#Lw
+
+# http://www.webelements.com
+ground_state_magnetic_moments = np.array([
+   0.0, # X
+   1.0, # H
+   0.0, # He
+   1.0, # Li
+   0.0, # Be
+   1.0, # B
+   2.0, # C
+   3.0, # N
+   2.0, # O
+   1.0, # F
+   0.0, # Ne
+   1.0, # Na
+   0.0, # Mg
+   1.0, # Al
+   2.0, # Si
+   3.0, # P
+   2.0, # S
+   1.0, # Cl
+   0.0, # Ar
+   1.0, # K
+   0.0, # Ca
+   1.0, # Sc
+   2.0, # Ti
+   3.0, # V
+   6.0, # Cr
+   5.0, # Mn
+   4.0, # Fe
+   3.0, # Co
+   2.0, # Ni
+   1.0, # Cu
+   0.0, # Zn
+   1.0, # Ga
+   2.0, # Ge
+   3.0, # As
+   2.0, # Se
+   1.0, # Br
+   0.0, # Kr
+   1.0, # Rb
+   0.0, # Sr
+   1.0, # Y
+   2.0, # Zr
+   5.0, # Nb
+   6.0, # Mo
+   5.0, # Tc
+   4.0, # Ru
+   3.0, # Rh
+   0.0, # Pd
+   1.0, # Ag
+   0.0, # Cd
+   1.0, # In
+   2.0, # Sn
+   3.0, # Sb
+   2.0, # Te
+   1.0, # I
+   0.0, # Xe
+   1.0, # Cs
+   0.0, # Ba
+   1.0, # La
+   1.0, # Ce
+   3.0, # Pr
+   4.0, # Nd
+   5.0, # Pm
+   6.0, # Sm
+   7.0, # Eu
+   8.0, # Gd
+   5.0, # Tb
+   4.0, # Dy
+   3.0, # Ho
+   2.0, # Er
+   1.0, # Tm
+   0.0, # Yb
+   1.0, # Lu
+   2.0, # Hf
+   3.0, # Ta
+   4.0, # W
+   5.0, # Re
+   4.0, # Os
+   3.0, # Ir
+   2.0, # Pt
+   1.0, # Au
+   0.0, # Hg
+   1.0, # Tl
+   2.0, # Pb
+   3.0, # Bi
+   2.0, # Po
+   1.0, # At
+   0.0, # Rn
+   1.0, # Fr
+   0.0, # Ra
+   1.0, # Ac
+   2.0, # Th
+   3.0, # Pa
+   4.0, # U
+   5.0, # Np
+   6.0, # Pu
+   7.0, # Am
+   8.0, # Cm
+   5.0, # Bk
+   4.0, # Cf
+   4.0, # Es
+   2.0, # Fm
+   1.0, # Md
+   0.0, # No
+np.nan])# Lw
diff --git a/ase/data/alternatives.py b/ase/data/alternatives.py
new file mode 100644
index 0000000..d0b0001
--- /dev/null
+++ b/ase/data/alternatives.py
@@ -0,0 +1,108 @@
+# alternative structures
+# data from CRC Handbook 2004 85th edition
+alternative_structures = [
+    None,# X
+    None,# H
+    None,# He
+    None,# Li
+    None,# Be
+    None,# B
+    None,# C
+    None,# N
+    None,# O
+    None,# F
+    None,# Ne
+    None,# Na
+    None,# Mg
+    None,# Al
+    None,# Si
+    None,# P
+    None,# S
+    None,# Cl
+    None,# Ar
+    None,# K
+    None,# Ca
+    None,# Sc
+    None,# Ti
+    None,# V
+    None,# Cr
+    None,# Mn
+    {'symmetry': 'fcc', 'a': 2.9315, 'comment' : 'T>910 C'},# Fe
+    None,# Co
+    None,# Ni
+    None,# Cu
+    None,# Zn
+    None,# Ga
+    None,# Ge
+    None,# As
+    None,# Se
+    None,# Br
+    None,# Kr
+    None,# Rb
+    None,# Sr
+    None,# Y
+    None,# Zr
+    None,# Nb
+    None,# Mo
+    None,# Tc
+    None,# Ru
+    None,# Rh
+    None,# Pd
+    None,# Ag
+    None,# Cd
+    None,# In
+    None,# Sn
+    None,# Sb
+    None,# Te
+    None,# I
+    None,# Xe
+    None,# Cs
+    None,# Ba
+    None,# La
+    None,# Ce
+    None,# Pr
+    None,# Nd
+    None,# Pm
+    None,# Sm
+    None,# Eu
+    None,# Gd
+    None,# Tb
+    None,# Dy
+    None,# Ho
+    None,# Er
+    None,# Tm
+    None,# Yb
+    None,# Lu
+    None,# Hf
+    None,# Ta
+    None,# W
+    None,# Re
+    None,# Os
+    None,# Ir
+    None,# Pt
+    None,# Au
+    None,# Hg
+    None,# Tl
+    None,# Pb
+    None,# Bi
+    None,# Po
+    None,# At
+    None,# Rn
+    None,# Fr
+    None,# Ra
+    None,# Ac
+    None,# Th
+    None,# Pa
+    None,# U
+    None,# Np
+    None,# Pu
+    None,# Am
+    None,# Cm
+    None,# Bk
+    None,# Cf
+    None,# Es
+    None,# Fm
+    None,# Md
+    None,# No
+    None,# Lr
+    ]
diff --git a/ase/data/cccbdb_ip.py b/ase/data/cccbdb_ip.py
new file mode 100644
index 0000000..4286b76
--- /dev/null
+++ b/ase/data/cccbdb_ip.py
@@ -0,0 +1,79 @@
+"""
+Experimental ionization energies from CCCBDB at
+http://srdata.nist.gov/cccbdb/default.htm
+
+Information presented on these pages is considered public information
+and may be distributed or copied http://www.nist.gov/public_affairs/disclaimer.cfm
+"""
+IP = {# System     IE    IE_vert
+    'H'         : (13.60,  None),
+    'Li'        : ( 5.39,  None),
+    'Be'        : ( 9.32,  None),
+    'B'         : ( 8.30,  None),
+    'C'         : (11.26,  None),
+    'N'         : (14.53,  None),
+    'O'         : (13.62,  None),
+    'F'         : (17.42,  None),
+    'Na'        : ( 5.14,  None),
+    'Mg'        : ( 7.65,  None),
+    'Al'        : ( 5.99,  None),
+    'Si'        : ( 8.15,  None),
+    'P'         : (10.49,  None),
+    'S'         : (10.36,  None),
+    'Cl'        : (12.97,  None),
+    'LiH'       : ( 7.90,  None),
+    'BeH'       : ( 8.21,  None),
+    'CH'        : (10.64,  None),
+    'CH2_s3B1d' : (10.40,  None),
+    'CH3'       : ( 9.84,  None),
+    'CH4'       : (12.61, 13.60),
+    'NH'        : (13.10, 13.49),
+    'NH2'       : (10.78, 12.00),
+    'NH3'       : (10.07, 10.82),
+    'OH'        : (13.02,  None),
+    'H2O'       : (12.62,  None),
+    'HF'        : (16.03, 16.12),
+    'SiH2_s1A1d': ( 8.92,  None),
+    'SiH3'      : ( 8.14,  8.74),
+    'SiH4'      : (11.00, 12.30),
+    'PH2'       : ( 9.82,  None),
+    'PH3'       : ( 9.87, 10.95),
+    'SH2'       : (10.46, 10.50),
+    'HCl'       : (12.74,  None),
+    'Li2'       : ( 5.11,  None),
+    'LiF'       : (11.30,  None),
+    'C2H2'      : (11.40, 11.49),
+    'C2H4'      : (10.51, 10.68),
+    'CN'        : (13.60,  None),
+    'HCN'       : (13.60, 13.61),
+    'CO'        : (14.01, 14.01),
+    'HCO'       : ( 8.12,  9.31),
+    'H2CO'      : (10.88, 10.88),
+    'CH3OH'     : (10.84, 10.96),
+    'N2'        : (15.58, 15.58),
+    'N2H4'      : ( 8.10,  8.98),
+    'NO'        : ( 9.26,  9.26),
+    'O2'        : (12.07, 12.30),
+    'H2O2'      : (10.58, 11.70),
+    'F2'        : (15.70, 15.70),
+    'CO2'       : (13.78, 13.78),
+    'Na2'       : ( 4.89,  None),
+    'Si2'       : ( 7.90,  None),
+    'P2'        : (10.53, 10.62),
+    'S2'        : ( 9.36,  9.55),
+    'Cl2'       : (11.48, 11.49),
+    'NaCl'      : ( 9.20,  9.80),
+    'SiO'       : (11.49,  None),
+    'CS'        : (11.33,  None),
+    'SO'        : (11.29,  None),
+    'ClO'       : (10.89, 11.01),
+    'ClF'       : (12.66, 12.77),
+    'Si2H6'     : ( 9.74, 10.53),
+    'CH3Cl'     : (11.26, 11.29),
+    'CH3SH'     : ( 9.44,  9.44),
+    'HOCl'      : (11.12,  None),
+    'SO2'       : (12.35, 12.50),
+    'C6H6'      : ( 9.24,  9.25),
+    'C12H10'    : ( 8.16,  None), # Biphenyl
+    'C10H8'     : ( 8.14,  None), # Naphthalene
+    }
diff --git a/ase/data/colors.py b/ase/data/colors.py
new file mode 100644
index 0000000..d4c08bb
--- /dev/null
+++ b/ase/data/colors.py
@@ -0,0 +1,224 @@
+import numpy as np
+
+
+# Jmol colors.  See: http://jmol.sourceforge.net/jscolors/#color_U
+jmol_colors = np.array([
+(1.000,0.000,0.000) ,# None
+(1.000,1.000,1.000), # H
+(0.851,1.000,1.000), # He
+(0.800,0.502,1.000), # Li
+(0.761,1.000,0.000), # Be
+(1.000,0.710,0.710), # B
+(0.565,0.565,0.565), # C
+(0.188,0.314,0.973), # N
+(1.000,0.051,0.051), # O
+(0.565,0.878,0.314), # F
+(0.702,0.890,0.961), # Ne
+(0.671,0.361,0.949), # Na
+(0.541,1.000,0.000), # Mg
+(0.749,0.651,0.651), # Al
+(0.941,0.784,0.627), # Si
+(1.000,0.502,0.000), # P
+(1.000,1.000,0.188), # S
+(0.122,0.941,0.122), # Cl
+(0.502,0.820,0.890), # Ar
+(0.561,0.251,0.831), # K
+(0.239,1.000,0.000), # Ca
+(0.902,0.902,0.902), # Sc
+(0.749,0.761,0.780), # Ti
+(0.651,0.651,0.671), # V
+(0.541,0.600,0.780), # Cr
+(0.612,0.478,0.780), # Mn
+(0.878,0.400,0.200), # Fe
+(0.941,0.565,0.627), # Co
+(0.314,0.816,0.314), # Ni
+(0.784,0.502,0.200), # Cu
+(0.490,0.502,0.690), # Zn
+(0.761,0.561,0.561), # Ga
+(0.400,0.561,0.561), # Ge
+(0.741,0.502,0.890), # As
+(1.000,0.631,0.000), # Se
+(0.651,0.161,0.161), # Br
+(0.361,0.722,0.820), # Kr
+(0.439,0.180,0.690), # Rb
+(0.000,1.000,0.000), # Sr
+(0.580,1.000,1.000), # Y
+(0.580,0.878,0.878), # Zr
+(0.451,0.761,0.788), # Nb
+(0.329,0.710,0.710), # Mo
+(0.231,0.620,0.620), # Tc
+(0.141,0.561,0.561), # Ru
+(0.039,0.490,0.549), # Rh
+(0.000,0.412,0.522), # Pd
+(0.753,0.753,0.753), # Ag
+(1.000,0.851,0.561), # Cd
+(0.651,0.459,0.451), # In
+(0.400,0.502,0.502), # Sn
+(0.620,0.388,0.710), # Sb
+(0.831,0.478,0.000), # Te
+(0.580,0.000,0.580), # I
+(0.259,0.620,0.690), # Xe
+(0.341,0.090,0.561), # Cs
+(0.000,0.788,0.000), # Ba
+(0.439,0.831,1.000), # La
+(1.000,1.000,0.780), # Ce
+(0.851,1.000,0.780), # Pr
+(0.780,1.000,0.780), # Nd
+(0.639,1.000,0.780), # Pm
+(0.561,1.000,0.780), # Sm
+(0.380,1.000,0.780), # Eu
+(0.271,1.000,0.780), # Gd
+(0.188,1.000,0.780), # Tb
+(0.122,1.000,0.780), # Dy
+(0.000,1.000,0.612), # Ho
+(0.000,0.902,0.459), # Er
+(0.000,0.831,0.322), # Tm
+(0.000,0.749,0.220), # Yb
+(0.000,0.671,0.141), # Lu
+(0.302,0.761,1.000), # Hf
+(0.302,0.651,1.000), # Ta
+(0.129,0.580,0.839), # W
+(0.149,0.490,0.671), # Re
+(0.149,0.400,0.588), # Os
+(0.090,0.329,0.529), # Ir
+(0.816,0.816,0.878), # Pt
+(1.000,0.820,0.137), # Au
+(0.722,0.722,0.816), # Hg
+(0.651,0.329,0.302), # Tl
+(0.341,0.349,0.380), # Pb
+(0.620,0.310,0.710), # Bi
+(0.671,0.361,0.000), # Po
+(0.459,0.310,0.271), # At
+(0.259,0.510,0.588), # Rn
+(0.259,0.000,0.400), # Fr
+(0.000,0.490,0.000), # Ra
+(0.439,0.671,0.980), # Ac
+(0.000,0.729,1.000), # Th
+(0.000,0.631,1.000), # Pa
+(0.000,0.561,1.000), # U
+(0.000,0.502,1.000), # Np
+(0.000,0.420,1.000), # Pu
+(0.329,0.361,0.949), # Am
+(0.471,0.361,0.890), # Cm
+(0.541,0.310,0.890), # Bk
+(0.631,0.212,0.831), # Cf
+(0.702,0.122,0.831), # Es
+(0.702,0.122,0.729), # Fm
+(0.702,0.051,0.651), # Md
+(0.741,0.051,0.529), # No
+(0.780,0.000,0.400), # Lr
+(0.800,0.000,0.349), # Rf
+(0.820,0.000,0.310), # Db
+(0.851,0.000,0.271), # Sg
+(0.878,0.000,0.220), # Bh
+(0.902,0.000,0.180), # Hs
+(0.922,0.000,0.149), # Mt
+])
+
+# CPK colors in units of RGB values:
+cpk_colors = np.array([ 
+(1.000,0.000,0.000) ,# None
+(1.000,1.000,1.000) ,# H
+(1.000,0.753,0.796) ,# He
+(0.698,0.133,0.133) ,# Li
+(1.000,0.078,0.576) ,# Be
+(0.000,1.000,0.000) ,# B
+(0.784,0.784,0.784) ,# C
+(0.561,0.561,1.000) ,# N
+(0.941,0.000,0.000) ,# O
+(0.855,0.647,0.125) ,# F
+(1.000,0.078,0.576) ,# Ne
+(0.000,0.000,1.000) ,# Na
+(0.133,0.545,0.133) ,# Mg
+(0.502,0.502,0.565) ,# Al
+(0.855,0.647,0.125) ,# Si
+(1.000,0.647,0.000) ,# P
+(1.000,0.784,0.196) ,# S
+(0.000,1.000,0.000) ,# Cl
+(1.000,0.078,0.576) ,# Ar
+(1.000,0.078,0.576) ,# K
+(0.502,0.502,0.565) ,# Ca
+(1.000,0.078,0.576) ,# Sc
+(0.502,0.502,0.565) ,# Ti
+(1.000,0.078,0.576) ,# V
+(0.502,0.502,0.565) ,# Cr
+(0.502,0.502,0.565) ,# Mn
+(1.000,0.647,0.000) ,# Fe
+(1.000,0.078,0.576) ,# Co
+(0.647,0.165,0.165) ,# Ni
+(0.647,0.165,0.165) ,# Cu
+(0.647,0.165,0.165) ,# Zn
+(1.000,0.078,0.576) ,# Ga
+(1.000,0.078,0.576) ,# Ge
+(1.000,0.078,0.576) ,# As
+(1.000,0.078,0.576) ,# Se
+(0.647,0.165,0.165) ,# Br
+(1.000,0.078,0.576) ,# Kr
+(1.000,0.078,0.576) ,# Rb
+(1.000,0.078,0.576) ,# Sr
+(1.000,0.078,0.576) ,# Y
+(1.000,0.078,0.576) ,# Zr
+(1.000,0.078,0.576) ,# Nb
+(1.000,0.078,0.576) ,# Mo
+(1.000,0.078,0.576) ,# Tc
+(1.000,0.078,0.576) ,# Ru
+(1.000,0.078,0.576) ,# Rh
+(1.000,0.078,0.576) ,# Pd
+(0.502,0.502,0.565) ,# Ag
+(1.000,0.078,0.576) ,# Cd
+(1.000,0.078,0.576) ,# In
+(1.000,0.078,0.576) ,# Sn
+(1.000,0.078,0.576) ,# Sb
+(1.000,0.078,0.576) ,# Te
+(0.627,0.125,0.941) ,# I
+(1.000,0.078,0.576) ,# Xe
+(1.000,0.078,0.576) ,# Cs
+(1.000,0.647,0.000) ,# Ba
+(1.000,0.078,0.576) ,# La
+(1.000,0.078,0.576) ,# Ce
+(1.000,0.078,0.576) ,# Pr
+(1.000,0.078,0.576) ,# Nd
+(1.000,0.078,0.576) ,# Pm
+(1.000,0.078,0.576) ,# Sm
+(1.000,0.078,0.576) ,# Eu
+(1.000,0.078,0.576) ,# Gd
+(1.000,0.078,0.576) ,# Tb
+(1.000,0.078,0.576) ,# Dy
+(1.000,0.078,0.576) ,# Ho
+(1.000,0.078,0.576) ,# Er
+(1.000,0.078,0.576) ,# Tm
+(1.000,0.078,0.576) ,# Yb
+(1.000,0.078,0.576) ,# Lu
+(1.000,0.078,0.576) ,# Hf
+(1.000,0.078,0.576) ,# Ta
+(1.000,0.078,0.576) ,# W
+(1.000,0.078,0.576) ,# Re
+(1.000,0.078,0.576) ,# Os
+(1.000,0.078,0.576) ,# Ir
+(1.000,0.078,0.576) ,# Pt
+(0.855,0.647,0.125) ,# Au
+(1.000,0.078,0.576) ,# Hg
+(1.000,0.078,0.576) ,# Tl
+(1.000,0.078,0.576) ,# Pb
+(1.000,0.078,0.576) ,# Bi
+(1.000,0.078,0.576) ,# Po
+(1.000,0.078,0.576) ,# At
+(1.000,1.000,1.000) ,# Rn
+(1.000,1.000,1.000) ,# Fr
+(1.000,1.000,1.000) ,# Ra
+(1.000,1.000,1.000) ,# Ac
+(1.000,0.078,0.576) ,# Th
+(1.000,1.000,1.000) ,# Pa
+(1.000,0.078,0.576) ,# U
+(1.000,1.000,1.000) ,# Np
+(1.000,1.000,1.000) ,# Pu
+(1.000,1.000,1.000) ,# Am
+(1.000,1.000,1.000) ,# Cm
+(1.000,1.000,1.000) ,# Bk
+(1.000,1.000,1.000) ,# Cf
+(1.000,1.000,1.000) ,# Es
+(1.000,1.000,1.000) ,# Fm
+(1.000,1.000,1.000) ,# Md
+(1.000,1.000,1.000) ,# No
+(1.000,1.000,1.000)  # Lw
+])
diff --git a/ase/data/dbh24.py b/ase/data/dbh24.py
new file mode 100644
index 0000000..93d18e4
--- /dev/null
+++ b/ase/data/dbh24.py
@@ -0,0 +1,540 @@
+"""
+The following contains a database of 24 gas-phase reaction barrier heights for small molecules.
+It is the DBH24 (diverse barrier heights) set of the Truhlar group,
+with 12 forward and 12 backward barriers.
+All geometries are from
+Zheng, Zhao and Truhler, "J. Chem. Theo. Comput.", 3:569-582, 2007
+while energies are from
+Zheng, Zhao and Truhler, "J. Chem. Theo. Comput.", 5:808-821, 2009
+"""
+
+from ase.atoms import Atoms
+
+dbh24 = ['dbh24_H', 'dbh24_N2O','dbh24_OH','dbh24_N2','dbh24_tst_H_N2O__OH_N2',
+	 'dbh24_HCl','dbh24_tst_H_ClH__HCl_H',
+	 'dbh24_CH3','dbh24_FCl','dbh24_CH3F','dbh24_Cl','dbh24_tst_CH3_FCl__CH3F_Cl',
+         'dbh24_Cl-ion_CH3Cl','dbh24_tst_Cl-ion_CH3Cl',
+         'dbh24_F-ion_CH3Cl','dbh24_Cl-ion_CH3F','dbh24_tst-Cl-ion_CH3F__F_ion_CH3Cl',
+         'dbh24_OH-ion','dbh24_CH3OH','dbh24_F-ion','dbh24_tst-OH-ion_CH3F__F_ion_CH3OH',
+         'dbh24_HN2','dbh24_tst_H_N2__HN2',
+         'dbh24_C2H4','dbh24_CH3CH2','dbh24_tst_H_C2H4__CH3CH2',
+         'dbh24_HCN','dbh24_HNC','dbh24_tst_HCN__HNC',
+         'dbh24_CH4','dbh24_H2O','dbh24_tst_OH_CH4__CH3_H2O',
+         'dbh24_H2','dbh24_O','dbh24_tst_H_OH__O_H2',
+         'dbh24_H2S','dbh24_HS','dbh24_tst_H_H2S__H2_HS']
+
+dbh24_reaction_list = {
+'dbh24_r1': {
+    'description': 'HAT 1',
+    'number': 1,
+    'initial': ['dbh24_H', 'dbh24_N2O'],
+    'final': ['dbh24_OH','dbh24_N2'],
+    'tst': 'dbh24_tst_H_N2O__OH_N2'},
+'dbh24_r2': {
+    'description': 'HAT 2',
+    'number': 2,
+    'initial': ['dbh24_H', 'dbh24_HCl'],
+    'final': ['dbh24_HCl', 'dbh24_H'],
+    'tst': 'dbh24_tst_H_ClH__HCl_H'},
+'dbh24_r3': {
+    'description': 'HAT 3',
+    'number': 3,
+    'initial': ['dbh24_CH3', 'dbh24_FCl'],
+    'final': ['dbh24_CH3F', 'dbh24_Cl'],
+    'tst': 'dbh24_tst_CH3_FCl__CH3F_Cl'},
+'dbh24_r4': {
+    'description': 'NS 1',
+    'number': 4,
+    'initial': ['dbh24_Cl-ion_CH3Cl'],
+    'final': ['dbh24_Cl-ion_CH3Cl'],
+    'tst': 'dbh24_tst_Cl-ion_CH3Cl'},
+'dbh24_r5': {
+    'description': 'NS 2',
+    'number': 5,
+    'initial': ['dbh24_F-ion_CH3Cl'],
+    'final': ['dbh24_Cl-ion_CH3F'],
+    'tst': 'dbh24_tst-Cl-ion_CH3F__F_ion_CH3Cl'},
+'dbh24_r6': {
+    'description': 'NS 3',
+    'number': 6,
+    'initial': ['dbh24_OH-ion', 'dbh24_CH3F'],
+    'final': ['dbh24_CH3OH', 'dbh24_F-ion'],
+    'tst': 'dbh24_tst-OH-ion_CH3F__F_ion_CH3OH'},
+'dbh24_r7': {
+    'description': 'UA 1',
+    'number': 7,
+    'initial': ['dbh24_H', 'dbh24_N2'],
+    'final': ['dbh24_HN2'],
+    'tst': 'dbh24_tst_H_N2__HN2'},
+'dbh24_r8': {
+    'description': 'UA 2',
+    'number': 8,
+    'initial': ['dbh24_H', 'dbh24_C2H4'],
+    'final': ['dbh24_CH3CH2'],
+    'tst': 'dbh24_tst_H_C2H4__CH3CH2'},
+'dbh24_r9': {
+    'description': 'UA 3',
+    'number': 9,
+    'initial': ['dbh24_HCN'],
+    'final': ['dbh24_HNC'],
+    'tst': 'dbh24_tst_HCN__HNC'},
+'dbh24_r10': {
+    'description': 'HT 1',
+    'number': 10,
+    'initial': ['dbh24_OH', 'dbh24_CH4'],
+    'final': ['dbh24_CH3', 'dbh24_H2O'],
+    'tst': 'dbh24_tst_OH_CH4__CH3_H2O'},
+'dbh24_r11': {
+    'description': 'HT 2',
+    'number': 11,
+    'initial': ['dbh24_H', 'dbh24_OH'],
+    'final': ['dbh24_O', 'dbh24_H2'],
+    'tst': 'dbh24_tst_H_OH__O_H2'},
+'dbh24_r12': {
+    'description': 'HT 3',
+    'number': 12,
+    'initial': ['dbh24_H', 'dbh24_H2S'],
+    'final': ['dbh24_H2', 'dbh24_HS'],
+    'tst': 'dbh24_tst_H_H2S__H2_HS'}
+}
+
+data = {
+# reaction 1 = HAT 1
+'dbh24_H': {
+    'name': 'dbh24_H',
+    'symbols': 'H',
+    'magmoms': [1.],
+    'charge': 0.,
+    'positions': [[0. , 0. , 0.]]},
+'dbh24_N2O': {
+    'name': "dbh24_N2O",
+    'symbols': 'NNO',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.      ,  0.      , -1.195674],
+                  [ 0.      ,  0.      , -0.075111],
+                  [ 0.      ,  0.      ,  1.111937]]},
+'dbh24_OH': {
+    'name': "dbh24_OH",
+    'symbols': 'OH',
+    'magmoms': [ 1., 0.],
+    'charge': 0.,
+    'positions': [[ 0.      ,  0.      ,  0.106894],
+                  [ 0.      ,  0.      , -0.855149]]},
+'dbh24_N2': {
+    'name': "dbh24_N2",
+    'symbols': 'NN',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.     ,  0.     ,  0.548555],
+                  [ 0.     ,  0.     , -0.548555]]},
+'dbh24_tst_H_N2O__OH_N2': {
+    'name': "dbh24_tst_H_N2O__OH_N2",
+    'Vf': 17.13, # kcal/mol
+    'Vb': 82.47, # kcal/mol
+    'symbols': 'HONN',
+    'magmoms': [1., 0., 0., 0.],
+    'charge': 0.,
+    'positions': [[ -0.303286,	-1.930712, 0.],
+		  [ -0.861006,  -0.621526, 0.],
+		  [  0.000000, 	 0.257027, 0.],
+                  [  1.027333,   0.729104, 0.]]},
+# reaction 2 = HAT 2
+'dbh24_HCl': {
+    'name': "dbh24_HCl",
+    'symbols': 'HCl',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.     ,  0.     , -1.203645],
+                  [ 0.     ,  0.     , 0.070803]]},
+'dbh24_tst_H_ClH__HCl_H': {
+    'name': "dbh24_tst_H_ClH__HCl_H",
+    'Vf': 18.00, # kcal/mol
+    'Vb': 18.00, # kcal/mol
+    'symbols': 'HClH',
+    'magmoms': [1., 0., 0.],
+    'charge': 0.,
+    'positions': [[ 0., 0.,  1.485800],
+		  [ 0., 0.,  0.      ],
+		  [ 0., 0., -1.485800]]},
+# reaction 3 = HAT 3
+'dbh24_CH3': {
+    'name': "dbh24_CH3",
+    'symbols': 'CHHH',
+    'magmoms': [1.,0.,0.,0.],
+    'charge': 0.,
+    'positions': [[  0.,	0., 	  0.],
+		  [  1.077317,	0., 	  0.],
+		  [ -0.538659,  0.932984, 0.],
+		  [ -0.538659, -0.932984, 0.]]},
+'dbh24_FCl': {
+    'name': "dbh24_FCl",
+    'symbols': 'FCl',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[  0.,  0., -1.065985],
+		  [  0.,  0.,   0.564345]]},
+'dbh24_CH3F': {
+    'name': "dbh24_CH3F",
+    'symbols': 'CFHHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ -0.632074,  0.000001,   0.000000],
+		  [  0.749117,  0.000002,  -0.000002],
+		  [ -0.983182, -0.338489,   0.972625],
+		  [ -0.983222,  1.011553,  -0.193172],
+		  [ -0.983203, -0.673084,  -0.779437]]},
+'dbh24_Cl': {
+    'name': 'dbh24_Cl',
+    'symbols': 'Cl',
+    'magmoms': [1.],
+    'charge': 0.,
+    'positions': [[0. , 0. , 0.]]},
+'dbh24_tst_CH3_FCl__CH3F_Cl': {
+    'name': "dbh24_tst_CH3_FCl__CH3F_Cl",
+    'Vf': 6.75, # kcal/mol
+    'Vb': 60.00, # kcal/mol
+    'symbols': 'ClFCHHH',
+    'magmoms': [0.,0.,1.,0.,0.,0.],
+    'charge': 0.,
+    'positions': [[  1.454749, 	-0.001237,	-0.000040],
+		  [ -0.323587,	 0.004631,	 0.000124],
+		  [ -2.387418,	-0.002147,	-0.000073],
+		  [ -2.495086,  -0.855361,	-0.649404],
+		  [ -2.497313,  -0.138673,	 1.063139],
+		  [ -2.501537,	 0.986269, 	-0.413734]]},
+# reaction 4 = NS 1
+'dbh24_Cl-ion_CH3Cl': {
+    'name': "dbh24_Cl-ion_CH3Cl",
+    'symbols': 'ClCHHHCl',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000000,  0.000000, -2.384735],
+                  [ 0.000000,  0.000000, -0.566331],
+                  [ 0.000000,  1.025066, -0.224379],
+                  [-0.887734, -0.512533, -0.224379],
+                  [ 0.887734, -0.512533, -0.224379],
+                  [ 0.000000,  0.000000,  2.624213]]},
+'dbh24_tst_Cl-ion_CH3Cl': {
+    'name': "dbh24_tst_Cl-ion_CH3Cl",
+    'Vf': 13.41, # kcal/mol
+    'Vb': 13.41, # kcal/mol
+    'symbols': 'ClCHHHCl',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000025,  0.019526,  2.322499],
+                  [ 0.000513,  0.000486, -0.000089],
+                  [ 0.761278, -0.750733,  0.006377],
+                  [-1.030451, -0.282724,  0.002147],
+                  [ 0.270728,  1.034927, -0.008697],
+                  [-0.000297, -0.019784, -2.322458]]},
+# reaction 5 = NS 2
+'dbh24_F-ion_CH3Cl': {
+    'name': "dbh24_F-ion_CH3Cl",
+    'symbols': 'ClCHHHF',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000000,  0.000000,  1.623138],
+                  [ 0.000000,  0.000000, -0.227358],
+                  [ 0.000000,  1.026321, -0.555141],
+                  [ 0.888820, -0.513160, -0.555141],
+                  [-0.888820, -0.513160, -0.555141],
+                  [ 0.000000,  0.000000, -2.729308]]},
+'dbh24_Cl-ion_CH3F': {
+    'name': "dbh24_Cl-ion_CH3F",
+    'symbols': 'FCHHHCl',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000000,  0.000000, -2.648539],
+                  [ 0.000000,  0.000000, -1.240170],
+                  [ 0.000000,  1.024719, -0.886406],
+                  [-0.887432, -0.512359, -0.886406],
+                  [ 0.887432, -0.512359, -0.886406],
+                  [ 0.000000,  0.000000,  1.996299]]},
+'dbh24_tst-Cl-ion_CH3F__F_ion_CH3Cl': {
+    'name': "dbh24_tst-Cl-ion_CH3F__F_ion_CH3Cl",
+    'Vf': 3.44, # kcal/mol
+    'Vb': 29.42, # kcal/mol
+    'symbols': 'FCHHHCl',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000000,  0.000000, -2.537929],
+                  [ 0.000000,  0.000000, -0.488372],
+                  [ 1.062087,  0.000000, -0.614972],
+                  [-0.531044,  0.919794, -0.614972],
+                  [-0.531044, -0.919794, -0.614972],
+                  [ 0.000000,  0.000000,  1.624501]]},
+# reaction 6 = NS 3
+'dbh24_OH-ion': {
+    'name': "dbh24_OH-ion",
+    'symbols': 'OH',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.000000,  0.000000,  0.106894],
+                  [ 0.000000,  0.000000, -0.855149]]},
+'dbh24_CH3OH': {
+    'name': "dbh24_CH3OH",
+    'symbols': 'COHHHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ -0.046423,  0.663069,  0.000000],
+                  [ -0.046423, -0.755063,  0.000000],
+                  [ -1.086956,  0.975938,  0.000000],
+                  [  0.860592, -1.057039,  0.000000],
+                  [  0.438145,  1.071594,  0.889539],
+                  [  0.438145,  1.071594, -0.889539]]},
+'dbh24_F-ion': {
+    'name': "dbh24_F-ion",
+    'symbols': 'F',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 0.0, 0.0, 0.0]]},
+'dbh24_tst-OH-ion_CH3F__F_ion_CH3OH': {
+    'name': "dbh24_tst-OH-ion_CH3F__F_ion_CH3OH",
+    'Vf': -2.44, # kcal/mol
+    'Vb': 17.66, # kcal/mol
+    'symbols': 'FCHHHOH',
+    'magmoms': None,
+    'charge': -1.,
+    'positions': [[ 1.850614, -0.013179, -0.000128],
+                  [ 0.090857,  0.010586,  0.000269],
+                  [ 0.040907,  1.079548, -0.011749],
+                  [ 0.037163, -0.528013, -0.922944],
+                  [ 0.037486, -0.507463,  0.935132],
+                  [-1.892801,  0.103266, -0.000118],
+                  [-2.173821, -0.815112,  0.000039]]},
+# reaction 7 = UA 1
+'dbh24_HN2': {
+    'name': "dbh24_HN2",
+    'symbols': 'NNH',
+    'magmoms': [1., 0., 0.],
+    'charge': 0.,
+    'positions': [[ -0.062442,  0.659491,  0.000000],
+                  [ -0.062442, -0.518709,  0.000000],
+                  [  0.874194, -0.985478,  0.000000]]},
+'dbh24_tst_H_N2__HN2': {
+    'name': "dbh24_tst_H_N2__HN2",
+    'Vf': 14.36, # kcal/mol
+    'Vb': 10.61, # kcal/mol
+    'symbols': 'NNH',
+    'magmoms': [1., 0., 0.],
+    'charge': 0.,
+    'positions': [[ 0.084563, -0.642934,  0.000000],
+                  [ 0.084563,  0.479877,  0.000000],
+                  [-1.183883,  1.141399,  0.000000]]},
+# reaction 8 = UA 2
+'dbh24_C2H4': {
+    'name': "dbh24_C2H4",
+    'symbols': 'CCHHHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.665593],
+                  [ 0.000000,  0.000000, -0.665593],
+                  [ 0.000000,  0.921495,  1.231668],
+                  [ 0.000000, -0.921495,  1.231668],
+                  [ 0.000000,  0.921495, -1.231668],
+                  [ 0.000000, -0.921495, -1.231668]]},
+'dbh24_CH3CH2': {
+    'name': "dbh24_CH3CH2",
+    'symbols': 'CCHHHHH',
+    'magmoms': [1.,0.,0.,0.,0.,0.,0.],
+    'charge': 0.,
+    'positions': [[ -0.258719, -0.816829, 0.000000],
+                  [ -0.250987,  0.674191, 0.000000],
+                  [  0.758830, -1.225939, 0.000000],
+                  [ -0.758830, -1.213866, 0.883419],
+                  [ -0.758830, -1.213866,-0.883419],
+                  [ -0.170021,  1.225939,-0.924320],
+                  [ -0.170021,  1.225939, 0.924320]]},
+'dbh24_tst_H_C2H4__CH3CH2': {
+    'name': "dbh24_tst_H_C2H4__CH3CH2",
+    'Vf': 1.72, # kcal/mol
+    'Vb': 41.75, # kcal/mol
+    'symbols': 'CCHHHHH',
+    'magmoms': [1.,0.,0.,0.,0.,0.,0.],
+    'charge': 0.,
+    'positions': [[ -0.567877,  0.000051, -0.218958],
+                  [  0.751139, -0.000036,  0.041932],
+                  [ -1.493884, -0.000488,  1.531765],
+                  [ -1.101691,  0.920651, -0.408626],
+                  [ -1.102022, -0.920234, -0.409110],
+                  [  1.299128, -0.922344,  0.173763],
+                  [  1.298899,  0.922325,  0.174363]]},
+# reaction 9 = UA 3
+'dbh24_HCN': {
+    'name': "dbh24_HCN",
+    'symbols': 'CNH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  -0.500365],
+                  [ 0.000000,  0.000000,   0.652640],
+                  [ 0.000000,  0.000000,  -1.566291]]},
+'dbh24_HNC': {
+    'name': "dbh24_HNC",
+    'symbols': 'CNH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000, -0.737248],
+                  [ 0.000000,  0.000000,  0.432089],
+                  [ 0.000000,  0.000000,  1.426960]]},
+'dbh24_tst_HCN__HNC': {
+    'name': "dbh24_tst_HCN__HNC",
+    'Vf': 48.07, # kcal/mol
+    'Vb': 32.82, # kcal/mol
+    'symbols': 'CNH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.080319,  0.620258,  0.000000],
+                  [ 0.080319, -0.568095,  0.000000],
+                  [-1.044148,  0.255121,  0.000000]]},
+# reaction 10 = HT 1
+'dbh24_CH4': {
+    'name': "dbh24_CH4",
+    'symbols': 'CHHHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.000000],
+                  [ 0.627837,  0.627837,  0.627837],
+                  [-0.627837, -0.627837,  0.627837],
+                  [ 0.627837, -0.627837, -0.627837],
+                  [-0.627837,  0.627837, -0.627837]]},
+'dbh24_H2O': {
+    'name': "dbh24_H2O",
+    'symbols': 'OHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.117145],
+                  [ 0.000000,  0.756709, -0.468582],
+                  [ 0.000000, -0.756709, -0.468582]]},
+'dbh24_tst_OH_CH4__CH3_H2O': {
+    'name': "dbh24_tst_OH_CH4__CH3_H2O",
+    'Vf': 6.7, # kcal/mol
+    'Vb': 19.6, # kcal/mol
+    'symbols': 'COHHHHH',
+    'magmoms': [0.,1.,0.,0.,0.,0.,0.],
+    'charge': 0.,
+    'positions': [[ -1.211487,  0.007968,  0.000407],
+                  [  1.293965, -0.108694,  0.000133],
+                  [  0.009476, -0.118020,  0.002799],
+                  [ -1.525529, -0.233250,  1.010070],
+                  [ -1.430665,  1.033233, -0.278082],
+                  [ -1.552710, -0.710114, -0.737702],
+                  [  1.416636,  0.849894, -0.000591]]},
+# reaction 11 = HT 2
+'dbh24_O': {
+    'name': 'dbh24_O',
+    'symbols': 'O',
+    'magmoms': [2.],
+    'charge': 0.,
+    'positions': [[0. , 0. , 0.]]},
+'dbh24_H2': {
+    'name': "dbh24_H2",
+    'symbols': 'HH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.370938],
+                  [ 0.000000,  0.000000, -0.370938]]},
+'dbh24_tst_H_OH__O_H2': {
+    'name': "dbh24_tst_H_OH__O_H2",
+    'Vf': 10.7, # kcal/mol
+    'Vb': 13.1, # kcal/mol
+    'symbols': 'HOH',
+    'magmoms': [1.,0.,1.],
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000, -0.860287],
+                  [ 0.000000,  0.000000,  0.329024],
+                  [ 0.000000,  0.000000, -1.771905]]},
+# reaction 12 = HT 3
+'dbh24_H2S': {
+    'name': "dbh24_H2S",
+    'symbols': 'SHH',
+    'magmoms': None,
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.102519],
+                  [ 0.000000,  0.966249, -0.820154],
+                  [ 0.000000, -0.966249, -0.820154]]},
+'dbh24_HS': {
+    'name': "dbh24_HS",
+    'symbols': 'SH',
+    'magmoms': [0.,1.],
+    'charge': 0.,
+    'positions': [[ 0.000000,  0.000000,  0.078835],
+                  [ 0.000000,  0.000000, -1.261367]]},
+'dbh24_tst_H_H2S__H2_HS': {
+    'name': "dbh24_tst_H_H2S__H2_HS",
+    'Vf': 3.6, # kcal/mol
+    'Vb': 17.3, # kcal/mol
+    'symbols': 'HSHH',
+    'magmoms': [0.,1.,0.,0.],
+    'charge': 0.,
+    'positions': [[ 1.262097, -0.220097,  0.000000],
+                  [ 0.000000,  0.223153,  0.000000],
+                  [-0.500576, -1.115445,  0.000000],
+                  [-0.761521, -2.234913,  0.000000]]},
+}
+
+def create_dbh24_system(name, **kwargs):
+    """Creates a DBH24 system.
+    """
+    if name not in data:
+        raise NotImplementedError('System %s not in database.' % name)
+    d = data[name]
+    if 'magmoms' not in kwargs:
+        kwargs['magmoms'] = d['magmoms']
+    return Atoms(d['symbols'], d['positions'], **kwargs)
+
+def get_dbh24_magmoms(name):
+    """Returns the magnetic moments of DBH24 systems.
+    """
+    if name not in data:
+        raise KeyError('System %s not in database.' % name)
+    else:
+        return data[name]['magmoms']
+
+def get_dbh24_charge(name):
+    """ Returns the total charge of DBH24 systems.
+    """
+    assert name in dbh24
+    d = data[name]
+    charge = d['charge']
+    return charge
+
+def get_dbh24_Vf(name):
+    """ Returns forward DBH24 TST barrier in kcal/mol
+    """
+    assert name in dbh24
+    d = data[name]
+    Vf = d['Vf']
+    return Vf
+
+def get_dbh24_Vb(name):
+    """ Returns backward DBH24 TST barrier in kcal/mol
+    """
+    assert name in dbh24
+    d = data[name]
+    Vb = d['Vb']
+    return Vb
+
+def get_dbh24_initial_states(name):
+    """ Returns initial DBH24 states
+    """
+    assert name in dbh24_reaction_list
+    d = dbh24_reaction_list[name]
+    initial = d['initial']
+    return initial
+
+def get_dbh24_final_states(name):
+    """ Returns final DBH24 states
+    """
+    assert name in dbh24_reaction_list
+    d = dbh24_reaction_list[name]
+    final = d['final']
+    return final
+
+def get_dbh24_tst(name):
+    """ Returns DBH24 TST names
+    """
+    assert name in dbh24_reaction_list
+    d = dbh24_reaction_list[name]
+    tst = d['tst']
+    return tst
diff --git a/ase/data/extra_molecules.py b/ase/data/extra_molecules.py
new file mode 100644
index 0000000..06365e0
--- /dev/null
+++ b/ase/data/extra_molecules.py
@@ -0,0 +1,158 @@
+"""
+Database of molecules outside the G2_1 set
+
+"""
+
+molecule_names = ['Be2','C7NH5','BDA','biphenyl','C60']
+
+data = {
+'Be2': {
+    'description': "Diatomic Beryllium",
+    'name': "Be_2",
+    'enthalpy': 155.1,
+    'ZPE': 1.0000,
+    'thermal correction': 5.0600,
+    'symbols': 'BeBe',
+    'magmoms': None,
+    'positions': [[ 0.  ,  0.  ,  1.0106],
+                  [ 0.  ,  0.  , -1.0106]]},
+'C7NH5': {
+    'description': "Benzonitride",
+    'name': "C_7NH_5",
+    'symbols': 'C7NH5',
+    'magmoms': None,
+    'positions': [[ -1.593581, -1.142601, 0.],
+                  [ -2.235542,  0.095555, 0.],
+                  [ -0.204885, -1.210726, 0.],
+                  [  0.549645, -0.025355, 0.],
+                  [  1.976332, -0.085321, 0.],
+                  [ -0.099258,  1.220706, 0.],
+                  [ -1.488628,  1.273345, 0.],
+                  [  3.136871, -0.128138, 0.],
+                  [ -2.177996, -2.060896, 0.],
+                  [ -3.323594,  0.141242, 0.],
+                  [  0.301694, -2.173705, 0.],
+                  [  0.488716,  2.136782, 0.],
+                  [ -1.987765,  2.240495, 0.]]},
+'BDA': {
+    'description': "1,4-Benzodiamine",
+    # aka p-Aminoaniline; p-Benzenediamine; p-Diaminobenzene;
+    #     p-Phenylenediamine; Paraphenylen-diamine
+    'name': "BDA",
+    # PBE-gpaw relaxed
+    'symbols': 'C6H4N2H4',
+    'magmoms': None,
+    'positions': [[ 0.004212,  1.406347,  0.061073],
+                  [ 1.193490,  0.687096,  0.029481],
+                  [ 1.190824, -0.690400, -0.028344],
+                  [ 0.000295, -1.406191, -0.059503],
+                  [-1.186974, -0.685668, -0.045413],
+                  [-1.185376,  0.690203,  0.009452],
+                  [ 2.147124,  1.219997,  0.064477],
+                  [ 2.141593, -1.227477, -0.054266],
+                  [-2.138408, -1.222814, -0.095050],
+                  [-2.137740,  1.226930,  0.023036],
+                  [-0.006314,  2.776024,  0.186278],
+                  [-0.007340, -2.777839, -0.159936],
+                  [ 0.844710, -3.256543,  0.110098],
+                  [-0.854965, -3.253324,  0.130125],
+                  [ 0.845826,  3.267270, -0.055549],
+                  [-0.854666,  3.254654, -0.092676]]},
+'biphenyl': {
+    'description': "Biphenyl",
+    'name': "biphenyl",
+    # PBE-gpaw relaxed
+    'ionization energy': 8.16,
+    'symbols': 'C6H5C6H5',
+    'magmoms': None,
+    'positions': [[-0.74081, -0.00000, -0.00003],
+                  [-1.46261, -1.20370, -0.00993],
+                  [-2.85531, -1.20350, -0.00663],
+                  [-3.55761, -0.00000, -0.00003],
+                  [-2.85531,  1.20350,  0.00667],
+                  [-1.46261,  1.20370,  0.00997],
+                  [-0.92071, -2.14850,  0.00967],
+                  [-3.38981, -2.15110, -0.00083],
+                  [-4.64571, -0.00000, -0.00003],
+                  [-3.38981,  2.15110,  0.00077],
+                  [-0.92071,  2.14850, -0.00963],
+                  [ 3.55849, -0.00000, -0.00003],
+                  [ 2.85509, -0.86640, -0.83553],
+                  [ 1.46289, -0.87000, -0.83153],
+                  [ 0.73969, -0.00000, -0.00003],
+                  [ 1.46289,  0.87000,  0.83157],
+                  [ 2.85509,  0.86640,  0.83547],
+                  [ 4.64659, -0.00000, -0.00003],
+                  [ 3.39189, -1.53770, -1.50253],
+                  [ 0.91869, -1.53310, -1.50263],
+                  [ 0.91869,  1.53310,  1.50267],
+                  [ 3.39189,  1.53770,  1.50257]]},
+'C60': {
+    'description': "Buckminsterfullerene, I*h symm.",
+    'name': "C_{60}",
+    # The Buckyball has two degrees of freedom, the C-C bond, and the C=C bond.
+    # This is an LDA-gpaw relaxed structure with bond lengths 1.437 and 1.385.
+    # Experimentally, the two bond lengths are 1.45 and 1.40 Angstrom.
+    'symbols': 'C60',
+    'magmoms': None,
+    'positions': [[ 2.2101953,  0.5866631,  2.6669504],
+                  [ 3.1076393,  0.1577008,  1.6300286],
+                  [ 1.3284430, -0.3158939,  3.2363232],
+                  [ 3.0908709, -1.1585005,  1.2014240],
+                  [ 3.1879245, -1.4574599, -0.1997005],
+                  [ 3.2214623,  1.2230966,  0.6739440],
+                  [ 3.3161210,  0.9351586, -0.6765151],
+                  [ 3.2984981, -0.4301142, -1.1204138],
+                  [-0.4480842,  1.3591484,  3.2081020],
+                  [ 0.4672056,  2.2949830,  2.6175264],
+                  [-0.0256575,  0.0764219,  3.5086259],
+                  [ 1.7727917,  1.9176584,  2.3529691],
+                  [ 2.3954623,  2.3095689,  1.1189539],
+                  [-0.2610195,  3.0820935,  1.6623117],
+                  [ 0.3407726,  3.4592388,  0.4745968],
+                  [ 1.6951171,  3.0692446,  0.1976623],
+                  [-2.1258394, -0.8458853,  2.6700963],
+                  [-2.5620990,  0.4855202,  2.3531715],
+                  [-0.8781521, -1.0461985,  3.2367302],
+                  [-1.7415096,  1.5679963,  2.6197333],
+                  [-1.6262468,  2.6357030,  1.6641811],
+                  [-3.2984810,  0.4301871,  1.1204208],
+                  [-3.1879469,  1.4573895,  0.1996030],
+                  [-2.3360261,  2.5813627,  0.4760912],
+                  [-0.5005210, -2.9797771,  1.7940308],
+                  [-1.7944338, -2.7729087,  1.2047891],
+                  [-0.0514245, -2.1328841,  2.7938830],
+                  [-2.5891471, -1.7225828,  1.6329715],
+                  [-3.3160705, -0.9350636,  0.6765268],
+                  [-1.6951919, -3.0692581, -0.1976564],
+                  [-2.3954901, -2.3096853, -1.1189862],
+                  [-3.2214182, -1.2231835, -0.6739581],
+                  [ 2.1758234, -2.0946263,  1.7922529],
+                  [ 1.7118619, -2.9749681,  0.7557198],
+                  [ 1.3130656, -1.6829416,  2.7943892],
+                  [ 0.3959024, -3.4051395,  0.7557638],
+                  [-0.3408219, -3.4591883, -0.4745610],
+                  [ 2.3360057, -2.5814499, -0.4761050],
+                  [ 1.6263757, -2.6357349, -1.6642309],
+                  [ 0.2611352, -3.0821271, -1.6622618],
+                  [-2.2100844, -0.5868636, -2.6670300],
+                  [-1.7726970, -1.9178969, -2.3530466],
+                  [-0.4670723, -2.2950509, -2.6175105],
+                  [-1.3283500,  0.3157683, -3.2362375],
+                  [-2.1759882,  2.0945383, -1.7923294],
+                  [-3.0909663,  1.1583472, -1.2015749],
+                  [-3.1076090, -0.1578453, -1.6301627],
+                  [-1.3131365,  1.6828292, -2.7943639],
+                  [ 0.5003224,  2.9799637, -1.7940203],
+                  [-0.3961148,  3.4052817, -0.7557272],
+                  [-1.7120629,  2.9749122, -0.7557988],
+                  [ 0.0512824,  2.1329478, -2.7937450],
+                  [ 2.1258630,  0.8460809, -2.6700534],
+                  [ 2.5891853,  1.7227742, -1.6329562],
+                  [ 1.7943010,  2.7730684, -1.2048262],
+                  [ 0.8781323,  1.0463514, -3.2365313],
+                  [ 0.4482452, -1.3591061, -3.2080510],
+                  [ 1.7416948, -1.5679557, -2.6197714],
+                  [ 2.5621724, -0.4853529, -2.3532026],
+                  [ 0.0257904, -0.0763567, -3.5084446]]},
+}
diff --git a/ase/data/g2.py b/ase/data/g2.py
new file mode 100644
index 0000000..e0a1969
--- /dev/null
+++ b/ase/data/g2.py
@@ -0,0 +1,47 @@
+"""
+The following contains a database of small molecules
+
+Data for the G2/97 database are from
+Raghavachari, Redfern, and Pople, J. Chem. Phys. Vol. 106, 1063 (1997).
+See http://www.cse.anl.gov/Catalysis_and_Energy_Conversion/Computational_Thermochemistry.shtml for the original files.
+
+All numbers are experimental values, except for coordinates, which are
+MP2(full)/6-31G(d) optimized geometries (from http://www.cse.anl.gov/OldCHMwebsiteContent/compmat/G2-97.htm)
+
+Atomic species:
+ref: Curtiss et al. JCP 106, 1063 (1997).
+'Enthalpy' is the experimental enthalpies of formation at 0K
+'thermal correction' is the thermal corrections H(298)-H(0)
+
+Molecular species:
+ref: Staroverov et al. JCP 119, 12129 (2003)
+'Enthalpy' is the experimental enthalpies of formation at 298K
+'ZPE' is the zero-point energies
+'thermal correction' is the thermal enthalpy corrections H(298K) - H_exp(0K)
+ZPE and thermal corrections are estimated from B3LYP geometries and vibrations.
+
+Experimental ionization potentials are from http://srdata.nist.gov/cccbdb/.
+
+For details about G2-1 and G2-2 sets see doi:10.1063/1.477422.
+"""
+
+from ase.data.g2_1 import data as data_g2_1
+from ase.data.g2_2 import data as data_g2_2
+
+data = data_g2_1.copy()
+
+data.update(data_g2_2)
+
+from ase.data.g2_1 import atom_names as atom_names_g2_1
+from ase.data.g2_1 import molecule_names as molecule_names_g2_1
+from ase.data.g2_2 import atom_names as atom_names_g2_2
+from ase.data.g2_2 import molecule_names as molecule_names_g2_2
+
+atom_names = []
+for a in atom_names_g2_1 + atom_names_g2_2:
+    if a not in atom_names:
+        atom_names.append(a)
+molecule_names = molecule_names_g2_1 + molecule_names_g2_2
+
+from ase.data.g2_2 import get_ionization_energy
+from ase.data.g2_2 import get_atomization_energy
diff --git a/ase/data/g2_1.py b/ase/data/g2_1.py
new file mode 100644
index 0000000..4d5fd63
--- /dev/null
+++ b/ase/data/g2_1.py
@@ -0,0 +1,1020 @@
+"""
+The following contains a database of small molecules
+
+Data for the G2/97 database are from
+Raghavachari, Redfern, and Pople, J. Chem. Phys. Vol. 106, 1063 (1997).
+See http://www.cse.anl.gov/Catalysis_and_Energy_Conversion/Computational_Thermochemistry.shtml for the original files.
+
+All numbers are experimental values, except for coordinates, which are
+MP2(full)/6-31G(d) optimized geometries (from http://www.cse.anl.gov/OldCHMwebsiteContent/compmat/G2-97.htm)
+
+Atomic species:
+ref: Curtiss et al. JCP 106, 1063 (1997).
+'Enthalpy' is the experimental enthalpies of formation at 0K
+'thermal correction' is the thermal corrections H(298)-H(0)
+
+Molecular species:
+ref: Staroverov et al. JCP 119, 12129 (2003)
+'Enthalpy' is the experimental enthalpies of formation at 298K
+'ZPE' is the zero-point energies
+'thermal correction' is the thermal enthalpy corrections H(298K) - H_exp(0K)
+ZPE and thermal corrections are estimated from B3LYP geometries and vibrations.
+
+For details about G2-1 and G2-2 sets see doi:10.1063/1.477422.
+
+Experimental ionization potentials are from http://srdata.nist.gov/cccbdb/
+Information presented on these pages is considered public information
+and may be distributed or copied http://www.nist.gov/public_affairs/disclaimer.cfm
+
+"""
+
+from ase.atoms import Atoms, string2symbols
+
+atom_names = ['H','Li','Be','C','N','O','F','Na','Si','P','S','Cl']
+
+molecule_names = ['LiH','BeH','CH','CH2_s3B1d','CH2_s1A1d','CH3','CH4','NH','NH2','NH3','OH','H2O','HF','SiH2_s1A1d','SiH2_s3B1d','SiH3','SiH4','PH2','PH3','SH2','HCl','Li2','LiF','C2H2','C2H4','C2H6','CN','HCN','CO','HCO','H2CO','CH3OH','N2','N2H4','NO','O2','H2O2','F2','CO2','Na2','Si2','P2','S2','Cl2','NaCl','SiO','CS','SO','ClO','ClF','Si2H6','CH3Cl','CH3SH','HOCl','SO2']
+
+data = {
+'H': {
+    'name': 'Hydrogen',
+    'database': 'G2-1',
+    'symbols': 'H',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 51.63,
+    'thermal correction': 1.01,
+    'ionization energy': 13.60,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Li': {
+    'name': 'Lithium',
+    'database': 'G2-1',
+    'symbols': 'Li',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 37.69,
+    'thermal correction': 1.10,
+    'ionization energy': 5.39,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Be': {
+    'name': 'Beryllium',
+    'database': 'G2-1',
+    'symbols': 'Be',
+    'magmoms': None,
+    'charges': None,
+    'enthalpy': 76.48,
+    'thermal correction': 0.46,
+    'ionization energy': 9.32,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'C': {
+    'name': 'Carbon',
+    'database': 'G2-1',
+    'symbols': 'C',
+    'magmoms': [2.],
+    'charges': None,
+    'enthalpy': 169.98,
+    'thermal correction': 0.25,
+    'ionization energy': 11.26,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'N': {
+    'name': 'Nitrogen',
+    'database': 'G2-1',
+    'symbols': 'N',
+    'magmoms': [3.],
+    'charges': None,
+    'enthalpy': 112.53,
+    'thermal correction': 1.04,
+    'ionization energy': 14.53,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'O': {
+    'name': 'Oxygen',
+    'database': 'G2-1',
+    'symbols': 'O',
+    'magmoms': [2.],
+    'charges': None,
+    'enthalpy': 58.99,
+    'thermal correction': 1.04,
+    'ionization energy': 13.62,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'F': {
+    'name': 'Fluorine',
+    'database': 'G2-1',
+    'symbols': 'F',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 18.47,
+    'thermal correction': 1.05,
+    'ionization energy': 17.42,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Na': {
+    'name': 'Sodium',
+    'database': 'G2-1',
+    'symbols': 'Na',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 25.69,
+    'thermal correction': 1.54,
+    'ionization energy': 5.14,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Si': {
+    'name': 'Silicon',
+    'database': 'G2-1',
+    'symbols': 'Si',
+    'magmoms': [2.],
+    'charges': None,
+    'enthalpy': 106.60,
+    'thermal correction': 0.76,
+    'ionization energy': 8.15,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'P': {
+    'name': 'Phosphorus',
+    'database': 'G2-1',
+    'symbols': 'P',
+    'magmoms': [3.],
+    'charges': None,
+    'enthalpy': 75.42,
+    'thermal correction': 1.28,
+    'ionization energy': 10.49,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'S': {
+    'name': 'Sulfur',
+    'database': 'G2-1',
+    'symbols': 'S',
+    'magmoms': [2.],
+    'charges': None,
+    'enthalpy': 65.66,
+    'thermal correction': 1.05,
+    'ionization energy': 10.36,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Cl': {
+    'name': 'Chlorine',
+    'database': 'G2-1',
+    'symbols': 'Cl',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 28.59,
+    'thermal correction': 1.10,
+    'ionization energy': 12.97,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'LiH': {
+    'description': "Lithium hydride (LiH), C*v symm.",
+    'name': "LiH",
+    'database': 'G2-1',
+    'enthalpy': 33.3,
+    'ZPE': 2.0149,
+    'thermal correction': 2.0783,
+    'ionization energy': 7.90,
+    'symbols': 'LiH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.  ,  0.  ,  0.41],
+                  [ 0.  ,  0.  , -1.23]]},
+'BeH': {
+    'description': "Beryllium hydride (BeH), D*h symm.",
+    'name': "BeH",
+    'database': 'G2-1',
+    'enthalpy': 81.7,
+    'ZPE': 2.9073,
+    'thermal correction': 2.0739,
+    'ionization energy': 8.21,
+    'symbols': 'BeH',
+    'magmoms': [ 0.8,  0.2],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.269654],
+                  [ 0.      ,  0.      , -1.078616]]},
+'CH': {
+    'description': "CH radical. Doublet, C*v symm.",
+    'name': "CH",
+    'database': 'G2-1',
+    'enthalpy': 142.5,
+    'ZPE': 3.9659,
+    'thermal correction': 2.0739,
+    'ionization energy': 10.64,
+    'symbols': 'CH',
+    'magmoms': [ 1.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.160074],
+                  [ 0.      ,  0.      , -0.960446]]},
+'CH2_s3B1d': {
+    'description': "Triplet methylene (CH2), C2v symm, 3-B1.",
+    'name': "CH_2 (^3B_1)",
+    'database': 'G2-1',
+    'enthalpy': 93.7,
+    'ZPE': 10.6953,
+    'thermal correction': 2.3877,
+    'ionization energy': 10.40,
+    'symbols': 'CHH',
+    'magmoms': [ 2.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.110381],
+                  [ 0.      ,  0.982622, -0.331142],
+                  [ 0.      , -0.982622, -0.331142]]},
+'CH2_s1A1d': {
+    'description': "Singlet methylene (CH2), C2v symm, 1-A1.",
+    'name': "CH_2 (^1A_1)",
+    'database': 'G2-1',
+    'enthalpy': 102.8,
+    'ZPE': 10.2422,
+    'thermal correction': 2.3745,
+    'symbols': 'CHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.174343],
+                  [ 0.      ,  0.862232, -0.523029],
+                  [ 0.      , -0.862232, -0.523029]]},
+'CH3': {
+    'description': "Methyl radical (CH3), D3h symm.",
+    'name': "CH_3",
+    'database': 'G2-1',
+    'enthalpy': 35.0,
+    'ZPE': 18.3383,
+    'thermal correction': 2.5383,
+    'ionization energy': 9.84,
+    'symbols': 'CHHH',
+    'magmoms': [ 1.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  1.07841 ,  0.      ],
+                  [ 0.93393 , -0.539205,  0.      ],
+                  [-0.93393 , -0.539205,  0.      ]]},
+'CH4': {
+    'description': "Methane (CH4), Td symm.",
+    'name': "CH_4",
+    'database': 'G2-1',
+    'enthalpy': -17.9,
+    'ZPE': 27.6744,
+    'thermal correction': 2.3939,
+    'ionization energy': 12.64,
+    'vertical ionization energy': 13.60,
+    'symbols': 'CHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.629118,  0.629118,  0.629118],
+                  [-0.629118, -0.629118,  0.629118],
+                  [ 0.629118, -0.629118, -0.629118],
+                  [-0.629118,  0.629118, -0.629118]]},
+'NH': {
+    'description': "NH, triplet, C*v symm.",
+    'name': "NH",
+    'database': 'G2-1',
+    'enthalpy': 85.2,
+    'ZPE': 4.5739,
+    'thermal correction': 2.0739,
+    'ionization energy': 13.10,
+    'vertical ionization energy': 13.49,
+    'symbols': 'NH',
+    'magmoms': [ 2.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.129929],
+                  [ 0.      ,  0.      , -0.909501]]},
+'NH2': {
+    'description': "NH2 radical, C2v symm, 2-B1.",
+    'name': "NH_2",
+    'database': 'G2-1',
+    'enthalpy': 45.1,
+    'ZPE': 11.7420,
+    'thermal correction': 2.3726,
+    'ionization energy': 10.78,
+    'vertical ionization energy': 12.00,
+    'symbols': 'NHH',
+    'magmoms': [ 1.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.14169 ],
+                  [ 0.      ,  0.806442, -0.495913],
+                  [ 0.      , -0.806442, -0.495913]]},
+'NH3': {
+    'description': "Ammonia (NH3), C3v symm.",
+    'name': "NH_3",
+    'database': 'G2-1',
+    'enthalpy': -11.0,
+    'ZPE': 21.2462,
+    'thermal correction': 2.3896,
+    'ionization energy': 10.07,
+    'vertical ionization energy': 10.82,
+    'symbols': 'NHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.116489],
+                  [ 0.      ,  0.939731, -0.271808],
+                  [ 0.813831, -0.469865, -0.271808],
+                  [-0.813831, -0.469865, -0.271808]]},
+'OH': {
+    'description': "OH radical, C*v symm.",
+    'name': "OH",
+    'database': 'G2-1',
+    'enthalpy': 9.4,
+    'ZPE': 5.2039,
+    'thermal correction': 2.0739,
+    'ionization energy': 13.02,
+    'symbols': 'OH',
+    'magmoms': [ 0.5,  0.5],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.108786],
+                  [ 0.      ,  0.      , -0.870284]]},
+'H2O': {
+    'description': "Water (H2O), C2v symm.",
+    'name': "H_2O",
+    'database': 'G2-1',
+    'enthalpy': -57.8,
+    'ZPE': 13.2179,
+    'thermal correction': 2.3720,
+    'ionization energy': 12.62,
+    'symbols': 'OHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.119262],
+                  [ 0.      ,  0.763239, -0.477047],
+                  [ 0.      , -0.763239, -0.477047]]},
+'HF': {
+    'description': "Hydrogen fluoride (HF), C*v symm.",
+    'name': "HF",
+    'database': 'G2-1',
+    'enthalpy': -65.1,
+    'ZPE': 5.7994,
+    'thermal correction': 2.0733,
+    'ionization energy': 16.03,
+    'vertical ionization energy': 16.12,
+    'symbols': 'FH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.093389],
+                  [ 0.      ,  0.      , -0.840502]]},
+'SiH2_s1A1d': {
+    'description': "Singlet silylene (SiH2), C2v symm, 1-A1.",
+    'name': "SiH_2 (^1A_1)",
+    'database': 'G2-1',
+    'enthalpy': 65.2,
+    'ZPE': 7.1875,
+    'thermal correction': 2.3927,
+    'ionization energy': 8.92,
+    'symbols': 'SiHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.131272],
+                  [ 0.      ,  1.096938, -0.918905],
+                  [ 0.      , -1.096938, -0.918905]]},
+'SiH2_s3B1d': {
+    'description': "Triplet silylene (SiH2), C2v symm, 3-B1.",
+    'name': "SiH_2 (^3B_1)",
+    'database': 'G2-1',
+    'enthalpy': 86.2,
+    'ZPE': 7.4203,
+    'thermal correction': 2.4078,
+    'symbols': 'SiHH',
+    'magmoms': [ 2.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.094869],
+                  [ 0.      ,  1.271862, -0.664083],
+                  [ 0.      , -1.271862, -0.664083]]},
+'SiH3': {
+    'description': "Silyl radical (SiH3), C3v symm.",
+    'name': "SiH_3",
+    'database': 'G2-1',
+    'enthalpy': 47.9,
+    'ZPE': 13.0898,
+    'thermal correction': 2.4912,
+    'ionization energy': 8.14,
+    'vertical ionization energy': 8.74,
+    'symbols': 'SiHHH',
+    'magmoms': [ 1.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.079299],
+                  [ 0.      ,  1.41328 , -0.370061],
+                  [ 1.223937, -0.70664 , -0.370061],
+                  [-1.223937, -0.70664 , -0.370061]]},
+'SiH4': {
+    'description': "Silane (SiH4), Td symm.",
+    'name': "SiH_4",
+    'database': 'G2-1',
+    'enthalpy': 8.2,
+    'ZPE': 19.2664,
+    'thermal correction': 2.5232,
+    'ionization energy': 11.00,
+    'vertical ionization energy': 12.30,
+    'symbols': 'SiHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.856135,  0.856135,  0.856135],
+                  [-0.856135, -0.856135,  0.856135],
+                  [-0.856135,  0.856135, -0.856135],
+                  [ 0.856135, -0.856135, -0.856135]]},
+'PH2': {
+    'description': "PH2 radical, C2v symm.",
+    'name': "PH_2",
+    'database': 'G2-1',
+    'enthalpy': 33.1,
+    'ZPE': 8.2725,
+    'thermal correction': 2.3845,
+    'ionization energy': 9.82,
+    'symbols': 'PHH',
+    'magmoms': [ 1.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.115396],
+                  [ 0.      ,  1.025642, -0.865468],
+                  [ 0.      , -1.025642, -0.865468]]},
+'PH3': {
+    'description': "Phosphine (PH3), C3v symm.",
+    'name': "PH_3",
+    'database': 'G2-1',
+    'enthalpy': 1.3,
+    'ZPE': 14.7885,
+    'thermal correction': 2.4203,
+    'ionization energy': 9.87,
+    'vertical ionization energy': 10.95,
+    'symbols': 'PHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.124619],
+                  [ 0.      ,  1.200647, -0.623095],
+                  [ 1.039791, -0.600323, -0.623095],
+                  [-1.039791, -0.600323, -0.623095]]},
+'SH2': {
+    'description': "Hydrogen sulfide (H2S), C2v symm.",
+    'name': "SH_2",
+    'database': 'G2-1',
+    'enthalpy': -4.9,
+    'ZPE': 9.3129,
+    'thermal correction': 2.3808,
+    'ionization energy': 10.46,
+    'vertical ionization energy': 10.50,
+    'symbols': 'SHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.102135],
+                  [ 0.      ,  0.974269, -0.817083],
+                  [ 0.      , -0.974269, -0.817083]]},
+'HCl': {
+    'description': "Hydrogen chloride (HCl), C*v symm.",
+    'name': "HCl",
+    'database': 'G2-1',
+    'enthalpy': -22.1,
+    'ZPE': 4.1673,
+    'thermal correction': 2.0739,
+    'ionization energy': 12.74,
+    'symbols': 'ClH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.07111 ],
+                  [ 0.      ,  0.      , -1.208868]]},
+'Li2': {
+    'description': "Dilithium (Li2), D*h symm.",
+    'name': "Li_2",
+    'database': 'G2-1',
+    'enthalpy': 51.6,
+    'ZPE': 0.4838,
+    'thermal correction': 2.3086,
+    'ionization energy': 5.11,
+    'symbols': 'LiLi',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.     ,  0.     ,  1.38653],
+                  [ 0.     ,  0.     , -1.38653]]},
+'LiF': {
+    'description': "Lithium Fluoride (LiF), C*v symm.",
+    'name': "LiF",
+    'database': 'G2-1',
+    'enthalpy': -80.1,
+    'ZPE': 1.4019,
+    'thermal correction': 2.0990,
+    'ionization energy': 11.30,
+    'symbols': 'LiF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.174965],
+                  [ 0.      ,  0.      ,  0.391655]]},
+'C2H2': {
+    'description': "Acetylene (C2H2), D*h symm.",
+    'name': "C_2H_2",
+    'database': 'G2-1',
+    'enthalpy': 54.2,
+    'ZPE': 16.6001,
+    'thermal correction': 2.4228,
+    'ionization energy': 11.40,
+    'vertical ionization energy': 11.49,
+    'symbols': 'CCHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.     ,  0.     ,  0.60808],
+                  [ 0.     ,  0.     , -0.60808],
+                  [ 0.     ,  0.     , -1.67399],
+                  [ 0.     ,  0.     ,  1.67399]]},
+'C2H4': {
+    'description': "Ethylene (H2C=CH2), D2h symm.",
+    'name': "C_2H_4",
+    'database': 'G2-1',
+    'enthalpy': 12.5,
+    'ZPE': 31.5267,
+    'thermal correction': 2.5100,
+    'ionization energy': 11.40,
+    'vertical ionization energy': 11.49,
+    'symbols': 'CCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.66748 ],
+                  [ 0.      ,  0.      , -0.66748 ],
+                  [ 0.      ,  0.922832,  1.237695],
+                  [ 0.      , -0.922832,  1.237695],
+                  [ 0.      ,  0.922832, -1.237695],
+                  [ 0.      , -0.922832, -1.237695]]},
+'C2H6': {
+    'description': "Ethane (H3C-CH3), D3d symm.",
+    'name': "C_2H_6",
+    'database': 'G2-1',
+    'enthalpy': -20.1,
+    'ZPE': 46.0950,
+    'thermal correction': 2.7912,
+    'symbols': 'CCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.762209],
+                  [ 0.      ,  0.      , -0.762209],
+                  [ 0.      ,  1.018957,  1.157229],
+                  [-0.882443, -0.509479,  1.157229],
+                  [ 0.882443, -0.509479,  1.157229],
+                  [ 0.      , -1.018957, -1.157229],
+                  [-0.882443,  0.509479, -1.157229],
+                  [ 0.882443,  0.509479, -1.157229]]},
+'CN': {
+    'description': "Cyano radical (CN), C*v symm, 2-Sigma+.",
+    'name': "CN",
+    'database': 'G2-1',
+    'enthalpy': 104.9,
+    'ZPE': 3.0183,
+    'thermal correction': 2.0739,
+    'ionization energy': 13.60,
+    'symbols': 'CN',
+    'magmoms': [ 1.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -0.611046],
+                  [ 0.      ,  0.      ,  0.523753]]},
+'HCN': {
+    'description': "Hydrogen cyanide (HCN), C*v symm.",
+    'name': "HCN",
+    'database': 'G2-1',
+    'enthalpy': 31.5,
+    'ZPE': 10.2654,
+    'thermal correction': 2.1768,
+    'ionization energy': 13.60,
+    'vertical ionization energy': 13.61,
+    'symbols': 'CNH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -0.511747],
+                  [ 0.      ,  0.      ,  0.664461],
+                  [ 0.      ,  0.      , -1.580746]]},
+'CO': {
+    'description': "Carbon monoxide (CO), C*v symm.",
+    'name': "CO",
+    'database': 'G2-1',
+    'enthalpy': -26.4,
+    'ZPE': 3.1062,
+    'thermal correction': 2.0739,
+    'ionization energy': 14.01,
+    'vertical ionization energy': 14.01,
+    'symbols': 'OC',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.493003],
+                  [ 0.      ,  0.      , -0.657337]]},
+'HCO': {
+    'description': "HCO radical, Bent Cs symm.",
+    'name': "HCO",
+    'database': 'G2-1',
+    'enthalpy': 10.0,
+    'ZPE': 8.0290,
+    'thermal correction': 2.3864,
+    'ionization energy': 8.12,
+    'vertical ionization energy': 9.31,
+    'symbols': 'COH',
+    'magmoms': [ 1.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.06256 ,  0.593926,  0.      ],
+                  [ 0.06256 , -0.596914,  0.      ],
+                  [-0.875835,  1.211755,  0.      ]]},
+'H2CO': {
+    'description': "Formaldehyde (H2C=O), C2v symm.",
+    'name': "H_2CO",
+    'database': 'G2-1',
+    'enthalpy': -26.0,
+    'ZPE': 16.4502,
+    'thermal correction': 2.3927,
+    'ionization energy': 10.88,
+    'vertical ionization energy': 10.88,
+    'symbols': 'OCHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.683501],
+                  [ 0.      ,  0.      , -0.536614],
+                  [ 0.      ,  0.93439 , -1.124164],
+                  [ 0.      , -0.93439 , -1.124164]]},
+'CH3OH': {
+    'description': "Methanol (CH3-OH), Cs symm.",
+    'name': "H_3COH",
+    'database': 'G2-1',
+    'enthalpy': -48.0,
+    'ZPE': 31.6635,
+    'thermal correction': 2.6832,
+    'ionization energy': 10.84,
+    'vertical ionization energy': 10.96,
+    'symbols': 'COHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.047131,  0.664389,  0.      ],
+                  [-0.047131, -0.758551,  0.      ],
+                  [-1.092995,  0.969785,  0.      ],
+                  [ 0.878534, -1.048458,  0.      ],
+                  [ 0.437145,  1.080376,  0.891772],
+                  [ 0.437145,  1.080376, -0.891772]]},
+'N2': {
+    'description': "N2 molecule, D*h symm.",
+    'name': "N_2",
+    'database': 'G2-1',
+    'enthalpy': 0.0,
+    'ZPE': 3.4243,
+    'thermal correction': 2.0733,
+    'ionization energy': 15.58,
+    'vertical ionization energy': 15.58,
+    'symbols': 'NN',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.     ,  0.     ,  0.56499],
+                  [ 0.     ,  0.     , -0.56499]]},
+'N2H4': {
+    'description': "Hydrazine (H2N-NH2), C2 symm.",
+    'name': "H_2NNH_2",
+    'database': 'G2-1',
+    'enthalpy': 22.8,
+    'ZPE': 32.9706,
+    'thermal correction': 2.6531,
+    'ionization energy': 8.10,
+    'vertical ionization energy': 8.98,
+    'symbols': 'NNHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.718959, -0.077687],
+                  [ 0.      , -0.718959, -0.077687],
+                  [ 0.211082,  1.092752,  0.847887],
+                  [-0.948214,  1.005026, -0.304078],
+                  [-0.211082, -1.092752,  0.847887],
+                  [ 0.948214, -1.005026, -0.304078]]},
+'NO': {
+    'description': "NO radical, C*v symm, 2-Pi.",
+    'name': "NO",
+    'database': 'G2-1',
+    'enthalpy': 21.6,
+    'ZPE': 2.7974,
+    'thermal correction': 2.0745,
+    'ionization energy': 9.26,
+    'vertical ionization energy': 9.26,
+    'symbols': 'NO',
+    'magmoms': [ 0.6,  0.4],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -0.609442],
+                  [ 0.      ,  0.      ,  0.533261]]},
+'O2': {
+    'description': "O2 molecule, D*h symm, Triplet.",
+    'name': "O_2",
+    'database': 'G2-1',
+    'enthalpy': 0.0,
+    'ZPE': 2.3444,
+    'thermal correction': 2.0752,
+    'ionization energy': 12.07,
+    'vertical ionization energy': 12.30,
+    'symbols': 'OO',
+    'magmoms': [ 1.,  1.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.622978],
+                  [ 0.      ,  0.      , -0.622978]]},
+'H2O2': {
+    'description': "Hydrogen peroxide (HO-OH), C2 symm.",
+    'name': "HOOH",
+    'database': 'G2-1',
+    'enthalpy': -32.5,
+    'ZPE': 16.4081,
+    'thermal correction': 2.6230,
+    'ionization energy': 10.58,
+    'vertical ionization energy': 11.70,
+    'symbols': 'OOHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.734058, -0.05275 ],
+                  [ 0.      , -0.734058, -0.05275 ],
+                  [ 0.839547,  0.880752,  0.422001],
+                  [-0.839547, -0.880752,  0.422001]]},
+'F2': {
+    'description': "F2 molecule, D*h symm.",
+    'name': "F_2",
+    'database': 'G2-1',
+    'enthalpy': 0.0,
+    'ZPE': 1.5179,
+    'thermal correction': 2.0915,
+    'ionization energy': 15.70,
+    'vertical ionization energy': 15.70,
+    'symbols': 'FF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.710304],
+                  [ 0.      ,  0.      , -0.710304]]},
+'CO2': {
+    'description': "Carbon dioxide (CO2), D*h symm.",
+    'name': "CO_2",
+    'database': 'G2-1',
+    'enthalpy': -94.1,
+    'ZPE': 7.3130,
+    'thermal correction': 2.2321,
+    'ionization energy': 13.78,
+    'vertical ionization energy': 13.78,
+    'symbols': 'COO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  0.      ,  1.178658],
+                  [ 0.      ,  0.      , -1.178658]]},
+'Na2': {
+    'description': "Disodium (Na2), D*h symm.",
+    'name': "Na_2",
+    'database': 'G2-1',
+    'enthalpy': 34.0,
+    'ZPE': 0.2246,
+    'thermal correction': 2.4699,
+    'ionization energy': 4.89,
+    'symbols': 'NaNa',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.576262],
+                  [ 0.      ,  0.      , -1.576262]]},
+'Si2': {
+    'description': "Si2 molecule, D*h symm, Triplet (3-Sigma-G-).",
+    'name': "Si_2",
+    'database': 'G2-1',
+    'enthalpy': 139.9,
+    'ZPE': 0.7028,
+    'thermal correction': 2.2182,
+    'ionization energy': 7.90,
+    'symbols': 'SiSi',
+    'magmoms': [ 1.,  1.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.130054],
+                  [ 0.      ,  0.      , -1.130054]]},
+'P2': {
+    'description': "P2 molecule, D*h symm.",
+    'name': "P_2",
+    'database': 'G2-1',
+    'enthalpy': 34.3,
+    'ZPE': 1.1358,
+    'thermal correction': 2.1235,
+    'ionization energy': 10.53,
+    'vertical ionization energy': 10.62,
+    'symbols': 'PP',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.966144],
+                  [ 0.      ,  0.      , -0.966144]]},
+'S2': {
+    'description': "S2 molecule, D*h symm, triplet.",
+    'name': "S_2",
+    'database': 'G2-1',
+    'enthalpy': 30.7,
+    'ZPE': 1.0078,
+    'thermal correction': 2.1436,
+    'ionization energy': 9.36,
+    'vertical ionization energy': 9.55,
+    'symbols': 'SS',
+    'magmoms': [ 1.,  1.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.960113],
+                  [ 0.      ,  0.      , -0.960113]]},
+'Cl2': {
+    'description': "Cl2 molecule, D*h symm.",
+    'name': "Cl_2",
+    'database': 'G2-1',
+    'enthalpy': 0.0,
+    'ZPE': 0.7737,
+    'thermal correction': 2.1963,
+    'ionization energy': 11.48,
+    'vertical ionization energy': 11.49,
+    'symbols': 'ClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.007541],
+                  [ 0.      ,  0.      , -1.007541]]},
+'NaCl': {
+    'description': "Sodium Chloride (NaCl), C*v symm.",
+    'name': "NaCl",
+    'database': 'G2-1',
+    'enthalpy': -43.6,
+    'ZPE': 0.5152,
+    'thermal correction': 2.2935,
+    'ionization energy': 9.20,
+    'vertical ionization energy': 9.80,
+    'symbols': 'NaCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.     ,  0.     , -1.45166],
+                  [ 0.     ,  0.     ,  0.93931]]},
+'SiO': {
+    'description': "Silicon monoxide (SiO), C*v symm.",
+    'name': "SiO",
+    'database': 'G2-1',
+    'enthalpy': -24.6,
+    'ZPE': 1.7859,
+    'thermal correction': 2.0821,
+    'ionization energy': 11.49,
+    'symbols': 'SiO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.560846],
+                  [ 0.      ,  0.      , -0.98148 ]]},
+'CS': {
+    'description': "Carbon monosulfide (CS), C*v symm.",
+    'name': "SC",
+    'database': 'G2-1',
+    'enthalpy': 66.9,
+    'ZPE': 1.8242,
+    'thermal correction': 2.0814,
+    'ionization energy': 11.33,
+    'symbols': 'CS',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.123382],
+                  [ 0.      ,  0.      ,  0.421268]]},
+'SO': {
+    'description': "Sulfur monoxide (SO), C*v symm, triplet.",
+    'name': "SO",
+    'database': 'G2-1',
+    'enthalpy': 1.2,
+    'ZPE': 1.6158,
+    'thermal correction': 2.0877,
+    'ionization energy': 11.29,
+    'symbols': 'OS',
+    'magmoms': [ 1.,  1.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.015992],
+                  [ 0.      ,  0.      ,  0.507996]]},
+'ClO': {
+    'description': "ClO radical, C*v symm, 2-PI.",
+    'name': "ClO",
+    'database': 'G2-1',
+    'enthalpy': 24.2,
+    'ZPE': 1.1923,
+    'thermal correction': 2.1172,
+    'ionization energy': 10.89,
+    'vertical ionization energy': 11.01,
+    'symbols': 'ClO',
+    'magmoms': [ 1.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.514172],
+                  [ 0.      ,  0.      , -1.092615]]},
+'ClF': {
+    'description': "ClF molecule, C*v symm, 1-SG.",
+    'name': "FCl",
+    'database': 'G2-1',
+    'enthalpy': -13.2,
+    'ZPE': 1.1113,
+    'thermal correction': 2.1273,
+    'ionization energy': 12.66,
+    'vertical ionization energy': 12.77,
+    'symbols': 'FCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.084794],
+                  [ 0.      ,  0.      ,  0.574302]]},
+'Si2H6': {
+    'description': "Disilane (H3Si-SiH3), D3d symm.",
+    'name': "Si_2H_6",
+    'database': 'G2-1',
+    'enthalpy': 19.1,
+    'ZPE': 30.2265,
+    'thermal correction': 3.7927,
+    'ionization energy': 9.74,
+    'vertical ionization energy': 10.53,
+    'symbols': 'SiSiHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.167683],
+                  [ 0.      ,  0.      , -1.167683],
+                  [ 0.      ,  1.393286,  1.68602 ],
+                  [-1.206621, -0.696643,  1.68602 ],
+                  [ 1.206621, -0.696643,  1.68602 ],
+                  [ 0.      , -1.393286, -1.68602 ],
+                  [-1.206621,  0.696643, -1.68602 ],
+                  [ 1.206621,  0.696643, -1.68602 ]]},
+'CH3Cl': {
+    'description': "Methyl chloride (CH3Cl), C3v symm.",
+    'name': "CH_3Cl",
+    'database': 'G2-1',
+    'enthalpy': -19.6,
+    'ZPE': 23.3013,
+    'thermal correction': 2.4956,
+    'symbols': 'CClHHH',
+    'ionization energy': 11.26,
+    'vertical ionization energy': 11.29,
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.121389],
+                  [ 0.      ,  0.      ,  0.655951],
+                  [ 0.      ,  1.029318, -1.47428 ],
+                  [ 0.891415, -0.514659, -1.47428 ],
+                  [-0.891415, -0.514659, -1.47428 ]]},
+'CH3SH': {
+    'description': "Methanethiol (H3C-SH), Staggered, Cs symm.",
+    'name': "H_3CSH",
+    'database': 'G2-1',
+    'enthalpy': -5.5,
+    'ZPE': 28.3973,
+    'thermal correction': 2.8690,
+    'ionization energy': 9.44,
+    'vertical ionization energy': 9.44,
+    'symbols': 'CSHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.047953,  1.149519,  0.      ],
+                  [-0.047953, -0.664856,  0.      ],
+                  [ 1.283076, -0.823249,  0.      ],
+                  [-1.092601,  1.461428,  0.      ],
+                  [ 0.432249,  1.551207,  0.892259],
+                  [ 0.432249,  1.551207, -0.892259]]},
+'HOCl': {
+    'description': "HOCl molecule, Cs symm.",
+    'name': "HOCl",
+    'database': 'G2-1',
+    'enthalpy': -17.8,
+    'ZPE': 8.1539,
+    'thermal correction': 2.4416,
+    'ionization energy': 11.12,
+    'symbols': 'OHCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.036702,  1.113517,  0.      ],
+                  [-0.917548,  1.328879,  0.      ],
+                  [ 0.036702, -0.602177,  0.      ]]},
+'SO2': {
+    'description': "Sulfur dioxide (SO2), C2v symm.",
+    'name': "SO_2",
+    'database': 'G2-1',
+    'enthalpy': -71.0,
+    'ZPE': 4.3242,
+    'thermal correction': 2.5245,
+    'ionization energy': 12.35,
+    'vertical ionization energy': 12.50,
+    'symbols': 'SOO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.370268],
+                  [ 0.      ,  1.277617, -0.370268],
+                  [ 0.      , -1.277617, -0.370268]]},
+}
+
+def get_ionization_energy(name, vertical=True):
+    """Return the experimental ionization energy from the database.
+
+    If vertical is True, the vertical ionization energy is returned if
+    available.
+    """
+    if name not in data:
+        raise KeyError('System %s not in database.' % name)
+    elif 'ionization energy' not in data[name]:
+        raise KeyError('No data on ionization energy for system %s.' % name)
+    else:
+        if vertical and 'vertical ionization energy' in data[name]:
+            return data[name]['vertical ionization energy']
+        else:
+            return data[name]['ionization energy']
+
+
+def get_atomization_energy(name):
+    """Determine extrapolated experimental atomization energy from the database.
+
+    The atomization energy is extrapolated from experimental heats of
+    formation at room temperature, using calculated zero-point energies
+    and thermal corrections.
+
+    The atomization energy is returned in kcal/mol = 43.36 meV:
+
+    >>> from ase.units import *; print kcal / mol
+    0.0433641146392
+
+    """
+    d = data[name]
+    e = d['enthalpy']
+    z = d['ZPE']
+    dh = d['thermal correction']
+    ae = -e + z + dh
+    for a in string2symbols(d['symbols']):
+        h = data[a]['enthalpy']
+        dh = data[a]['thermal correction']
+        ae += h - dh
+    return ae
diff --git a/ase/data/g2_1_ref.py b/ase/data/g2_1_ref.py
new file mode 100644
index 0000000..4ea6e08
--- /dev/null
+++ b/ase/data/g2_1_ref.py
@@ -0,0 +1,254 @@
+# atomization energies in kcal / mol (= 43.364 meV)
+# All values evaluated with PBE xc-orbitals and densities at
+# experimental geometries. Zero-point vibration has been removed
+# from experimental energies (from [1]).
+atomization = {
+# Molec   expt    LSD    PBE    RPBE   BLYP
+'H2'  : ( 109.5, 113.2, 104.6, 105.5, 109.4),
+'LiH' : (  57.8,  61.0,  53.5,  53.4,  58.1),
+'CH4' : ( 419.3, 462.3, 419.8, 410.6, 416.6),
+'NH3' : ( 297.4, 337.3, 301.7, 293.2, 301.4),
+'OH'  : ( 106.4, 124.1, 109.8, 106.3, 109.6),
+'H2O' : ( 232.2, 266.5, 234.2, 226.6, 232.5),
+'HF'  : ( 140.8, 162.2, 142.0, 137.5, 141.0),
+'Li2' : (  24.4,  23.9,  19.9,  20.2,  20.5),
+'LiF' : ( 138.9, 156.1, 138.6, 132.9, 140.1),
+'Be2' : (   3.0,  12.8,   9.8,   7.9,   6.1),
+'C2H2': ( 405.4, 460.3, 414.9, 400.4, 405.3),
+'C2H4': ( 562.6, 632.6, 571.5, 554.5, 560.7),
+'HCN' : ( 311.9, 361.0, 326.1, 313.6, 320.3),
+'CO'  : ( 259.3, 299.1, 268.8, 257.9, 261.8),
+'N2'  : ( 228.5, 267.4, 243.2, 232.7, 239.8),
+'NO'  : ( 152.9, 198.7, 171.9, 161.6, 166.0),
+'O2'  : ( 120.5, 175.0, 143.7, 133.3, 135.3),
+'F2'  : (  38.5,  78.2,  53.4,  45.6,  49.4),
+'P2'  : ( 117.3, 143.8, 121.1, 114.1, 121.0),
+'Cl2' : (  58.0,  83.0,  65.1,  58.9,  57.2)
+}
+
+# exchange-only atomization energies in kcal / mol (= 43.364 meV)
+# All values evaluated with PBE xc-orbitals and densities at
+# experimental geometries. (from [1]).
+ex_atomization = {
+# Molec   exact   LSD    PBE    RPBE   BLYP
+'H2'  : (  84.0,  81.5,  84.8,  85.8,  85.4),
+'LiH' : (  33.9,  33.6,  36.9,  36.8,  36.2),
+'CH4' : ( 327.2, 369.9, 336.0, 326.9, 331.2),
+'NH3' : ( 199.5, 255.0, 227.4, 218.9, 222.6),
+'OH'  : (  67.3,  96.2,  84.5,  80.9,  82.7),
+'H2O' : ( 154.6, 212.9, 183.9, 176.4, 180.5),
+'HF'  : (  96.1, 136.1, 117.1, 112.6, 115.4),
+'Li2' : (   3.5,   6.5,   6.4,   6.7,   3.9),
+'LiF' : (  86.8, 129.7, 116.5, 110.8, 113.6),
+'Be2' : ( -11.0,   9.4,   3.1,   1.2,   1.6),
+'C2H2': ( 290.6, 382.7, 333.0, 318.5, 325.5),
+'C2H4': ( 423.9, 517.7, 456.5, 439.6, 447.4),
+'HCN' : ( 194.5, 294.0, 256.1, 243.5, 249.1),
+'CO'  : ( 169.2, 261.9, 224.0, 213.1, 218.7),
+'N2'  : ( 110.2, 211.4, 184.1, 173.6, 177.6),
+'NO'  : (  45.6, 156.9, 122.8, 112.5, 117.0),
+'O2'  : (  24.9, 147.5, 104.4,  94.1,  99.3),
+'F2'  : ( -43.3,  64.0,  32.5,  24.7,  28.8),
+'P2'  : (  31.8,  98.4,  73.1,  66.1,  70.1),
+'Cl2' : (  15.5,  68.2,  39.8,  33.7,  37.0)
+}
+
+# Exchange energy of some spherical atoms in Hartrees (= 27.211 eV)
+# All functionals were evaluated with self-consistent exchange-only
+# OEP orbitals and densities (from [1]).
+ex_energy = {
+# Atom       exact     LSD       PBE       RPBE      BLYP
+'H'   : (   0.3125,   0.2680,   0.3059,   0.3112,   0.3098),
+'He'  : (   1.0258,   0.8840,   1.0136,   1.0313,   1.0255),
+'Li'  : (   1.7807,   1.5379,   1.7572,   1.7876,   1.7753),
+'Be'  : (   2.6658,   2.3124,   2.6358,   2.6801,   2.6578),
+'N'   : (   6.6044,   5.9008,   6.5521,   6.6252,   6.5961),
+'Ne'  : (  12.1050,  11.0335,  12.0667,  12.1593,  12.1378),
+'Na'  : (  14.0131,  12.7859,  13.9506,  14.0528,  14.0304),
+'Mg'  : (  15.9884,  14.6117,  15.9147,  16.0260,  16.0005),
+'P'   : (  22.6341,  20.7931,  22.5028,  22.6369,  22.6221),
+'Ar'  : (  30.1747,  27.8632,  29.9961,  30.1494,  30.1535),
+'Kr'  : (  93.8330,  88.6245,  93.4257,  93.6645,  93.8721),
+'Xe'  : ( 179.0635, 170.5660, 178.2450, 178.5649, 179.0427)
+}
+
+# Correlation energy of some spherical atoms in Hartrees (= 27.211 eV)
+# All functionals were evaluated with self-consistent exchange-only
+# OEP orbitals and densities (from [1]).
+# 'Exact' values are from reference [2]
+ec_energy = {
+# Atom     exact   LSD     PBE     BLYP
+'H'   : ( 0.0000, 0.0222, 0.0060, 0.0000),
+'He'  : ( 0.0420, 0.1125, 0.0420, 0.0438),
+'Li'  : ( 0.0455, 0.1508, 0.0514, 0.0534),
+'Be'  : ( 0.0950, 0.2240, 0.0856, 0.0945),
+'N'   : ( 0.1858, 0.4268, 0.1799, 0.1919),
+'Ne'  : ( 0.3929, 0.7428, 0.3513, 0.3835),
+'Na'  : ( 0.3988, 0.8010, 0.3715, 0.4083),
+'Mg'  : ( 0.4424, 0.8874, 0.4110, 0.4594),
+'P'   : ( 0.5446, 1.1127, 0.5265, 0.5664),
+'Ar'  : ( 0.7314, 1.4242, 0.7067, 0.7508),
+'Kr'  : ( 3.2693, 1.7672, 1.7486, 2.0788),
+'Xe'  : ( 5.1773, 2.9184, 2.7440, 3.1789)
+}
+
+# atomization energies in kcal / mol (= 43.364 meV).
+# All values evaluated with self-consistent orbitals and densities.
+# Geometry optimization to within 10meV/Ang.
+# Data from reference [3].
+atomization_vasp = {
+# Molecule        Expt. PBE_VASP PBE_G03 PBE0_VASP PBE0_G03
+'LiH'       : (     58,   53.5,   53.5,   52.6,   52.9,),
+'BeH'       : (     48,   55.5,   55.6,   55.4,   56.0,),
+'CH'        : (     84,   84.7,   84.8,   83.3,   83.0,),
+'CH2_s3B1d' : (    189,  194.4,  194.6,  193.9,  193.8,),
+'CH2_s1A1d' : (    182,  178.8,  179.1,  176.3,  176.5,),
+'CH3'       : (    306,  309.7,  310.1,  308.3,  308.6,),
+'CH4'       : (    420,  419.6,  420.2,  417.2,  417.9,),
+'NH'        : (     82,   88.6,   88.6,   85.2,   85.3,),
+'NH2'       : (    182,  188.7,  188.9,  183.4,  183.3,),
+'NH3'       : (    297,  301.7,  302.3,  294.7,  295.3,),
+'OH'        : (    107,  109.7,  110.1,  105.4,  105.8,),
+'H2O'       : (    233,  233.7,  234.5,  226.4,  227.3,),
+'HF'        : (    142,  141.5,  142.2,  136.2,  137.0,),
+'SiH2_s1A1d': (    154,  147.9,  148.0,  147.2,  147.4,),
+'SiH2_s3B1d': (    131,  131.3,  131.8,  132.2,  132.5,),
+'SiH3'      : (    226,  222.2,  222.6,  223.5,  223.8,),
+'SiH4'      : (    324,  313.3,  313.7,  315.0,  315.7,),
+'PH2'       : (    153,  154.5,  154.6,  153.0,  153.2,),
+'PH3'       : (    241,  239.0,  239.3,  237.3,  237.5,),
+'SH2'       : (    182,  182.0,  182.2,  179.8,  180.0,),
+'HCl'       : (    107,  106.3,  106.5,  104.4,  105.0,),
+'Li2'       : (     26,   19.9,   20.1,   19.3,   19.3,),
+'LiF'       : (    139,  138.4,  139.0,  131.0,  131.9,),
+'C2H2'      : (    404,  414.5,  415.1,  404.5,  404.7,),
+'C2H4'      : (    562,  571.0,  571.9,  563.8,  564.2,),
+'C2H6'      : (    711,  716.0,  717.1,  711.6,  712.4,),
+'CN'        : (    179,  197.5,  197.7,  179.1,  179.1,),
+'HCN'       : (    313,  326.3,  326.5,  311.1,  311.5,),
+'CO'        : (    261,  268.6,  269.1,  255.3,  255.8,),
+'HCO'       : (    279,  294.9,  295.5,  280.5,  280.9,),
+'H2CO'      : (    376,  385.5,  386.3,  371.9,  372.8,),
+'CH3OH'     : (    513,  519.3,  520.4,  509.0,  510.3,),
+'N2'        : (    227,  243.7,  243.9,  225.3,  225.9,),
+'N2H4'      : (    437,  452.7,  453.7,  437.9,  438.8,),
+'NO'        : (    153,  172.0,  172.5,  153.3,  153.8,),
+'O2'        : (    118,  143.3,  144.0,  124.1,  124.9,),
+'H2O2'      : (    268,  281.6,  282.6,  262.7,  263.8,),
+'F2'        : (     38,   52.6,   53.0,   35.2,   35.3,),
+'CO2'       : (    392,  415.4,  416.5,  390.8,  392.0,),
+'Na2'       : (     19,   17.7,   18.1,   15.6,   15.9,),
+'Si2'       : (     74,   81.3,   81.4,   76.5,   77.3,),
+'P2'        : (    116,  121.5,  121.7,  111.8,  111.7,),
+'S2'        : (     98,  115.4,  115.2,  107.3,  107.0,),
+'Cl2'       : (     57,   65.8,   65.8,   60.1,   59.9,),
+'NaCl'      : (     99,   93.6,   94.5,   92.1,   93.6,),
+'SiO'       : (    191,  195.6,  196.6,  182.2,  183.3,),
+'CS'        : (    172,  179.5,  179.6,  168.0,  168.2,),
+'SO'        : (    122,  141.5,  141.3,  127.9,  127.3,),
+'ClO'       : (     62,   81.6,   81.5,   67.4,   67.6,),
+'ClF'       : (     62,   72.3,   72.5,   61.3,   61.3,),
+'Si2H6'     : (    533,  519.5,  520.4,  522.2,  523.3,),
+'CH3Cl'     : (    395,  399.4,  400.2,  395.0,  395.7,),
+'CH3SH'     : (    473,  477.8,  478.6,  472.7,  473.5,),
+'HOCl'      : (    165,  175.2,  175.7,  162.9,  163.3,),
+'SO2'       : (    253,  281.1,  280.7,  254.1,  253.5,),
+}
+
+# Experimental, and calculated bindinglengths of 16 diatomic molecules
+# of the G2-1 test set. In Angstroms.
+# Data from reference [3].
+diatomic = {
+#System Expt. PBEVASP PBEG03 PBE0VASP PBE0G03
+'BeH': (1.343, 1.354, 1.353, 1.350, 1.348),
+'CH' : (1.120, 1.136, 1.136, 1.124, 1.124),
+'Cl2': (1.988, 1.999, 2.004, 1.973, 1.978),
+'ClF': (1.628, 1.648, 1.650, 1.614, 1.617),
+'ClO': (1.570, 1.576, 1.577, 1.554, 1.555),
+'CN' : (1.172, 1.173, 1.174, 1.159, 1.159),
+'CO' : (1.128, 1.136, 1.135, 1.122, 1.122),
+'F2' : (1.412, 1.414, 1.413, 1.377, 1.376),
+'FH' : (0.917, 0.932, 0.930, 0.919, 0.918),
+'HCl': (1.275, 1.287, 1.288, 1.276, 1.278),
+'Li2': (2.673, 2.728, 2.728, 2.727, 2.727),
+'LiF': (1.564, 1.583, 1.575, 1.571, 1.561),
+'LiH': (1.595, 1.604, 1.604, 1.602, 1.597),
+'N2' : (1.098, 1.103, 1.102, 1.089, 1.089),
+'O2' : (1.208, 1.218, 1.218, 1.193, 1.192),
+'Na2': (3.079, 3.087, 3.076, 3.086, 3.086),
+}
+
+## Note the difference between the experimental data from Blaha [1] and
+## from Kresse [3]:
+## Mol  BLAHA KRESSE
+## LiH :  57.8  58.0
+## CH4 : 419.3 420.0
+## NH3 : 297.4 297.0
+## OH  : 106.4 107.0
+## H2O : 232.2 233.0
+## HF  : 140.8 142.0
+## Li2 :  24.4  26.0
+## LiF : 138.9 139.0
+## C2H2: 405.4 404.0
+## C2H4: 562.6 562.0
+## HCN : 311.9 313.0
+## CO  : 259.3 261.0
+## N2  : 228.5 227.0
+## NO  : 152.9 153.0
+## O2  : 120.5 118.0
+## F2  :  38.5  38.0
+## P2  : 117.3 116.0
+## Cl2 :  58.0  57.0
+## -----------------
+## MAE :   0.0   1.0
+
+## and the difference between the PBE results of Blaha [1] and those from
+## VASP and GAUSSIAN-03 [3]
+## Mol  BLAHA  VASP   G03
+## LiH :  53.5  53.5  53.5
+## CH4 : 419.8 419.6 420.2
+## NH3 : 301.7 301.7 302.3
+## OH  : 109.8 109.7 110.1
+## H2O : 234.2 233.7 234.5
+## HF  : 142.0 141.5 142.2
+## Li2 :  19.9  19.9  20.1
+## LiF : 138.6 138.4 139.0
+## C2H2: 414.9 414.5 415.1
+## C2H4: 571.5 571.0 571.9
+## HCN : 326.1 326.3 326.5
+## CO  : 268.8 268.6 269.1
+## N2  : 243.2 243.7 243.9
+## NO  : 171.9 172.0 172.5
+## O2  : 143.7 143.3 144.0
+## F2  :  53.4  52.6  53.0
+## P2  : 121.1 121.5 121.7
+## Cl2 :  65.1  65.8  65.8
+## -----------------------
+## MAE :   0.0   0.3   0.4
+##
+## Where in the last two, geometry optimization has been performed, but not in
+## the first.
+
+# References:
+# [1]:
+# Kurth, Perdew, and Blaha
+# Molecular and Solid-State Tests of Density Functional Approximations
+# International Journal of Quantum Chemistry, Vol. 85, 889-909 (1999)
+# [2]:
+# Krieger, Chen, Iafrate, and Savin
+# In Electron Correlations and Materials Properties
+# Gonis and Kioussis; eds.
+# Plenum: New York, 1999.
+# [3]:
+# Paier, Hirschl, Marsman, and Kresse
+# The Perdew-Burke-Ernzerhof exchange-correlation functional applied to the
+# G2-1 test set using a plane wave basis set
+# The Journal of Chemical Physics, Vol 122, 234102 (2005)
+
+def convert(input, column):
+    keys = input.keys()
+    keys.sort()
+    data = {}
+    for k in keys:
+        data[k] = input[k][column]
+    return data
diff --git a/ase/data/g2_1_ref_g03.py b/ase/data/g2_1_ref_g03.py
new file mode 100644
index 0000000..7f149f6
--- /dev/null
+++ b/ase/data/g2_1_ref_g03.py
@@ -0,0 +1,17 @@
+from ase.data.g2_1_ref import convert
+from ase.data.g2_1_ref import atomization_vasp
+from ase.data.g2_1_ref import diatomic
+
+info = {}
+
+info['atomization energy'] = {}
+
+info['atomization energy'].update({'reference': convert(atomization_vasp, 0)})
+info['atomization energy'].update({'PBE': convert(atomization_vasp, 2)})
+info['atomization energy'].update({'PBE0': convert(atomization_vasp, 4)})
+
+info['bondlength'] = {}
+
+info['bondlength'].update({'reference': convert(diatomic, 0)})
+info['bondlength'].update({'PBE': convert(diatomic, 2)})
+info['bondlength'].update({'PBE0': convert(diatomic, 4)})
diff --git a/ase/data/g2_1_ref_vasp.py b/ase/data/g2_1_ref_vasp.py
new file mode 100644
index 0000000..5f52c1a
--- /dev/null
+++ b/ase/data/g2_1_ref_vasp.py
@@ -0,0 +1,17 @@
+from ase.data.g2_1_ref import convert
+from ase.data.g2_1_ref import atomization_vasp
+from ase.data.g2_1_ref import diatomic
+
+info = {}
+
+info['atomization energy'] = {}
+
+info['atomization energy'].update({'reference': convert(atomization_vasp, 0)})
+info['atomization energy'].update({'PBE': convert(atomization_vasp, 1)})
+info['atomization energy'].update({'PBE0': convert(atomization_vasp, 3)})
+
+info['bondlength'] = {}
+
+info['bondlength'].update({'reference': convert(diatomic, 0)})
+info['bondlength'].update({'PBE': convert(diatomic, 1)})
+info['bondlength'].update({'PBE0': convert(diatomic, 3)})
diff --git a/ase/data/g2_1_ref_wien97.py b/ase/data/g2_1_ref_wien97.py
new file mode 100644
index 0000000..fe74dc0
--- /dev/null
+++ b/ase/data/g2_1_ref_wien97.py
@@ -0,0 +1,12 @@
+from ase.data.g2_1_ref import convert
+from ase.data.g2_1_ref import atomization
+
+info = {}
+
+info['atomization energy'] = {}
+
+info['atomization energy'].update({'reference': convert(atomization, 0)})
+info['atomization energy'].update({'LSD': convert(atomization, 1)})
+info['atomization energy'].update({'PBE': convert(atomization, 2)})
+info['atomization energy'].update({'RPBE': convert(atomization, 3)})
+info['atomization energy'].update({'BLYP': convert(atomization, 4)})
diff --git a/ase/data/g2_2.py b/ase/data/g2_2.py
new file mode 100644
index 0000000..f62a0f8
--- /dev/null
+++ b/ase/data/g2_2.py
@@ -0,0 +1,1686 @@
+"""
+The following contains a database of small molecules
+
+Data for the G2/97 database are from
+Raghavachari, Redfern, and Pople, J. Chem. Phys. Vol. 106, 1063 (1997).
+See http://www.cse.anl.gov/Catalysis_and_Energy_Conversion/Computational_Thermochemistry.shtml for the original files.
+
+All numbers are experimental values, except for coordinates, which are
+MP2(full)/6-31G(d) optimized geometries (from http://www.cse.anl.gov/OldCHMwebsiteContent/compmat/G2-97.htm)
+
+Atomic species:
+ref: Curtiss et al. JCP 106, 1063 (1997).
+'Enthalpy' is the experimental enthalpies of formation at 0K
+'thermal correction' is the thermal corrections H(298)-H(0)
+
+Molecular species:
+ref: Staroverov et al. JCP 119, 12129 (2003)
+'Enthalpy' is the experimental enthalpies of formation at 298K
+'ZPE' is the zero-point energies
+'thermal correction' is the thermal enthalpy corrections H(298K) - H_exp(0K)
+ZPE and thermal corrections are estimated from B3LYP geometries and vibrations.
+
+For details about G2-1 and G2-2 sets see doi:10.1063/1.477422.
+
+Experimental ionization potentials are from http://srdata.nist.gov/cccbdb/
+Information presented on these pages is considered public information
+and may be distributed or copied http://www.nist.gov/public_affairs/disclaimer.cfm
+"""
+
+from ase.atoms import Atoms, string2symbols
+
+atom_names = ['H','B','C','N','O','F','Al','Si','S','Cl']
+
+molecule_names = ['BF3','BCl3','AlF3','AlCl3','CF4','CCl4','OCS','CS2','COF2','SiF4','SiCl4','N2O','ClNO','NF3','PF3','O3','F2O','ClF3','C2F4','C2Cl4','CF3CN','C3H4_C3v','C3H4_D2d','C3H4_C2v','C3H6_Cs','C3H6_D3h','C3H8','butadiene','2-butyne','methylenecyclopropane','bicyclobutane','cyclobutene','cyclobutane','isobutene','trans-butane','isobutane','C5H8','C6H6','H2CF2','HCF3','H2CCl2','HCCl3','H3CNH2','CH3CN','CH3NO2','CH3ONO','CH3SiH3','HCOOH','HCOOCH3','CH3CONH2','CH2NHCH2','NCCN','C2H6NH','CH3CH2NH2','H2CCO','CH2OCH2','CH3CHO','OCHCHO','CH3CH2OH','CH3OCH3','CH2SCH2','C2H6SO','CH3CH2SH','CH3SCH3','H2CCHF','CH3CH2Cl','H2CCHCl','H2CCHCN','CH3COCH3','CH3COOH','CH3COF','CH3COCl','C3H7Cl','C2H6CHOH','CH3CH2OCH3','C3H9N','C4H4O','C4H4S','C4H4NH','C5H5N','H2','SH','CCH','C2H3','CH3CO','H2COH','CH3O','CH3CH2O','CH3S','C2H5','C3H7','C3H9C','NO2']
+
+data = {
+'B': {
+    'name': 'Boron',
+    'database': 'G2-2',
+    'symbols': 'B',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 136.20,
+    'thermal correction': 0.29,
+    'ionization energy': 8.30,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'Al': {
+    'name': 'Aluminium',
+    'database': 'G2-2',
+    'symbols': 'Al',
+    'magmoms': [1.],
+    'charges': None,
+    'enthalpy': 78.23,
+    'thermal correction': 1.08,
+    'ionization energy': 5.99,
+    'positions': [[ 0.  ,  0.  ,  0.]],
+    },
+'BF3': {
+    'description': "BF3, Planar D3h symm.",
+    'name': "BF_3",
+    'database': 'G2-2',
+    'enthalpy': -271.4,
+    'ZPE': 7.8257,
+    'thermal correction': 2.7893,
+    'symbols': 'BFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  1.32176 ,  0.      ],
+                  [ 1.144678, -0.66088 ,  0.      ],
+                  [-1.144678, -0.66088 ,  0.      ]]},
+'BCl3': {
+    'description': "BCl3, Planar D3h symm.",
+    'name': "BCl_3",
+    'database': 'G2-2',
+    'enthalpy': -96.3,
+    'ZPE': 4.6536,
+    'thermal correction': 3.3729,
+    'symbols': 'BClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  1.735352,  0.      ],
+                  [ 1.502859, -0.867676,  0.      ],
+                  [-1.502859, -0.867676,  0.      ]]},
+'AlF3': {
+    'description': "AlF3, Planar D3h symm.",
+    'name': "AlF_3",
+    'database': 'G2-2',
+    'enthalpy': -289.0,
+    'ZPE': 4.8645,
+    'thermal correction': 3.3986,
+    'symbols': 'AlFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  1.64472 ,  0.      ],
+                  [ 1.424369, -0.82236 ,  0.      ],
+                  [-1.424369, -0.82236 ,  0.      ]]},
+'AlCl3': {
+    'description': "AlCl3, Planar D3h symm.",
+    'name': "AlCl_3",
+    'database': 'G2-2',
+    'enthalpy': -139.7,
+    'ZPE': 2.9687,
+    'thermal correction': 3.9464,
+    'symbols': 'AlClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  2.069041,  0.      ],
+                  [ 1.791842, -1.03452 ,  0.      ],
+                  [-1.791842, -1.03452 ,  0.      ]]},
+'CF4': {
+    'description': "CF4, Td symm.",
+    'name': "CF_4",
+    'database': 'G2-2',
+    'enthalpy': -223.0,
+    'ZPE': 10.5999,
+    'thermal correction': 3.0717,
+    'symbols': 'CFFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.767436,  0.767436,  0.767436],
+                  [-0.767436, -0.767436,  0.767436],
+                  [-0.767436,  0.767436, -0.767436],
+                  [ 0.767436, -0.767436, -0.767436]]},
+'CCl4': {
+    'description': "CCl4, Td symm.",
+    'name': "CCl_4",
+    'database': 'G2-2',
+    'enthalpy': -22.9,
+    'ZPE': 5.7455,
+    'thermal correction': 4.1754,
+    'symbols': 'CClClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.     ,  0.     ,  0.     ],
+                  [ 1.02134,  1.02134,  1.02134],
+                  [-1.02134, -1.02134,  1.02134],
+                  [-1.02134,  1.02134, -1.02134],
+                  [ 1.02134, -1.02134, -1.02134]]},
+'OCS': {
+    'description': "O=C=S, Linear, C*v symm.",
+    'name': "COS",
+    'database': 'G2-2',
+    'enthalpy': -33.1,
+    'ZPE': 5.7706,
+    'thermal correction': 2.3663,
+    'symbols': 'OCS',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.699243],
+                  [ 0.      ,  0.      , -0.520492],
+                  [ 0.      ,  0.      ,  1.044806]]},
+'CS2': {
+    'description': "CS2, Linear, D*h symm.",
+    'name': "CS_2",
+    'database': 'G2-2',
+    'enthalpy': 28.0,
+    'ZPE': 4.3380,
+    'thermal correction': 2.5326,
+    'symbols': 'SCS',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.561117],
+                  [ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  0.      , -1.561117]]},
+'COF2': {
+    'description': "COF2, C2v symm.",
+    'name': "COF_2",
+    'database': 'G2-2',
+    'enthalpy': -149.1,
+    'ZPE': 8.8215,
+    'thermal correction': 2.6619,
+    'symbols': 'OCFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.330715],
+                  [ 0.      ,  0.      ,  0.144358],
+                  [ 0.      ,  1.06949 , -0.639548],
+                  [ 0.      , -1.06949 , -0.639548]]},
+'SiF4': {
+    'description': "SiF4, Td symm.",
+    'name': "SiF_4",
+    'database': 'G2-2',
+    'enthalpy': -386.0,
+    'ZPE': 7.8771,
+    'thermal correction': 3.7054,
+    'symbols': 'SiFFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.912806,  0.912806,  0.912806],
+                  [-0.912806, -0.912806,  0.912806],
+                  [-0.912806,  0.912806, -0.912806],
+                  [ 0.912806, -0.912806, -0.912806]]},
+'SiCl4': {
+    'description': "SiCl4, Td symm.",
+    'name': "SiCl_4",
+    'database': 'G2-2',
+    'enthalpy': -158.4,
+    'ZPE': 4.4396,
+    'thermal correction': 4.7182,
+    'symbols': 'SiClClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 1.169349,  1.169349,  1.169349],
+                  [-1.169349, -1.169349,  1.169349],
+                  [ 1.169349, -1.169349, -1.169349],
+                  [-1.169349,  1.169349, -1.169349]]},
+'N2O': {
+    'description': "N2O, Cs symm.",
+    'name': "N_2O",
+    'database': 'G2-2',
+    'enthalpy': 19.6,
+    'ZPE': 6.9748,
+    'thermal correction': 2.2710,
+    'symbols': 'NNO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.231969],
+                  [ 0.      ,  0.      , -0.060851],
+                  [ 0.      ,  0.      ,  1.131218]]},
+'ClNO': {
+    'description': "ClNO, Cs symm.",
+    'name': "ClNO",
+    'database': 'G2-2',
+    'enthalpy': 12.4,
+    'ZPE': 4.0619,
+    'thermal correction': 2.7039,
+    'symbols': 'ClNO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.537724, -0.961291,  0.      ],
+                  [ 0.      ,  0.997037,  0.      ],
+                  [ 1.142664,  1.170335,  0.      ]]},
+'NF3': {
+    'description': "NF3, C3v symm.",
+    'name': "NF_3",
+    'database': 'G2-2',
+    'enthalpy': -31.6,
+    'ZPE': 6.4477,
+    'thermal correction': 2.8301,
+    'symbols': 'NFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.489672],
+                  [ 0.      ,  1.238218, -0.126952],
+                  [ 1.072328, -0.619109, -0.126952],
+                  [-1.072328, -0.619109, -0.126952]]},
+'PF3': {
+    'description': "PF3, C3v symm.",
+    'name': "PF_3",
+    'database': 'G2-2',
+    'enthalpy': -229.1,
+    'ZPE': 5.2981,
+    'thermal correction': 3.1288,
+    'symbols': 'PFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.506767],
+                  [ 0.      ,  1.383861, -0.281537],
+                  [ 1.198459, -0.691931, -0.281537],
+                  [-1.198459, -0.691931, -0.281537]]},
+'O3': {
+    'description': "O3 (Ozone), C2v symm.",
+    'name': "O_3",
+    'database': 'G2-2',
+    'enthalpy': 34.1,
+    'ZPE': 4.6178,
+    'thermal correction': 2.4479,
+    'symbols': 'OOO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.10381 , -0.228542],
+                  [ 0.      ,  0.      ,  0.457084],
+                  [ 0.      , -1.10381 , -0.228542]]},
+'F2O': {
+    'description': "F2O, C2v symm.",
+    'name': "F_2O",
+    'database': 'G2-2',
+    'enthalpy': 5.9,
+    'ZPE': 3.4362,
+    'thermal correction': 2.5747,
+    'symbols': 'FOF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.110576, -0.273729],
+                  [ 0.      ,  0.      ,  0.61589 ],
+                  [ 0.      , -1.110576, -0.273729]]},
+'ClF3': {
+    'description': "ClF3, C2v symm.",
+    'name': "ClF_3",
+    'database': 'G2-2',
+    'enthalpy': -38.0,
+    'ZPE': 4.2922,
+    'thermal correction': 3.3289,
+    'symbols': 'ClFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.376796],
+                  [ 0.      ,  0.      , -1.258346],
+                  [ 0.      ,  1.714544,  0.27331 ],
+                  [ 0.      , -1.714544,  0.27331 ]]},
+'C2F4': {
+    'description': "C2F4 (F2C=CF2), D2H symm.",
+    'name': "C_2F_4",
+    'database': 'G2-2',
+    'enthalpy': -157.4,
+    'ZPE': 13.4118,
+    'thermal correction': 3.9037,
+    'symbols': 'CCFFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.66323 ],
+                  [ 0.      ,  0.      , -0.66323 ],
+                  [ 0.      ,  1.112665,  1.385652],
+                  [ 0.      , -1.112665,  1.385652],
+                  [ 0.      ,  1.112665, -1.385652],
+                  [ 0.      , -1.112665, -1.385652]]},
+'C2Cl4': {
+    'description': "C2Cl4 (Cl2C=CCl2), D2h symm.",
+    'name': "C_2Cl_4",
+    'database': 'G2-2',
+    'enthalpy': -3.0,
+    'ZPE': 9.4628,
+    'thermal correction': 4.7132,
+    'symbols': 'CCClClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.675402],
+                  [ 0.      ,  0.      , -0.675402],
+                  [ 0.      ,  1.448939,  1.589701],
+                  [ 0.      , -1.448939,  1.589701],
+                  [ 0.      , -1.448939, -1.589701],
+                  [ 0.      ,  1.448939, -1.589701]]},
+'CF3CN': {
+    'description': "CF3CN, C3v symm.",
+    'name': "CF_3CN",
+    'database': 'G2-2',
+    'enthalpy': -118.4,
+    'ZPE': 14.1020,
+    'thermal correction': 3.7996,
+    'symbols': 'CCFFFN',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -0.32635 ],
+                  [ 0.      ,  0.      ,  1.15083 ],
+                  [ 0.      ,  1.257579, -0.787225],
+                  [ 1.089096, -0.62879 , -0.787225],
+                  [-1.089096, -0.62879 , -0.787225],
+                  [ 0.      ,  0.      ,  2.329741]]},
+'C3H4_C3v': {
+    'description': "Propyne (C3H4), C3v symm.",
+    'name': "CH_3CCH (propyne)",
+    'database': 'G2-2',
+    'enthalpy': 44.2,
+    'ZPE': 34.2614,
+    'thermal correction': 3.1193,
+    'symbols': 'CCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.214947],
+                  [ 0.      ,  0.      ,  1.43313 ],
+                  [ 0.      ,  0.      , -1.246476],
+                  [ 0.      ,  0.      ,  2.498887],
+                  [ 0.      ,  1.021145, -1.636167],
+                  [ 0.884337, -0.510572, -1.636167],
+                  [-0.884337, -0.510572, -1.636167]]},
+'C3H4_D2d': {
+    'description': "Allene (C3H4), D2d symm.",
+    'name': "CH_2=C=CH_2 (allene)",
+    'database': 'G2-2',
+    'enthalpy': 45.5,
+    'ZPE': 34.1189,
+    'thermal correction': 2.9744,
+    'symbols': 'CCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  0.      ,  1.31119 ],
+                  [ 0.      ,  0.      , -1.31119 ],
+                  [ 0.      ,  0.926778,  1.876642],
+                  [ 0.      , -0.926778,  1.876642],
+                  [ 0.926778,  0.      , -1.876642],
+                  [-0.926778,  0.      , -1.876642]]},
+'C3H4_C2v': {
+    'description': "Cyclopropene (C3H4), C2v symm.",
+    'name': "C_3H_4 (cyclopropene)",
+    'database': 'G2-2',
+    'enthalpy': 66.2,
+    'ZPE': 34.7603,
+    'thermal correction': 2.6763,
+    'symbols': 'CCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.858299],
+                  [ 0.      , -0.650545, -0.498802],
+                  [ 0.      ,  0.650545, -0.498802],
+                  [ 0.912438,  0.      ,  1.456387],
+                  [-0.912438,  0.      ,  1.456387],
+                  [ 0.      , -1.584098, -1.038469],
+                  [ 0.      ,  1.584098, -1.038469]]},
+'C3H6_Cs': {
+    'description': "Propene (C3H6), Cs symm.",
+    'name': "CH_3CH=CH_2 (propylene)",
+    'database': 'G2-2',
+    'enthalpy': 4.8,
+    'ZPE': 49.1836,
+    'thermal correction': 3.1727,
+    'symbols': 'CCHHHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 1.29129 ,  0.133682,  0.      ],
+                  [ 0.      ,  0.479159,  0.      ],
+                  [ 1.60116 , -0.90742 ,  0.      ],
+                  [ 2.0808  ,  0.877337,  0.      ],
+                  [-0.263221,  1.536098,  0.      ],
+                  [-1.139757, -0.492341,  0.      ],
+                  [-0.776859, -1.523291,  0.      ],
+                  [-1.77554 , -0.352861,  0.88042 ],
+                  [-1.77554 , -0.352861, -0.88042 ]]},
+'C3H6_D3h': {
+    'description': "Cyclopropane (C3H6), D3h symm.",
+    'name': "C_3H_6 (cyclopropane)",
+    'database': 'G2-2',
+    'enthalpy': 12.7,
+    'ZPE': 50.2121,
+    'thermal correction': 2.7272,
+    'symbols': 'CCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.866998,  0.      ],
+                  [ 0.750842, -0.433499,  0.      ],
+                  [-0.750842, -0.433499,  0.      ],
+                  [ 0.      ,  1.455762,  0.910526],
+                  [ 0.      ,  1.455762, -0.910526],
+                  [ 1.260727, -0.727881, -0.910526],
+                  [ 1.260727, -0.727881,  0.910526],
+                  [-1.260727, -0.727881,  0.910526],
+                  [-1.260727, -0.727881, -0.910526]]},
+'C3H8': {
+    'description': "Propane (C3H8), C2v symm.",
+    'name': "C_3H_8 (propane)",
+    'database': 'G2-2',
+    'enthalpy': -25.0,
+    'ZPE': 63.8008,
+    'thermal correction': 3.4632,
+    'symbols': 'CCCHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.587716],
+                  [ 0.      ,  1.266857, -0.260186],
+                  [ 0.      , -1.266857, -0.260186],
+                  [-0.876898,  0.      ,  1.244713],
+                  [ 0.876898,  0.      ,  1.244713],
+                  [ 0.      ,  2.16615 ,  0.362066],
+                  [ 0.      , -2.16615 ,  0.362066],
+                  [ 0.883619,  1.304234, -0.904405],
+                  [-0.883619,  1.304234, -0.904405],
+                  [-0.883619, -1.304234, -0.904405],
+                  [ 0.883619, -1.304234, -0.904405]]},
+'butadiene': {
+    'description': "Trans-1,3-butadiene (C4H6), C2h symm.",
+    'name': "CH_2CHCHCH_2 (butadiene)",
+    'database': 'G2-2',
+    'enthalpy': 26.3,
+    'ZPE': 52.6273,
+    'thermal correction': 3.5341,
+    'symbols': 'CCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.605711,  1.74655 ,  0.      ],
+                  [ 0.605711,  0.404083,  0.      ],
+                  [-0.605711, -0.404083,  0.      ],
+                  [-0.605711, -1.74655 ,  0.      ],
+                  [ 1.527617,  2.317443,  0.      ],
+                  [-0.321132,  2.313116,  0.      ],
+                  [ 1.553503, -0.13364 ,  0.      ],
+                  [-1.553503,  0.13364 ,  0.      ],
+                  [ 0.321132, -2.313116,  0.      ],
+                  [-1.527617, -2.317443,  0.      ]]},
+'2-butyne': {
+    'description': "Dimethylacetylene (2-butyne, C4H6), D3h symm (eclipsed).",
+    'name': "C_4H_6 (2-butyne)",
+    'database': 'G2-2',
+    'enthalpy': 34.8,
+    'ZPE': 51.8731,
+    'thermal correction': 4.2344,
+    'symbols': 'CCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  2.071955],
+                  [ 0.      ,  0.      ,  0.60997 ],
+                  [ 0.      ,  0.      , -0.60997 ],
+                  [ 0.      ,  0.      , -2.071955],
+                  [ 0.      ,  1.020696,  2.464562],
+                  [-0.883949, -0.510348,  2.464562],
+                  [ 0.883949, -0.510348,  2.464562],
+                  [ 0.      ,  1.020696, -2.464562],
+                  [ 0.883949, -0.510348, -2.464562],
+                  [-0.883949, -0.510348, -2.464562]]},
+'methylenecyclopropane': {
+    'description': "Methylenecyclopropane (C4H6), C2v symm.",
+    'name': "C_4H_6 (methylene cyclopropane)",
+    'database': 'G2-2',
+    'enthalpy': 47.9,
+    'ZPE': 52.6230,
+    'thermal correction': 3.2881,
+    'symbols': 'CCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.315026],
+                  [ 0.      , -0.76792 , -0.932032],
+                  [ 0.      ,  0.76792 , -0.932032],
+                  [ 0.      ,  0.      ,  1.640027],
+                  [-0.912794, -1.271789, -1.239303],
+                  [ 0.912794, -1.271789, -1.239303],
+                  [ 0.912794,  1.271789, -1.239303],
+                  [-0.912794,  1.271789, -1.239303],
+                  [ 0.      , -0.926908,  2.20564 ],
+                  [ 0.      ,  0.926908,  2.20564 ]]},
+'bicyclobutane': {
+    'description': "Bicyclo[1.1.0]butane (C4H6), C2v symm.",
+    'name': "C_4H_6 (bicyclobutane)",
+    'database': 'G2-2',
+    'enthalpy': 51.9,
+    'ZPE': 53.3527,
+    'thermal correction': 2.9637,
+    'symbols': 'CCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.131343,  0.310424],
+                  [ 0.      , -1.131343,  0.310424],
+                  [ 0.747952,  0.      , -0.311812],
+                  [-0.747952,  0.      , -0.311812],
+                  [ 0.      ,  1.237033,  1.397617],
+                  [ 0.      ,  2.077375, -0.227668],
+                  [ 0.      , -1.237033,  1.397617],
+                  [ 0.      , -2.077375, -0.227668],
+                  [ 1.41441 ,  0.      , -1.161626],
+                  [-1.41441 ,  0.      , -1.161626]]},
+'cyclobutene': {
+    'description': "Cyclobutene (C4H6), C2v symm.",
+    'name': "C_4H_6 (cyclobutene)",
+    'database': 'G2-2',
+    'enthalpy': 37.4,
+    'ZPE': 53.4105,
+    'thermal correction': 3.0108,
+    'symbols': 'CCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      , -0.672762,  0.811217],
+                  [ 0.      ,  0.672762,  0.811217],
+                  [ 0.      , -0.78198 , -0.696648],
+                  [ 0.      ,  0.78198 , -0.696648],
+                  [ 0.      , -1.422393,  1.597763],
+                  [ 0.      ,  1.422393,  1.597763],
+                  [-0.88931 , -1.239242, -1.142591],
+                  [ 0.88931 , -1.239242, -1.142591],
+                  [ 0.88931 ,  1.239242, -1.142591],
+                  [-0.88931 ,  1.239242, -1.142591]]},
+'cyclobutane': {
+    'description': "Cyclobutane (C4H8), D2d symm.",
+    'name': "C_4H_8 (cyclobutane)",
+    'database': 'G2-2',
+    'enthalpy': 6.8,
+    'ZPE': 68.3314,
+    'thermal correction': 3.2310,
+    'symbols': 'CCCCHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.071142,  0.147626],
+                  [ 0.      , -1.071142,  0.147626],
+                  [-1.071142,  0.      , -0.147626],
+                  [ 1.071142,  0.      , -0.147626],
+                  [ 0.      ,  1.986858, -0.450077],
+                  [ 0.      ,  1.342921,  1.20752 ],
+                  [ 0.      , -1.986858, -0.450077],
+                  [ 0.      , -1.342921,  1.20752 ],
+                  [-1.986858,  0.      ,  0.450077],
+                  [-1.342921,  0.      , -1.20752 ],
+                  [ 1.986858,  0.      ,  0.450077],
+                  [ 1.342921,  0.      , -1.20752 ]]},
+'isobutene': {
+    'description': "Isobutene (C4H8), Single bonds trans, C2v symm.",
+    'name': "C_4H_8 (isobutene)",
+    'database': 'G2-2',
+    'enthalpy': -4.0,
+    'ZPE': 66.5693,
+    'thermal correction': 3.9495,
+    'symbols': 'CCHHCHHHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.458807],
+                  [ 0.      ,  0.      ,  0.119588],
+                  [ 0.      ,  0.924302,  2.028409],
+                  [ 0.      , -0.924302,  2.028409],
+                  [ 0.      ,  1.272683, -0.678803],
+                  [ 0.      ,  2.153042, -0.031588],
+                  [ 0.880211,  1.323542, -1.329592],
+                  [-0.880211,  1.323542, -1.329592],
+                  [ 0.      , -1.272683, -0.678803],
+                  [ 0.      , -2.153042, -0.031588],
+                  [-0.880211, -1.323542, -1.329592],
+                  [ 0.880211, -1.323542, -1.329592]]},
+'trans-butane': {
+    'description': "Trans-butane (C4H10), C2h symm.",
+    'name': "C_4H_{10} (trans butane)",
+    'database': 'G2-2',
+    'enthalpy': -30.0,
+    'ZPE': 81.3980,
+    'thermal correction': 4.2633,
+    'symbols': 'CCCCHHHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.702581,  1.820873,  0.      ],
+                  [ 0.702581,  0.296325,  0.      ],
+                  [-0.702581, -0.296325,  0.      ],
+                  [-0.702581, -1.820873,  0.      ],
+                  [ 1.719809,  2.22234 ,  0.      ],
+                  [-1.719809, -2.22234 ,  0.      ],
+                  [ 0.188154,  2.210362,  0.883614],
+                  [ 0.188154,  2.210362, -0.883614],
+                  [-0.188154, -2.210362,  0.883614],
+                  [-0.188154, -2.210362, -0.883614],
+                  [ 1.247707, -0.07266 , -0.877569],
+                  [ 1.247707, -0.07266 ,  0.877569],
+                  [-1.247707,  0.07266 , -0.877569],
+                  [-1.247707,  0.07266 ,  0.877569]]},
+'isobutane': {
+    'description': "Isobutane (C4H10), C3v symm.",
+    'name': "C_4H_{10} (isobutane)",
+    'database': 'G2-2',
+    'enthalpy': -32.1,
+    'ZPE': 81.1050,
+    'thermal correction': 4.2282,
+    'symbols': 'CHCHHHCHHHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.376949],
+                  [ 0.      ,  0.      ,  1.475269],
+                  [ 0.      ,  1.45029 , -0.096234],
+                  [ 0.      ,  1.493997, -1.190847],
+                  [-0.885482,  1.984695,  0.261297],
+                  [ 0.885482,  1.984695,  0.261297],
+                  [ 1.255988, -0.725145, -0.096234],
+                  [ 1.293839, -0.746998, -1.190847],
+                  [ 2.161537, -0.225498,  0.261297],
+                  [ 1.276055, -1.759198,  0.261297],
+                  [-1.255988, -0.725145, -0.096234],
+                  [-1.293839, -0.746998, -1.190847],
+                  [-1.276055, -1.759198,  0.261297],
+                  [-2.161537, -0.225498,  0.261297]]},
+'C5H8': {
+    'description': "Spiropentane (C5H8), D2d symm.",
+    'name': "C_5H_8 (spiropentane)",
+    'database': 'G2-2',
+    'enthalpy': 44.3,
+    'ZPE': 70.9964,
+    'thermal correction': 3.7149,
+    'symbols': 'CCCCCHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.      ],
+                  [ 0.      ,  0.762014,  1.265752],
+                  [ 0.      , -0.762014,  1.265752],
+                  [ 0.762014,  0.      , -1.265752],
+                  [-0.762014,  0.      , -1.265752],
+                  [-0.914023,  1.265075,  1.56809 ],
+                  [ 0.914023,  1.265075,  1.56809 ],
+                  [-0.914023, -1.265075,  1.56809 ],
+                  [ 0.914023, -1.265075,  1.56809 ],
+                  [ 1.265075, -0.914023, -1.56809 ],
+                  [ 1.265075,  0.914023, -1.56809 ],
+                  [-1.265075, -0.914023, -1.56809 ],
+                  [-1.265075,  0.914023, -1.56809 ]]},
+'C6H6': {
+    'description': "Benzene (C6H6), D6h symm.",
+    'name': "C_6H_6 (benzene)",
+    'database': 'G2-2',
+    'enthalpy': 19.7,
+    'ZPE': 61.9252,
+    'thermal correction': 3.3886,
+    'ionization energy': 9.24,
+    'vertical ionization energy': 9.25,
+    'symbols': 'CCCCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.395248,  0.      ],
+                  [ 1.20832 ,  0.697624,  0.      ],
+                  [ 1.20832 , -0.697624,  0.      ],
+                  [ 0.      , -1.395248,  0.      ],
+                  [-1.20832 , -0.697624,  0.      ],
+                  [-1.20832 ,  0.697624,  0.      ],
+                  [ 0.      ,  2.48236 ,  0.      ],
+                  [ 2.149787,  1.24118 ,  0.      ],
+                  [ 2.149787, -1.24118 ,  0.      ],
+                  [ 0.      , -2.48236 ,  0.      ],
+                  [-2.149787, -1.24118 ,  0.      ],
+                  [-2.149787,  1.24118 ,  0.      ]]},
+'H2CF2': {
+    'description': "Difluoromethane (H2CF2), C2v symm.",
+    'name': "CH_2F_2",
+    'database': 'G2-2',
+    'enthalpy': -107.7,
+    'ZPE': 20.2767,
+    'thermal correction': 2.5552,
+    'symbols': 'CFFHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.502903],
+                  [ 0.      ,  1.109716, -0.290601],
+                  [ 0.      , -1.109716, -0.290601],
+                  [-0.908369,  0.      ,  1.106699],
+                  [ 0.908369,  0.      ,  1.106699]]},
+'HCF3': {
+    'description': "Trifluoromethane (HCF3), C3v symm.",
+    'name': "CHF_3",
+    'database': 'G2-2',
+    'enthalpy': -166.6,
+    'ZPE': 15.7072,
+    'thermal correction': 2.7717,
+    'symbols': 'CHFFF',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.341023],
+                  [ 0.      ,  0.      ,  1.429485],
+                  [ 0.      ,  1.2582  , -0.128727],
+                  [ 1.089633, -0.6291  , -0.128727],
+                  [-1.089633, -0.6291  , -0.128727]]},
+'H2CCl2': {
+    'description': "Dichloromethane (H2CCl2), C2v symm.",
+    'name': "CH_2Cl_2",
+    'database': 'G2-2',
+    'enthalpy': -22.8,
+    'ZPE': 18.0930,
+    'thermal correction': 2.8527,
+    'symbols': 'CClClHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.759945],
+                  [ 0.      ,  1.4742  , -0.215115],
+                  [ 0.      , -1.4742  , -0.215115],
+                  [-0.894585,  0.      ,  1.377127],
+                  [ 0.894585,  0.      ,  1.377127]]},
+'HCCl3': {
+    'description': "Chloroform (HCCl3), C3v symm.",
+    'name': "CHCl_3",
+    'database': 'G2-2',
+    'enthalpy': -24.7,
+    'ZPE': 12.1975,
+    'thermal correction': 3.4262,
+    'symbols': 'CHClClCl',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.451679],
+                  [ 0.      ,  0.      ,  1.537586],
+                  [ 0.      ,  1.681723, -0.083287],
+                  [ 1.456415, -0.840862, -0.083287],
+                  [-1.456415, -0.840862, -0.083287]]},
+'H3CNH2': {
+    'description': "Methylamine (H3C-NH2), Cs symm.",
+    'name': "CH_3NH_2 (methylamine)",
+    'database': 'G2-2',
+    'enthalpy': -5.5,
+    'ZPE': 39.5595,
+    'thermal correction': 2.7428,
+    'symbols': 'CNHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.051736,  0.704422,  0.      ],
+                  [ 0.051736, -0.759616,  0.      ],
+                  [-0.941735,  1.176192,  0.      ],
+                  [-0.458181, -1.099433,  0.81237 ],
+                  [-0.458181, -1.099433, -0.81237 ],
+                  [ 0.592763,  1.056727,  0.88067 ],
+                  [ 0.592763,  1.056727, -0.88067 ]]},
+'CH3CN': {
+    'description': "Acetonitrile (CH3-CN), C3v symm.",
+    'name': "CH_3CN (methyl cyanide)",
+    'database': 'G2-2',
+    'enthalpy': 18.0,
+    'ZPE': 28.0001,
+    'thermal correction': 2.8552,
+    'symbols': 'CCNHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.18693 ],
+                  [ 0.      ,  0.      ,  0.273874],
+                  [ 0.      ,  0.      ,  1.452206],
+                  [ 0.      ,  1.024986, -1.56237 ],
+                  [ 0.887664, -0.512493, -1.56237 ],
+                  [-0.887664, -0.512493, -1.56237 ]]},
+'CH3NO2': {
+    'description': "Nitromethane (CH3-NO2), Cs symm.",
+    'name': "CH_3NO_2 (nitromethane)",
+    'database': 'G2-2',
+    'enthalpy': -17.8,
+    'ZPE': 30.7568,
+    'thermal correction': 2.7887,
+    'symbols': 'CNHHHOO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.114282, -1.314565,  0.      ],
+                  [ 0.      ,  0.16648 ,  0.      ],
+                  [ 0.899565, -1.715256,  0.      ],
+                  [-0.640921, -1.607212,  0.904956],
+                  [-0.640921, -1.607212, -0.904956],
+                  [ 0.066748,  0.728232, -1.103775],
+                  [ 0.066748,  0.728232,  1.103775]]},
+'CH3ONO': {
+    'description': "Methylnitrite (CH3-O-N=O), NOCH trans, ONOC cis, Cs symm.",
+    'name': "CH_3ONO (methyl nitrite)",
+    'database': 'G2-2',
+    'enthalpy': -15.9,
+    'ZPE': 29.9523,
+    'thermal correction': 3.3641,
+    'symbols': 'COHHHNO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-1.316208,  0.309247,  0.      ],
+                  [ 0.      ,  0.896852,  0.      ],
+                  [-1.985538,  1.166013,  0.      ],
+                  [-1.464336, -0.304637,  0.890672],
+                  [-1.464336, -0.304637, -0.890672],
+                  [ 1.045334, -0.022815,  0.      ],
+                  [ 0.686764, -1.178416,  0.      ]]},
+'CH3SiH3': {
+    'description': "Methylsilane (CH3-SiH3), C3v symm.",
+    'name': "CH_3SiH_3 (methyl silane)",
+    'database': 'G2-2',
+    'enthalpy': -7.0,
+    'ZPE': 37.6606,
+    'thermal correction': 3.2486,
+    'symbols': 'CSiHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.244466],
+                  [ 0.      ,  0.      ,  0.635703],
+                  [ 0.      , -1.019762, -1.636363],
+                  [-0.88314 ,  0.509881, -1.636363],
+                  [ 0.88314 ,  0.509881, -1.636363],
+                  [ 0.      ,  1.391234,  1.158682],
+                  [-1.204844, -0.695617,  1.158682],
+                  [ 1.204844, -0.695617,  1.158682]]},
+'HCOOH': {
+    'description': "Formic Acid (HCOOH), HOCO cis, Cs symm.",
+    'name': "HCOOH (formic acid)",
+    'database': 'G2-2',
+    'enthalpy': -90.5,
+    'ZPE': 20.9525,
+    'thermal correction': 2.5853,
+    'symbols': 'OCOHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-1.040945, -0.436432,  0.      ],
+                  [ 0.      ,  0.423949,  0.      ],
+                  [ 1.169372,  0.103741,  0.      ],
+                  [-0.64957 , -1.335134,  0.      ],
+                  [-0.377847,  1.452967,  0.      ]]},
+'HCOOCH3': {
+    'description': "Methyl formate (HCOOCH3), Cs symm.",
+    'name': "HCOOCH_3 (methyl formate)",
+    'database': 'G2-2',
+    'enthalpy': -85.0,
+    'ZPE': 38.3026,
+    'thermal correction': 3.4726,
+    'symbols': 'COOHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.931209, -0.083866,  0.      ],
+                  [-0.711019, -1.278209,  0.      ],
+                  [ 0.      ,  0.886841,  0.      ],
+                  [-1.92836 ,  0.374598,  0.      ],
+                  [ 1.356899,  0.397287,  0.      ],
+                  [ 1.980134,  1.288164,  0.      ],
+                  [ 1.541121, -0.206172,  0.889397],
+                  [ 1.541121, -0.206172, -0.889397]]},
+'CH3CONH2': {
+    'description': "Acetamide (CH3CONH2), C1 symm.",
+    'name': "CH_3CONH_2 (acetamide)",
+    'database': 'G2-2',
+    'enthalpy': -57.0,
+    'ZPE': 45.2566,
+    'thermal correction': 3.9313,
+    'symbols': 'OCNCHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[  4.24546000e-01,   1.32702400e+00,   8.03400000e-03],
+                  [  7.71580000e-02,   1.49789000e-01,  -4.24900000e-03],
+                  [  9.85518000e-01,  -8.78537000e-01,  -4.89100000e-02],
+                  [ -1.37147500e+00,  -2.88665000e-01,  -1.44000000e-04],
+                  [  7.07952000e-01,  -1.82424900e+00,   1.69942000e-01],
+                  [ -1.99722900e+00,   5.84922000e-01,  -1.75477000e-01],
+                  [ -1.56084200e+00,  -1.03927000e+00,  -7.71686000e-01],
+                  [ -1.63211300e+00,  -7.23007000e-01,   9.69814000e-01],
+                  [  1.95313300e+00,  -6.31574000e-01,   1.11866000e-01]]},
+'CH2NHCH2': {
+    'description': "Aziridine (cyclic CH2-NH-CH2 ring), C2v symm.",
+    'name': "C_2H_4NH (aziridine)",
+    'database': 'G2-2',
+    'enthalpy': 30.2,
+    'ZPE': 43.3728,
+    'thermal correction': 2.6399,
+    'symbols': 'CNCHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.03845 , -0.397326,  0.739421],
+                  [-0.03845 ,  0.875189,  0.      ],
+                  [-0.03845 , -0.397326, -0.739421],
+                  [ 0.903052,  1.268239,  0.      ],
+                  [-0.955661, -0.604926,  1.280047],
+                  [-0.955661, -0.604926, -1.280047],
+                  [ 0.869409, -0.708399,  1.249033],
+                  [ 0.869409, -0.708399, -1.249033]]},
+'NCCN': {
+    'description': "Cyanogen (NCCN). D*h symm.",
+    'name': "NCCN (cyanogen)",
+    'database': 'G2-2',
+    'enthalpy': 73.3,
+    'ZPE': 10.2315,
+    'thermal correction': 2.9336,
+    'symbols': 'NCCN',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.875875],
+                  [ 0.      ,  0.      ,  0.690573],
+                  [ 0.      ,  0.      , -0.690573],
+                  [ 0.      ,  0.      , -1.875875]]},
+'C2H6NH': {
+    'description': "Dimethylamine, (CH3)2NH, Cs symm.",
+    'name': "(CH_3)_2NH (dimethylamine)",
+    'database': 'G2-2',
+    'enthalpy': -4.4,
+    'ZPE': 57.0287,
+    'thermal correction': 3.3760,
+    'symbols': 'CNCHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.02753 , -0.224702,  1.20488 ],
+                  [-0.02753 ,  0.59247 ,  0.      ],
+                  [-0.02753 , -0.224702, -1.20488 ],
+                  [ 0.791501, -0.962742,  1.248506],
+                  [ 0.039598,  0.421182,  2.083405],
+                  [-0.97222 , -0.772987,  1.26175 ],
+                  [ 0.805303,  1.17822 ,  0.      ],
+                  [ 0.791501, -0.962742, -1.248506],
+                  [ 0.039598,  0.421182, -2.083405],
+                  [-0.97222 , -0.772987, -1.26175 ]]},
+'CH3CH2NH2': {
+    'description': "Trans-Ethylamine (CH3-CH2-NH2), Cs symm.",
+    'name': "CH_3CH_2NH_2 (trans ethylamine)",
+    'database': 'G2-2',
+    'enthalpy': -11.3,
+    'ZPE': 57.2420,
+    'thermal correction': 3.3678,
+    'symbols': 'CCNHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 1.210014, -0.353598,  0.      ],
+                  [ 0.      ,  0.575951,  0.      ],
+                  [-1.305351, -0.087478,  0.      ],
+                  [ 2.14931 ,  0.208498,  0.      ],
+                  [ 1.201796, -0.99776 ,  0.884909],
+                  [ 1.201796, -0.99776 , -0.884909],
+                  [ 0.034561,  1.230963, -0.876478],
+                  [ 0.034561,  1.230963,  0.876478],
+                  [-1.372326, -0.69834 ,  0.813132],
+                  [-1.372326, -0.69834 , -0.813132]]},
+'H2CCO': {
+    'description': "Ketene (H2C=C=O), C2v symm.",
+    'name': "CH_2CO (ketene)",
+    'database': 'G2-2',
+    'enthalpy': -11.4,
+    'ZPE': 19.5984,
+    'thermal correction': 2.8075,
+    'symbols': 'CCHHO',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -1.21934 ],
+                  [ 0.      ,  0.      ,  0.09892 ],
+                  [ 0.      ,  0.938847, -1.753224],
+                  [ 0.      , -0.938847, -1.753224],
+                  [ 0.      ,  0.      ,  1.27862 ]]},
+'CH2OCH2': {
+    'description': "Oxirane (cyclic CH2-O-CH2 ring), C2v symm.",
+    'name': "C_2H_4O (oxirane)",
+    'database': 'G2-2',
+    'enthalpy': -12.6,
+    'ZPE': 35.4204,
+    'thermal correction': 2.5816,
+    'symbols': 'COCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.73158 , -0.375674],
+                  [ 0.      ,  0.      ,  0.86095 ],
+                  [ 0.      , -0.73158 , -0.375674],
+                  [ 0.919568,  1.268821, -0.594878],
+                  [-0.919568,  1.268821, -0.594878],
+                  [-0.919568, -1.268821, -0.594878],
+                  [ 0.919568, -1.268821, -0.594878]]},
+'CH3CHO': {
+    'description': "Acetaldehyde (CH3CHO), Cs symm.",
+    'name': "CH_3CHO (acetaldehyde)",
+    'database': 'G2-2',
+    'enthalpy': -39.7,
+    'ZPE': 34.2288,
+    'thermal correction': 3.0428,
+    'symbols': 'OCHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 1.218055,  0.36124 ,  0.      ],
+                  [ 0.      ,  0.464133,  0.      ],
+                  [-0.477241,  1.465295,  0.      ],
+                  [-0.948102, -0.700138,  0.      ],
+                  [-0.385946, -1.634236,  0.      ],
+                  [-1.596321, -0.652475,  0.880946],
+                  [-1.596321, -0.652475, -0.880946]]},
+'OCHCHO': {
+    'description': "Glyoxal (O=CH-CH=O). Trans, C2h symm.",
+    'name': "HCOCOH (glyoxal)",
+    'database': 'G2-2',
+    'enthalpy': -50.7,
+    'ZPE': 22.8426,
+    'thermal correction': 3.2518,
+    'symbols': 'CCOHOH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.75643 ,  0.      ],
+                  [ 0.      , -0.75643 ,  0.      ],
+                  [ 1.04609 ,  1.389916,  0.      ],
+                  [-0.99994 ,  1.228191,  0.      ],
+                  [-1.04609 , -1.389916,  0.      ],
+                  [ 0.99994 , -1.228191,  0.      ]]},
+'CH3CH2OH': {
+    'description': "Ethanol (trans, CH3CH2OH), Cs symm.",
+    'name': "CH_3CH_2OH (ethanol)",
+    'database': 'G2-2',
+    'enthalpy': -56.2,
+    'ZPE': 49.3072,
+    'thermal correction': 3.3252,
+    'symbols': 'CCOHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 1.168181, -0.400382,  0.      ],
+                  [ 0.      ,  0.559462,  0.      ],
+                  [-1.190083, -0.227669,  0.      ],
+                  [-1.946623,  0.381525,  0.      ],
+                  [ 0.042557,  1.207508,  0.886933],
+                  [ 0.042557,  1.207508, -0.886933],
+                  [ 2.115891,  0.1448  ,  0.      ],
+                  [ 1.128599, -1.037234,  0.885881],
+                  [ 1.128599, -1.037234, -0.885881]]},
+'CH3OCH3': {
+    'description': "DimethylEther (CH3-O-CH3), C2v symm.",
+    'name': "CH_3OCH_3 (dimethylether)",
+    'database': 'G2-2',
+    'enthalpy': -44.0,
+    'ZPE': 49.1911,
+    'thermal correction': 3.3139,
+    'symbols': 'COCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.165725, -0.19995 ],
+                  [ 0.      ,  0.      ,  0.60011 ],
+                  [ 0.      , -1.165725, -0.19995 ],
+                  [ 0.      ,  2.017769,  0.480203],
+                  [ 0.891784,  1.21432 , -0.840474],
+                  [-0.891784,  1.21432 , -0.840474],
+                  [ 0.      , -2.017769,  0.480203],
+                  [-0.891784, -1.21432 , -0.840474],
+                  [ 0.891784, -1.21432 , -0.840474]]},
+'CH2SCH2': {
+    'description': "Thiooxirane (cyclic CH2-S-CH2 ring), C2v symm.",
+    'name': "C_2H_4S (thiirane)",
+    'database': 'G2-2',
+    'enthalpy': 19.6,
+    'ZPE': 33.9483,
+    'thermal correction': 2.7290,
+    'symbols': 'CSCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      , -0.739719, -0.792334],
+                  [ 0.      ,  0.      ,  0.863474],
+                  [ 0.      ,  0.739719, -0.792334],
+                  [-0.91394 , -1.250142, -1.076894],
+                  [ 0.91394 , -1.250142, -1.076894],
+                  [ 0.91394 ,  1.250142, -1.076894],
+                  [-0.91394 ,  1.250142, -1.076894]]},
+'C2H6SO': {
+    'description': "Dimethylsulfoxide (CH3)2SO, Cs symm.",
+    'name': "(CH_3)_2SO (dimethyl sulfoxide)",
+    'database': 'G2-2',
+    'enthalpy': -36.2,
+    'ZPE': 48.8479,
+    'thermal correction': 4.1905,
+    'symbols': 'SOCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[  2.00000000e-06,   2.31838000e-01,  -4.38643000e-01],
+                  [  2.00000000e-05,   1.50074200e+00,   3.79819000e-01],
+                  [  1.33952800e+00,  -8.09022000e-01,   1.80717000e-01],
+                  [ -1.33954800e+00,  -8.08992000e-01,   1.80718000e-01],
+                  [  1.25583500e+00,  -8.96385000e-01,   1.26682500e+00],
+                  [ -2.27940400e+00,  -3.13924000e-01,  -6.86740000e-02],
+                  [  1.30440700e+00,  -1.79332700e+00,  -2.92589000e-01],
+                  [  2.27939500e+00,  -3.13974000e-01,  -6.86740000e-02],
+                  [ -1.30444700e+00,  -1.79329800e+00,  -2.92587000e-01],
+                  [ -1.25585700e+00,  -8.96355000e-01,   1.26682600e+00]]},
+'CH3CH2SH': {
+    'description': "ThioEthanol (CH3-CH2-SH), Cs symm.",
+    'name': "C_2H_5SH (ethanethiol)",
+    'database': 'G2-2',
+    'enthalpy': -11.1,
+    'ZPE': 46.1583,
+    'thermal correction': 3.5900,
+    'symbols': 'CCSHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 1.514343,  0.679412,  0.      ],
+                  [ 0.      ,  0.826412,  0.      ],
+                  [-0.756068, -0.831284,  0.      ],
+                  [-2.035346, -0.427738,  0.      ],
+                  [-0.32497 ,  1.376482,  0.885793],
+                  [-0.32497 ,  1.376482, -0.885793],
+                  [ 1.986503,  1.665082,  0.      ],
+                  [ 1.854904,  0.137645,  0.885494],
+                  [ 1.854904,  0.137645, -0.885494]]},
+'CH3SCH3': {
+    'description': "Dimethyl ThioEther (CH3-S-CH3), C2v symm.",
+    'name': "CH_3SCH_3 (dimethyl sulfide)",
+    'database': 'G2-2',
+    'enthalpy': -8.9,
+    'ZPE': 46.6760,
+    'thermal correction': 3.6929,
+    'symbols': 'CSCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  1.366668, -0.513713],
+                  [ 0.      ,  0.      ,  0.664273],
+                  [ 0.      , -1.366668, -0.513713],
+                  [ 0.      ,  2.296687,  0.057284],
+                  [ 0.891644,  1.34568 , -1.144596],
+                  [-0.891644,  1.34568 , -1.144596],
+                  [ 0.      , -2.296687,  0.057284],
+                  [-0.891644, -1.34568 , -1.144596],
+                  [ 0.891644, -1.34568 , -1.144596]]},
+'H2CCHF': {
+    'description': "Vinyl fluoride (H2C=CHF), Cs symm.",
+    'name': "CH_2=CHF (vinyl fluoride)",
+    'database': 'G2-2',
+    'enthalpy': -33.2,
+    'ZPE': 27.2785,
+    'thermal correction': 2.7039,
+    'symbols': 'CCFHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.437714,  0.      ],
+                  [ 1.191923, -0.145087,  0.      ],
+                  [-1.148929, -0.278332,  0.      ],
+                  [-0.186445,  1.505778,  0.      ],
+                  [ 1.291348, -1.222833,  0.      ],
+                  [ 2.083924,  0.466279,  0.      ]]},
+'CH3CH2Cl': {
+    'description': "Ethyl chloride (CH3-CH2-Cl), Cs symm.",
+    'name': "C_2H_5Cl (ethyl chloride)",
+    'database': 'G2-2',
+    'enthalpy': -26.8,
+    'ZPE': 41.0686,
+    'thermal correction': 3.1488,
+    'symbols': 'CCClHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.807636,  0.      ],
+                  [ 1.505827,  0.647832,  0.      ],
+                  [-0.823553, -0.77997 ,  0.      ],
+                  [-0.344979,  1.341649,  0.885248],
+                  [-0.344979,  1.341649, -0.885248],
+                  [ 1.976903,  1.634877,  0.      ],
+                  [ 1.839246,  0.10425 ,  0.885398],
+                  [ 1.839246,  0.10425 , -0.885398]]},
+'H2CCHCl': {
+    'description': "Vinyl chloride, H2C=CHCl, Cs symm.",
+    'name': "CH_2=CHCl (vinyl chloride)",
+    'database': 'G2-2',
+    'enthalpy': 8.9,
+    'ZPE': 26.3554,
+    'thermal correction': 2.8269,
+    'symbols': 'CCClHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.756016,  0.      ],
+                  [ 1.303223,  1.028507,  0.      ],
+                  [-0.631555, -0.85498 ,  0.      ],
+                  [-0.771098,  1.516963,  0.      ],
+                  [ 2.056095,  0.249427,  0.      ],
+                  [ 1.632096,  2.061125,  0.      ]]},
+'H2CCHCN': {
+    'description': "CyanoEthylene (H2C=CHCN), Cs symm.",
+    'name': "CH_2=CHCN (acrylonitrile)",
+    'database': 'G2-2',
+    'enthalpy': 43.2,
+    'ZPE': 31.4081,
+    'thermal correction': 3.2034,
+    'symbols': 'CCCHHHN',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[-0.161594, -1.638625,  0.      ],
+                  [ 0.584957, -0.524961,  0.      ],
+                  [ 0.      ,  0.782253,  0.      ],
+                  [-1.245203, -1.598169,  0.      ],
+                  [ 0.305973, -2.616405,  0.      ],
+                  [ 1.669863, -0.572107,  0.      ],
+                  [-0.467259,  1.867811,  0.      ]]},
+'CH3COCH3': {
+    'description': "Acetone (CH3-CO-CH3), C2v symm.",
+    'name': "CH_3COCH_3 (acetone)",
+    'database': 'G2-2',
+    'enthalpy': -51.9,
+    'ZPE': 51.5587,
+    'thermal correction': 3.9878,
+    'symbols': 'OCCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.405591],
+                  [ 0.      ,  0.      ,  0.17906 ],
+                  [ 0.      ,  1.28549 , -0.616342],
+                  [ 0.      , -1.28549 , -0.616342],
+                  [ 0.      ,  2.134917,  0.066535],
+                  [ 0.      , -2.134917,  0.066535],
+                  [-0.881086,  1.331548, -1.264013],
+                  [ 0.881086,  1.331548, -1.264013],
+                  [ 0.881086, -1.331548, -1.264013],
+                  [-0.881086, -1.331548, -1.264013]]},
+'CH3COOH': {
+    'description': "Acetic Acid (CH3COOH), Single bonds trans, Cs symm.",
+    'name': "CH_3COOH (acetic acid)",
+    'database': 'G2-2',
+    'enthalpy': -103.4,
+    'ZPE': 38.1670,
+    'thermal correction': 3.4770,
+    'symbols': 'COOHCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.15456 ,  0.      ],
+                  [ 0.166384,  1.360084,  0.      ],
+                  [-1.236449, -0.415036,  0.      ],
+                  [-1.867646,  0.333582,  0.      ],
+                  [ 1.073776, -0.892748,  0.      ],
+                  [ 2.048189, -0.408135,  0.      ],
+                  [ 0.968661, -1.528353,  0.881747],
+                  [ 0.968661, -1.528353, -0.881747]]},
+'CH3COF': {
+    'description': "Acetyl fluoride (CH3COF), HCCO cis, Cs symm.",
+    'name': "CH_3COF (acetyl fluoride)",
+    'database': 'G2-2',
+    'enthalpy': -105.7,
+    'ZPE': 30.2742,
+    'thermal correction': 3.3126,
+    'symbols': 'COFCHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.186396,  0.      ],
+                  [ 0.126651,  1.377199,  0.      ],
+                  [-1.24395 , -0.382745,  0.      ],
+                  [ 1.049454, -0.876224,  0.      ],
+                  [ 2.035883, -0.417099,  0.      ],
+                  [ 0.924869, -1.508407,  0.881549],
+                  [ 0.924869, -1.508407, -0.881549]]},
+'CH3COCl': {
+    'description': "Acetyl,Chloride (CH3COCl), HCCO cis, Cs symm.",
+    'name': "CH_3COCl (acetyl chloride)",
+    'database': 'G2-2',
+    'enthalpy': -58.0,
+    'ZPE': 29.1855,
+    'thermal correction': 3.5235,
+    'symbols': 'CCClOHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.523878,  0.      ],
+                  [ 1.486075,  0.716377,  0.      ],
+                  [-0.452286, -1.217999,  0.      ],
+                  [-0.845539,  1.37494 ,  0.      ],
+                  [ 1.701027,  1.784793,  0.      ],
+                  [ 1.917847,  0.240067,  0.882679],
+                  [ 1.917847,  0.240067, -0.882679]]},
+'C3H7Cl': {
+    'description': "Propyl chloride (CH3CH2CH2Cl), Cs symm.",
+    'name': "CH_3CH_2CH_2Cl (propyl chloride)",
+    'database': 'G2-2',
+    'enthalpy': -31.5,
+    'ZPE': 58.6696,
+    'thermal correction': 3.9885,
+    'symbols': 'CCCHHHClHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.892629, -0.642344,  0.      ],
+                  [ 2.365587, -0.245168,  0.      ],
+                  [ 0.      ,  0.582921,  0.      ],
+                  [ 0.663731, -1.252117,  0.879201],
+                  [ 0.663731, -1.252117, -0.879201],
+                  [ 3.005476, -1.130924,  0.      ],
+                  [-1.73281 ,  0.139743,  0.      ],
+                  [ 2.614882,  0.347704, -0.88473 ],
+                  [ 2.614882,  0.347704,  0.88473 ],
+                  [ 0.172881,  1.195836,  0.88646 ],
+                  [ 0.172881,  1.195836, -0.88646 ]]},
+'C2H6CHOH': {
+    'description': "Isopropyl alcohol, (CH3)2CH-OH, Gauche isomer, C1 symm.",
+    'name': "(CH_3)_2CHOH (isopropanol)",
+    'database': 'G2-2',
+    'enthalpy': -65.2,
+    'ZPE': 66.5612,
+    'thermal correction': 4.0732,
+    'symbols': 'OCHHCCHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[  2.71910000e-02,   1.36369100e+00,  -1.67516000e-01],
+                  [ -9.26000000e-04,   3.64590000e-02,   3.70128000e-01],
+                  [  8.59465000e-01,   1.77564700e+00,   1.21307000e-01],
+                  [  7.37100000e-03,   8.21450000e-02,   1.47050600e+00],
+                  [ -1.31327500e+00,  -5.63514000e-01,  -8.89790000e-02],
+                  [  1.20072100e+00,  -7.64480000e-01,  -1.04920000e-01],
+                  [ -1.33400500e+00,  -6.07253000e-01,  -1.18100900e+00],
+                  [  1.20284300e+00,  -8.07817000e-01,  -1.19718900e+00],
+                  [ -2.14781200e+00,   5.49930000e-02,   2.47676000e-01],
+                  [  2.13646200e+00,  -2.99324000e-01,   2.23164000e-01],
+                  [ -1.43870900e+00,  -1.57427500e+00,   3.08340000e-01],
+                  [  1.17773600e+00,  -1.78443600e+00,   2.89967000e-01]]},
+'CH3CH2OCH3': {
+    'description': "Methyl ethyl ether (CH3-CH2-O-CH3), Trans, Cs symm.",
+    'name': "C_2H_5OCH_3 (methyl ethyl ether)",
+    'database': 'G2-2',
+    'enthalpy': -51.7,
+    'ZPE': 66.6936,
+    'thermal correction': 4.1058,
+    'symbols': 'OCCCHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.006429, -0.712741,  0.      ],
+                  [ 0.      ,  0.705845,  0.      ],
+                  [ 1.324518, -1.226029,  0.      ],
+                  [-1.442169,  1.160325,  0.      ],
+                  [ 0.530962,  1.086484,  0.886881],
+                  [ 0.530962,  1.086484, -0.886881],
+                  [ 1.241648, -2.313325,  0.      ],
+                  [ 1.881329, -0.905925, -0.89171 ],
+                  [ 1.881329, -0.905925,  0.89171 ],
+                  [-1.954863,  0.780605, -0.885855],
+                  [-1.954863,  0.780605,  0.885855],
+                  [-1.502025,  2.252083,  0.      ]]},
+'C3H9N': {
+    'description': "Trimethyl Amine, (CH3)3N, C3v symm.",
+    'name': "(CH_3)_3N (trimethylamine)",
+    'database': 'G2-2',
+    'enthalpy': -5.7,
+    'ZPE': 74.1584,
+    'thermal correction': 4.0631,
+    'symbols': 'NCCCHHHHHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.395846],
+                  [ 0.      ,  1.378021, -0.065175],
+                  [ 1.193401, -0.689011, -0.065175],
+                  [-1.193401, -0.689011, -0.065175],
+                  [ 0.      ,  1.461142, -1.167899],
+                  [ 0.886156,  1.891052,  0.317655],
+                  [-0.886156,  1.891052,  0.317655],
+                  [ 1.265386, -0.730571, -1.167899],
+                  [ 1.194621, -1.71296 ,  0.317655],
+                  [ 2.080777, -0.178092,  0.317655],
+                  [-1.265386, -0.730571, -1.167899],
+                  [-2.080777, -0.178092,  0.317655],
+                  [-1.194621, -1.71296 ,  0.317655]]},
+'C4H4O': {
+    'description': "Furan (cyclic C4H4O), C2v symm.",
+    'name': "C_4H_4O (furan)",
+    'database': 'G2-2',
+    'enthalpy': -8.3,
+    'ZPE': 43.2116,
+    'thermal correction': 2.9480,
+    'symbols': 'OCCCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.163339],
+                  [ 0.      ,  1.0947  ,  0.348039],
+                  [ 0.      , -1.0947  ,  0.348039],
+                  [ 0.      ,  0.7132  , -0.962161],
+                  [ 0.      , -0.7132  , -0.962161],
+                  [ 0.      ,  2.049359,  0.851113],
+                  [ 0.      , -2.049359,  0.851113],
+                  [ 0.      ,  1.370828, -1.819738],
+                  [ 0.      , -1.370828, -1.819738]]},
+'C4H4S': {
+    'description': "Thiophene (cyclic C4H4S), C2v symm.",
+    'name': "C_4H_4S (thiophene)",
+    'database': 'G2-2',
+    'enthalpy': 27.5,
+    'ZPE': 41.2029,
+    'thermal correction': 3.1702,
+    'symbols': 'SCCCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[  0.00000000e+00,   0.00000000e+00,   1.18975300e+00],
+                  [  0.00000000e+00,   1.23387600e+00,  -1.47400000e-03],
+                  [  0.00000000e+00,  -1.23387600e+00,  -1.47400000e-03],
+                  [  0.00000000e+00,   7.09173000e-01,  -1.27232200e+00],
+                  [  0.00000000e+00,  -7.09173000e-01,  -1.27232200e+00],
+                  [  0.00000000e+00,   2.27534300e+00,   2.91984000e-01],
+                  [  0.00000000e+00,  -2.27534300e+00,   2.91984000e-01],
+                  [  0.00000000e+00,   1.32193400e+00,  -2.16723100e+00],
+                  [  0.00000000e+00,  -1.32193400e+00,  -2.16723100e+00]]},
+'C4H4NH': {
+    'description': "Pyrrole (Planar cyclic C4H4NH), C2v symm.",
+    'name': "C_4H_5N (pyrrole)",
+    'database': 'G2-2',
+    'enthalpy': 25.9,
+    'ZPE': 50.9688,
+    'thermal correction': 3.1156,
+    'symbols': 'HNCCCCHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  2.129296],
+                  [ 0.      ,  0.      ,  1.118684],
+                  [ 0.      ,  1.124516,  0.333565],
+                  [ 0.      , -1.124516,  0.333565],
+                  [ 0.      ,  0.708407, -0.983807],
+                  [ 0.      , -0.708407, -0.983807],
+                  [ 0.      ,  2.112872,  0.770496],
+                  [ 0.      , -2.112872,  0.770496],
+                  [ 0.      ,  1.357252, -1.849085],
+                  [ 0.      , -1.357252, -1.849085]]},
+'C5H5N': {
+    'description': "Pyridine (cyclic C5H5N), C2v symm.",
+    'name': "C_5H_5N (pyridine)",
+    'database': 'G2-2',
+    'enthalpy': 33.6,
+    'ZPE': 54.8230,
+    'thermal correction': 3.3007,
+    'symbols': 'NCCCCCHHHHH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  1.424672],
+                  [ 0.      ,  0.      , -1.386178],
+                  [ 0.      ,  1.144277,  0.720306],
+                  [ 0.      , -1.144277,  0.720306],
+                  [ 0.      , -1.196404, -0.672917],
+                  [ 0.      ,  1.196404, -0.672917],
+                  [ 0.      ,  0.      , -2.473052],
+                  [ 0.      ,  2.060723,  1.307477],
+                  [ 0.      , -2.060723,  1.307477],
+                  [ 0.      , -2.155293, -1.183103],
+                  [ 0.      ,  2.155293, -1.183103]]},
+'H2': {
+    'description': "H2. D*h symm.",
+    'name': "H_2",
+    'database': 'G2-2',
+    'enthalpy': 0.0,
+    'ZPE': 6.2908,
+    'thermal correction': 2.0739,
+    'ionization energy': 15.43,
+    'symbols': 'HH',
+    'magmoms': None,
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.368583],
+                  [ 0.      ,  0.      , -0.368583]]},
+'SH': {
+    'description': "SH radical, C*v symm.",
+    'name': "HS",
+    'database': 'G2-2',
+    'enthalpy': 34.2,
+    'ZPE': 3.7625,
+    'thermal correction': 2.0739,
+    'symbols': 'SH',
+    'magmoms': [ 1.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.079083],
+                  [ 0.      ,  0.      , -1.26533 ]]},
+'CCH': {
+    'description': "CCH radical, C*v symm.",
+    'name': "CCH",
+    'database': 'G2-2',
+    'enthalpy': 135.1,
+    'ZPE': 7.8533,
+    'thermal correction': 2.7830,
+    'symbols': 'CCH',
+    'magmoms': [ 0.,  1.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      , -0.462628],
+                  [ 0.      ,  0.      ,  0.717162],
+                  [ 0.      ,  0.      , -1.527198]]},
+'C2H3': {
+    'description': "C2H3 radical, Cs symm, 2-A'.",
+    'name': "C_2H_3 (2A')",
+    'database': 'G2-2',
+    'enthalpy': 71.6,
+    'ZPE': 22.5747,
+    'thermal correction': 2.5483,
+    'symbols': 'CCHHH',
+    'magmoms': [ 0.,  1.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.049798, -0.576272,  0.      ],
+                  [ 0.049798,  0.710988,  0.      ],
+                  [-0.87675 , -1.151844,  0.      ],
+                  [ 0.969183, -1.154639,  0.      ],
+                  [-0.690013,  1.498185,  0.      ]]},
+'CH3CO': {
+    'description': "CH3CO radical, HCCO cis, Cs symm, 2-A'.",
+    'name': "CH_3CO (2A')",
+    'database': 'G2-2',
+    'enthalpy': -2.4,
+    'ZPE': 26.6070,
+    'thermal correction': 3.0842,
+    'symbols': 'CCHHHO',
+    'magmoms': [ 0.1,  0.6,  0. ,  0. ,  0. ,  0.3],
+    'charges': None,
+    'positions': [[-0.978291, -0.647814,  0.      ],
+                  [ 0.      ,  0.506283,  0.      ],
+                  [-0.455551, -1.607837,  0.      ],
+                  [-1.617626, -0.563271,  0.881061],
+                  [-1.617626, -0.563271, -0.881061],
+                  [ 1.195069,  0.447945,  0.      ]]},
+'H2COH': {
+    'description': "H2COH radical, C1 symm.",
+    'name': "H_2COH (2A)",
+    'database': 'G2-2',
+    'enthalpy': -4.1,
+    'ZPE': 23.1294,
+    'thermal correction': 2.6726,
+    'symbols': 'COHHH',
+    'magmoms': [ 0.7,  0.3,  0. ,  0. ,  0. ],
+    'charges': None,
+    'positions': [[ 0.687448,  0.029626, -0.082014],
+                  [-0.672094, -0.125648,  0.030405],
+                  [-1.09185 ,  0.740282, -0.095167],
+                  [ 1.122783,  0.975263,  0.225993],
+                  [ 1.221131, -0.888116,  0.118015]]},
+'CH3O': {
+    'description': "CH3O radical, Cs symm, 2-A'.",
+    'name': "CH_3O CS (2A')",
+    'database': 'G2-2',
+    'enthalpy': 4.1,
+    'ZPE': 22.4215,
+    'thermal correction': 2.4969,
+    'symbols': 'COHHH',
+    'magmoms': [ 0.,  1.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[-0.008618, -0.586475,  0.      ],
+                  [-0.008618,  0.799541,  0.      ],
+                  [ 1.055363, -0.868756,  0.      ],
+                  [-0.467358, -1.004363,  0.903279],
+                  [-0.467358, -1.004363, -0.903279]]},
+'CH3CH2O': {
+    'description': "CH3CH2O radical, Cs symm, 2-A''.",
+    'name': "CH_3CH_2O (2A'')",
+    'database': 'G2-2',
+    'enthalpy': -3.7,
+    'ZPE': 39.4440,
+    'thermal correction': 3.0158,
+    'symbols': 'CCOHHHHH',
+    'magmoms': [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[  1.00475700e+00,  -5.68263000e-01,   0.00000000e+00],
+                  [  0.00000000e+00,   5.88691000e-01,   0.00000000e+00],
+                  [ -1.26006200e+00,   7.29000000e-04,   0.00000000e+00],
+                  [  1.46956000e-01,   1.20468100e+00,   8.96529000e-01],
+                  [  1.46956000e-01,   1.20468100e+00,  -8.96529000e-01],
+                  [  2.01936300e+00,  -1.64100000e-01,   0.00000000e+00],
+                  [  8.69340000e-01,  -1.18683200e+00,   8.88071000e-01],
+                  [  8.69340000e-01,  -1.18683200e+00,  -8.88071000e-01]]},
+'CH3S': {
+    'description': "CH3S radical, Cs symm, 2-A'.",
+    'name': "CH_3S (2A')",
+    'database': 'G2-2',
+    'enthalpy': 29.8,
+    'ZPE': 21.9415,
+    'thermal correction': 2.6054,
+    'symbols': 'CSHHH',
+    'magmoms': [ 0.,  1.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[-0.003856,  1.106222,  0.      ],
+                  [-0.003856, -0.692579,  0.      ],
+                  [ 1.043269,  1.427057,  0.      ],
+                  [-0.479217,  1.508437,  0.895197],
+                  [-0.479217,  1.508437, -0.895197]]},
+'C2H5': {
+    'description': "C2H5 radical, Staggered, Cs symm, 2-A'.",
+    'name': "C_2H_5 (2A')",
+    'database': 'G2-2',
+    'enthalpy': 28.9,
+    'ZPE': 36.5675,
+    'thermal correction': 3.0942,
+    'symbols': 'CCHHHHH',
+    'magmoms': [ 0.,  1.,  0.,  0.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[-0.014359, -0.694617,  0.      ],
+                  [-0.014359,  0.794473,  0.      ],
+                  [ 1.006101, -1.104042,  0.      ],
+                  [-0.517037, -1.093613,  0.884839],
+                  [-0.517037, -1.093613, -0.884839],
+                  [ 0.100137,  1.346065,  0.923705],
+                  [ 0.100137,  1.346065, -0.923705]]},
+'C3H7': {
+    'description': "(CH3)2CH radical, Cs symm, 2-A'.",
+    'name': "(CH_3)_2CH (2A')",
+    'database': 'G2-2',
+    'enthalpy': 21.5,
+    'ZPE': 54.2928,
+    'thermal correction': 3.8435,
+    'symbols': 'CCCHHHHHHH',
+    'magmoms': [ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.014223,  0.54385 ,  0.      ],
+                  [ 0.014223, -0.199742,  1.291572],
+                  [ 0.014223, -0.199742, -1.291572],
+                  [-0.32289 ,  1.575329,  0.      ],
+                  [ 0.221417,  0.459174,  2.138477],
+                  [ 0.221417,  0.459174, -2.138477],
+                  [-0.955157, -0.684629,  1.484633],
+                  [ 0.767181, -0.995308,  1.286239],
+                  [ 0.767181, -0.995308, -1.286239],
+                  [-0.955157, -0.684629, -1.484633]]},
+'C3H9C': {
+    'description': "t-Butyl radical, (CH3)3C, C3v symm.",
+    'name': "(CH_3)_3C (t-butyl radical)",
+    'database': 'G2-2',
+    'enthalpy': 12.3,
+    'ZPE': 71.7833,
+    'thermal correction': 4.6662,
+    'symbols': 'CCCCHHHHHHHHH',
+    'magmoms': [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.191929],
+                  [ 0.      ,  1.478187, -0.020866],
+                  [ 1.280147, -0.739093, -0.020866],
+                  [-1.280147, -0.739093, -0.020866],
+                  [ 0.      ,  1.731496, -1.093792],
+                  [-0.887043,  1.945769,  0.417565],
+                  [ 0.887043,  1.945769,  0.417565],
+                  [ 1.49952 , -0.865748, -1.093792],
+                  [ 2.128607, -0.204683,  0.417565],
+                  [ 1.241564, -1.741086,  0.417565],
+                  [-1.49952 , -0.865748, -1.093792],
+                  [-1.241564, -1.741086,  0.417565],
+                  [-2.128607, -0.204683,  0.417565]]},
+'NO2': {
+    'description': "NO2 radical, C2v symm, 2-A1.",
+    'name': "NO_2",
+    'database': 'G2-2',
+    'enthalpy': 7.9,
+    'ZPE': 5.4631,
+    'thermal correction': 2.4366,
+    'symbols': 'NOO',
+    'magmoms': [ 1.,  0.,  0.],
+    'charges': None,
+    'positions': [[ 0.      ,  0.      ,  0.332273],
+                  [ 0.      ,  1.118122, -0.14537 ],
+                  [ 0.      , -1.118122, -0.14537 ]]},
+}
+
+
+# all constituent atoms
+atoms_g22 = []
+for f in data.keys():
+    s = Atoms(symbols=data[f]['symbols'], positions=data[f]['positions'])
+    for a in s:
+        atoms_g22.append(a.symbol)
+# unique atoms
+atoms_g22 = list(set(atoms_g22))
+
+# add remaining atoms from G2_1
+from ase.structure import molecule
+
+from ase.data.g2_1 import data as data1
+
+for a in atoms_g22:
+    if not a in data.keys():
+        data[a] = data1[a]
+
+from ase.data.g2_1 import get_ionization_energy
+from ase.data.g2_1 import get_atomization_energy
diff --git a/ase/data/gmtkn30.py b/ase/data/gmtkn30.py
new file mode 100644
index 0000000..69492a2
--- /dev/null
+++ b/ase/data/gmtkn30.py
@@ -0,0 +1,346 @@
+import os
+import pprint
+import re
+from urllib import urlretrieve
+import zipfile
+import shutil
+
+import datetime
+
+import numpy as np
+
+from ase.units import Bohr
+from ase.atom import Atom
+from ase.atoms import Atoms
+from ase.data import atomic_numbers, chemical_symbols
+
+# databases from http://toc.uni-muenster.de/GMTKN/GMTKN30/GMTKN30main.html
+url_root = 'http://toc.uni-muenster.de/GMTKN/GMTKN30/'
+# we may store all downloaded files locally
+# (a good idea, but need to ask permission from the authors)
+#url_root = './GMTKN30/'
+databases = [
+    'MB08-165', # 180
+    'W4-08', # 111
+    'G21IP', # 71
+    'G21EA', # 50
+    'PA', # 24
+    'SIE11', # 29
+    'BHPERI', # 61
+    'BH76', # 95
+    'RSE43', # 88
+    'O3ADD6', # 9
+    'G2RC', # 47
+    'AL2X', # 14
+    'NBPRC', # 21
+    'ISO34', # 63
+    'ISOL22', # 44
+    'DC9', # 19
+    'DARC', # 22
+    'ALK6', # 13
+    'BSR36', # 38
+    'IDISP', # 13
+    'WATER27', # 30
+    'S22', # 57
+    'ADIM6', # 12
+    'RG6', # 11
+    'HEAVY28', # 38
+    'PCONF', # 11
+    'ACONF', # 18
+    'SCONF', # 19
+    'CYCONF', # 11
+    ]
+
+database_files = {}
+for db in databases:
+    database_files[db] = {
+        'structures': 'strucs/' + db + 'structures.zip',
+        'ref': db + 'ref.html',
+        'module': 'GMTKN30_' + db.replace('-', '_'),
+        }
+    for xc in ['PBE', 'PBE0', 'SVWN']:
+        database_files[db][xc] = 'funcsGMTKN30/' + db + xc + '.html'
+
+def download_file(url, filename, dir='.'):
+    # do not mirror subdirectory structure of url
+    outfile = os.path.join(dir, os.path.basename(filename))
+    urlretrieve(os.path.join(url, filename), outfile)
+    return outfile
+
+def read_charge_filter(s):
+    try:
+        return re.search('\(([-+]\d+)\)', s).group(1)
+    except AttributeError:
+        return False
+
+def read_charge(filename, dir='.'):
+    fh = open(os.path.join(dir, filename), 'rb')
+    lines = filter(read_charge_filter, fh.readlines())
+    charge = []
+    for line in lines:
+        sline = line.split()
+        charge.append((sline[0],
+                       float(re.search('\(([-+]\d+)\)', sline[1]).group(1))))
+    fh.close()
+    return charge
+
+def read_charges(dirname, dir='.'):
+    fullname = os.path.join(dir, dirname)
+    for root, dirs, files in os.walk(fullname):
+        for file in files:
+            if file == 'README': # read charge/number of unpaired electrons file
+                return read_charge(file, dir=root)
+                break
+        else:
+            return []
+
+def read_number_of_unpaired_electrons_filter(s):
+    try:
+        return re.search('\((\d+)\)', s).group(1)
+    except AttributeError:
+        return False
+
+def read_number_of_unpaired_electrons(filename, dir='.'):
+    fh = open(os.path.join(dir, filename), 'rb')
+    lines = filter(read_number_of_unpaired_electrons_filter, fh.readlines())
+    number_of_unpaired_electrons = []
+    for line in lines:
+        sline = line.split()
+        no_unpaired_electrons = float(re.search('\((\d+)\)', sline[1]).group(1))
+        number_of_unpaired_electrons.append((sline[0], no_unpaired_electrons))
+    fh.close()
+    return number_of_unpaired_electrons
+
+def read_numbers_of_unpaired_electrons(dirname, dir='.'):
+    fullname = os.path.join(dir, dirname)
+    for root, dirs, files in os.walk(fullname):
+        for file in files:
+            if file == 'README': # read charge/number of unpaired electrons file
+                return read_number_of_unpaired_electrons(file, dir=root)
+                break
+        else:
+            return []
+
+def read_geometry_filter(s):
+    return (not s.startswith('$'))
+
+def read_geometry(filename, dir='.'):
+    fh = open(os.path.join(dir, filename), 'rb')
+    lines = filter(read_geometry_filter, fh.readlines())
+    # return geometry in ASE format
+    geometry = []
+    for line in lines:
+        sline = line.split()
+        # find chemical symbol (the symbols in the file are lowercase)
+        symbol = sline[-1]
+        for s in chemical_symbols:
+            if symbol == s.lower():
+                symbol = s
+                break
+        geometry.append(Atom(symbol=symbol, position=sline[:-1]))
+    fh.close()
+    atoms = Atoms(geometry)
+    atoms.set_positions(atoms.get_positions()*Bohr) # convert to Angstrom
+    return atoms
+
+def read_structures(dirname, dir='.'):
+    fullname = os.path.join(dir, dirname)
+    geometries = []
+    for root, dirs, files in os.walk(fullname):
+        for file in files:
+            if file != 'README': # skip file
+                geometries.append((file, read_geometry(file, dir=root)))
+    return geometries
+
+def read_html(filename, dir='.'):
+    fh = open(os.path.join(dir, filename), 'rb')
+    table = fh.read()
+    # extract html table: help from David Landis
+    table = table.split('<table')
+    table = table[1]
+    table = table.split('</table')
+    table = table[0]
+    # keep field separator tags
+    table = table.replace('<tr', ' TTRR <')
+    table = table.replace('<td', ' TTDD <')
+    # remove the html tags
+    #table = re.sub('<[^>]+>', '', table) # wrong
+    table = re.sub('<.*?>', '', table)
+    # remove end-of-line
+    table = re.sub('\n', '', table)
+    # split on columns
+    table = table.split('TTRR')
+    csv = []
+    separator = ':' # BHPERI contains chemical names with comas
+    ncompounds = 0
+    for item in table:
+        if item.find('TTDD')!=-1:
+            item = item.strip().replace('TTDD', separator)
+            # remove the first coma
+            item = item[1:]
+            litem = []
+            for f in item.split(separator):
+                fs = f.strip()
+                try:
+                    v = eval(fs)
+                    if fs.isdigit() and str(v) != fs: # e.g. undesirable eval('001') = 1
+                        v = fs   
+                # string: NameError, .*[+-*], etc: SyntaxError
+                except (NameError, SyntaxError):
+                    v = fs
+                litem.append(v)
+            # the number of compounds
+            # (exclude reference value and reaction number and divide by 2)
+            if ncompounds:
+                assert ncompounds == (len(litem)-2)/2, 'Error: number of compounds incorrect for reaction: ' + str(litem[0]) + ' in file: ' + filename
+            ncompounds = (len(litem)-2)/2
+            # set names of unused compounds to empty string
+            for i in range(ncompounds):
+                if litem[1+i] == 0: litem[1+i] = ''
+            # move the reaction identifier to the end of list
+            litem.append(litem.pop(0))
+            csv.append(litem)
+    fh.close()
+    # return the number of compounds per reaction, and the table
+    return ncompounds, csv
+
+def table2reference(ncompounds, table):
+    # convert from format given by read_html
+    reactions = []
+    reference = {}
+    for r in table:
+        reaction_id = r[-1]
+        reference[reaction_id] = r[-2]
+        stoich = []
+        for c in range(ncompounds):
+            if r[c] != '': # only defined compounds
+                # compound names can have spaces around
+                stoich.append((str(r[c]).strip(), r[c+ncompounds]))
+        stoich.append(('reaction_id', reaction_id))
+        reactions.append(stoich)
+    return reference, reactions
+
+def table2results(nsets, table, mode='default'):
+    assert mode in ['default', 'D3']
+    # convert from format given by read_html
+    if mode == 'default':
+        index = 0
+    else:
+        index = nsets
+    reference = {}
+    for r in table[:-3]: # ignore 3 last rows of statistics
+        reaction_id = r[-1]
+        if r[index] != '': # only defined compounds
+            reference[reaction_id] = r[index]
+    return reference
+
+def unzip_file(filename, dir='.'):
+    # unzip contents of filename into dir
+    fh = open(filename, 'rb')
+    z = zipfile.ZipFile(fh)
+    if not os.path.isdir(dir):
+        os.mkdir(dir)
+    for entry in z.namelist():
+        # skip spurious zip inside zip files (in HEAVY28)
+        if entry.find('.zip') == -1:
+            outfile = open(entry, 'wb')
+            outfile.write(z.read(entry))
+            outfile.close()
+    fh.close()
+
+def format_data(database, geometries, no_unpaired_electrons=[], charges=[]):
+    "Return data in the custom format.  "
+    import numpy as np
+    data = {}
+    for geometry in geometries:
+        system = geometry[0]
+        atoms = geometry[1]
+        # find the heaviest atom in the system
+        heaviest = max([a.number for a in atoms])
+        heaviest_index = [a.number for a in atoms].index(heaviest)
+        # find number of unpaired electrons
+        if system in [s[0] for s in no_unpaired_electrons]:
+            magmom = 0
+            for s, m in no_unpaired_electrons:
+                if system == s:
+                    magmom = m
+                    break
+            magmoms = [0.0 for a in atoms]
+            # assume the magnetic moment on the heaviest atom in the system
+            # this is incorrect, but is there a better way to set the magnetic moment?
+            magmoms[heaviest_index] = float(magmom)
+            usemagmoms = np.array(magmoms)
+        else:
+            usemagmoms = None
+        # find charge, put it on the heaviest atom
+        if system in [s[0] for s in charges]:
+            charge = 0
+            for s, c in charges:
+                if system == s:
+                    charge = c
+                    break
+            cs = [0.0 for a in atoms]
+            cs[heaviest_index] = float(charge)
+            usecharges = np.array(cs)
+        else:
+            usecharges = None
+        # populate data
+        data[system] = {
+            'database': database,
+            'name': atoms.get_name(),
+            'symbols': ''.join(atoms.get_chemical_symbols()),
+            'magmoms': usemagmoms, # None or list
+            'charges': usecharges, # None or list
+            'positions': atoms.get_positions(),
+            }
+    return data
+
+def main():
+    import os
+    if not os.path.isdir('GMTKN30/strucs'):
+        os.makedirs('GMTKN30/strucs')
+    #for database in ['G2RC', 'WATER27']:
+    for database in database_files.keys(): # all databases
+        fh = open(database_files[database]['module'].lower() + '.py', 'w')
+        fh.write('# Computer generated code! Hands off!\n')
+        fh.write('# Generated: ' + str(datetime.date.today()) + '\n')
+        fh.write('from numpy import array\n')
+        fh.write('data = ')
+        data = {} # specification of molecules
+        info = {} # reference/calculation info
+        # download structures
+        file = database_files[database]['structures']
+        f = os.path.abspath(download_file(url_root, file, dir='GMTKN30/strucs'))
+        fdir = os.path.splitext(os.path.basename(f))[0]
+        unzip_file(f, dir=fdir)
+        structures = read_structures(fdir)
+        no_unpaired_electrons = read_numbers_of_unpaired_electrons(fdir)
+        charges = read_charges(fdir)
+        # remove temporary directory
+        if os.path.isdir(fdir): shutil.rmtree(fdir)
+        data = format_data(database, structures, no_unpaired_electrons, charges)
+        pprint.pprint(data, stream=fh)
+        fh.write('info = ')
+        # download reference data
+        info = {}
+        file = database_files[database]['ref']
+        f = download_file(url_root, file, dir='GMTKN30')
+        ncompounds, table = read_html(f)
+        # transform table into reactions format
+        reference, reactions = table2reference(ncompounds, table)
+        info['reactions'] = reactions
+        info['reaction energy'] = {}
+        info['reaction energy']['reference'] = reference
+        # download XC results
+        for xc in ['PBE', 'PBE0', 'SVWN']:
+            file = database_files[database][xc]
+            f = download_file(url_root, file, dir='GMTKN30')
+            nsets, table = read_html(f)
+            # transform table into results format
+            reference = table2results(nsets, table)
+            info['reaction energy'][xc] = reference
+        pprint.pprint(info, stream=fh)
+        fh.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/ase/data/molecules.py b/ase/data/molecules.py
new file mode 100644
index 0000000..7341b6d
--- /dev/null
+++ b/ase/data/molecules.py
@@ -0,0 +1,62 @@
+def get_ionization_energy(name, vertical=True):
+    "Deprecated, use ase.data.g2.get_ionization_energy instead."
+    import warnings
+    warnings.warn('ase.data.molecules.get_ionization_energy is deprecated. '
+                  ' Please use from ase.data.g2 import get_ionization_energy' \
+                  ' instead.', DeprecationWarning, stacklevel=2)
+    from ase.data.g2 import get_ionization_energy
+    return get_ionization_energy(name, vertical)
+
+def get_atomization_energy(name):
+    "Deprecated, use ase.data.g2.get_atomization_energy instead."
+    import warnings
+    warnings.warn('ase.data.molecules.get_atomization_energy is deprecated. '
+                  ' Please use from ase.data.g2 import get_atomization_energy' \
+                  ' instead.', DeprecationWarning, stacklevel=2)
+    from ase.data.g2 import get_atomization_energy
+    return get_atomization_energy(name)
+
+def molecule(name, **kwargs):
+    "Deprecated."
+    import warnings
+    warnings.warn('ase.data.molecules.molecule is deprecated. '
+                  'Please use:' \
+                  ' from ase.structure import molecule' \
+                  ' instead.', DeprecationWarning, stacklevel=2)
+    from ase.structure import molecule
+    return molecule(name, **kwargs)
+
+def latex(name):
+    """Convert name to LaTeX"""
+    s = '$'
+    last = False
+    for i in name:
+        if i.isalpha():
+            if not last:
+                s = s + r'\rm{'
+                last = True
+        elif last:
+            s = s + '}'
+            last = False
+        s = s + i
+    if i.isalpha():
+        s = s + '}'
+    s = s.replace(' ', r'\ ') + '$'
+    return s
+
+
+def rest(name):
+    """Convert name to reStructuredText."""
+    s = ''
+    while name:
+        c = name[0]
+        if c == '_':
+            s += r'\ :sub:`%s`\ ' % name[1]
+            name = name[2:]
+        elif c == '^':
+            s += r'\ :sup:`%s`\ ' % name[1]
+            name = name[2:]
+        else:
+            s += c
+            name = name[1:]
+    return s
diff --git a/ase/data/s22.py b/ase/data/s22.py
new file mode 100644
index 0000000..ba3e22a
--- /dev/null
+++ b/ase/data/s22.py
@@ -0,0 +1,3039 @@
+"""
+The following contains the S22 and s26 databases of weakly interacting dimers and complexes
+
+S22 geometry data are from
+P. Jurecka, J. Sponer, J. Cerny, P. Hobza; Phys Chem Chem Phys 2006, 8 (17), 1985-1993.
+See http://www.begdb.com/index.php?action=106a6c241b8797f52e1e77317b96a201 for the original files.
+All geometries are optimized at either the CCSD(T) or MP2 level except for the methyl amide dimers 
+where only the hydrogen position is optimized at the DFT level (the precise optimization is written as a comment).
+
+The S22 interaction energies are all calculated using both CCSD(T)/CBS counter poised corrected (CP) and MP2 /CBS CP.
+The original S22 interaction energies are listed in the above references.
+The S22 energies used here are from
+Takatani, T. et al., J. Chem. Phys., 132, 144104 (2010)
+where a large and more complete basis set has been used for all database members.
+
+The original S22 set has been expanded with an extra 4 single hydrogen bonded complexes.
+The expanded set is called S26. Data for the 4 extra dimers are from
+Riley, K.E., Hobza, P., J. Chem. Phys. A, 111(33), 8257-8263 (2007).
+Geometry optimizations: MP2/cc-pVTZ CP or DFT TPSS/TZVP noCP
+Interaction energies: CCSD(T)/CBS CP or MP2/cc-pVDZ CP
+
+The original s22 has also been expanded with 4 non-equilibrium structures for each system.
+This defines the s22x5 database containing one shortened and three elongated structures:
+0.9, 1.0, 1.2, 1.5 and 2.0 times the original intermolecular distance.
+CCSD(T)/CBS interaction energies are consistent with the original s22 work.
+Reference: L. Grafova, M. Pitonak, P. Hobza, J. Chem. Theo. Comput., 2010, ASAP article.
+"""
+
+from ase.atoms import Atoms
+
+s22 = ['Ammonia_dimer','Water_dimer','Formic_acid_dimer','Formamide_dimer',
+'Uracil_dimer_h-bonded','2-pyridoxine_2-aminopyridine_complex',
+'Adenine-thymine_Watson-Crick_complex','Methane_dimer','Ethene_dimer',
+'Benzene-methane_complex','Benzene_dimer_parallel_displaced','Pyrazine_dimer',
+'Uracil_dimer_stack','Indole-benzene_complex_stack',
+'Adenine-thymine_complex_stack','Ethene-ethyne_complex','Benzene-water_complex',
+'Benzene-ammonia_complex','Benzene-HCN_complex','Benzene_dimer_T-shaped',
+'Indole-benzene_T-shape_complex','Phenol_dimer']
+
+s26 = s22 + ['Methanol_dimer','Methanol-formaldehyde_complex',
+'Methyl_amide_dimer_alpha','Methyl_amide_dimer_beta'] 
+
+s22x5 = ['Ammonia_dimer_0.9','Ammonia_dimer_1.0','Ammonia_dimer_1.2','Ammonia_dimer_1.5','Ammonia_dimer_2.0',
+'Water_dimer_0.9','Water_dimer_1.0','Water_dimer_1.2','Water_dimer_1.5','Water_dimer_2.0',
+'Formic_acid_dimer_0.9','Formic_acid_dimer_1.0','Formic_acid_dimer_1.2','Formic_acid_dimer_1.5','Formic_acid_dimer_2.0',
+'Formamide_dimer_0.9','Formamide_dimer_1.0','Formamide_dimer_1.2','Formamide_dimer_1.5','Formamide_dimer_2.0',
+'Uracil_dimer_h-bonded_0.9','Uracil_dimer_h-bonded_1.0','Uracil_dimer_h-bonded_1.2','Uracil_dimer_h-bonded_1.5','Uracil_dimer_h-bonded_2.0',
+'2-pyridoxine_2-aminopyridine_complex_0.9','2-pyridoxine_2-aminopyridine_complex_1.0',
+    '2-pyridoxine_2-aminopyridine_complex_1.2','2-pyridoxine_2-aminopyridine_complex_1.5','2-pyridoxine_2-aminopyridine_complex_2.0',
+'Adenine-thymine_Watson-Crick_complex_0.9','Adenine-thymine_Watson-Crick_complex_1.0',
+    'Adenine-thymine_Watson-Crick_complex_1.2','Adenine-thymine_Watson-Crick_complex_1.5','Adenine-thymine_Watson-Crick_complex_2.0',
+'Methane_dimer_0.9','Methane_dimer_1.0','Methane_dimer_1.2','Methane_dimer_1.5','Methane_dimer_2.0',
+'Ethene_dimer_0.9','Ethene_dimer_1.0','Ethene_dimer_1.2','Ethene_dimer_1.5','Ethene_dimer_2.0',
+'Benzene-methane_complex_0.9','Benzene-methane_complex_1.0','Benzene-methane_complex_1.2','Benzene-methane_complex_1.5','Benzene-methane_complex_2.0',
+'Benzene_dimer_parallel_displaced_0.9','Benzene_dimer_parallel_displaced_1.0',
+    'Benzene_dimer_parallel_displaced_1.2','Benzene_dimer_parallel_displaced_1.5','Benzene_dimer_parallel_displaced_2.0',
+'Pyrazine_dimer_0.9','Pyrazine_dimer_1.0','Pyrazine_dimer_1.2','Pyrazine_dimer_1.5','Pyrazine_dimer_2.0',
+'Uracil_dimer_stack_0.9','Uracil_dimer_stack_1.0','Uracil_dimer_stack_1.2','Uracil_dimer_stack_1.5','Uracil_dimer_stack_2.0',
+'Indole-benzene_complex_stack_0.9','Indole-benzene_complex_stack_1.0',
+    'Indole-benzene_complex_stack_1.2','Indole-benzene_complex_stack_1.5','Indole-benzene_complex_stack_2.0',
+'Adenine-thymine_complex_stack_0.9','Adenine-thymine_complex_stack_1.0',
+    'Adenine-thymine_complex_stack_1.2','Adenine-thymine_complex_stack_1.5','Adenine-thymine_complex_stack_2.0',
+'Ethene-ethyne_complex_0.9','Ethene-ethyne_complex_1.0','Ethene-ethyne_complex_1.2','Ethene-ethyne_complex_1.5','Ethene-ethyne_complex_2.0',
+'Benzene-water_complex_0.9','Benzene-water_complex_1.0','Benzene-water_complex_1.2','Benzene-water_complex_1.5','Benzene-water_complex_2.0',
+'Benzene-ammonia_complex_0.9','Benzene-ammonia_complex_1.0','Benzene-ammonia_complex_1.2','Benzene-ammonia_complex_1.5','Benzene-ammonia_complex_2.0',
+'Benzene-HCN_complex_0.9','Benzene-HCN_complex_1.0','Benzene-HCN_complex_1.2','Benzene-HCN_complex_1.5','Benzene-HCN_complex_2.0',
+'Benzene_dimer_T-shaped_0.9','Benzene_dimer_T-shaped_1.0','Benzene_dimer_T-shaped_1.2','Benzene_dimer_T-shaped_1.5','Benzene_dimer_T-shaped_2.0',
+'Indole-benzene_T-shape_complex_0.9','Indole-benzene_T-shape_complex_1.0',
+    'Indole-benzene_T-shape_complex_1.2','Indole-benzene_T-shape_complex_1.5','Indole-benzene_T-shape_complex_2.0',
+'Phenol_dimer_0.9','Phenol_dimer_1.0','Phenol_dimer_1.2','Phenol_dimer_1.5','Phenol_dimer_2.0']
+
+data = {
+# --- s22 and s22x5 ---#
+'2-pyridoxine_2-aminopyridine_complex': {
+    'description': "Complex, S22, S26, 2 h-bond, double h-bond, nucleic base model",
+    'name': "2-pyridoxine_2-aminopyridine_complex",
+    's26_number': "06",
+    'interaction energy CC': -0.7372,
+    'interaction energies s22x5': [-0.6561,-0.7242,-0.6041,-0.3547,-0.1414],
+    'offset': 0.0130,
+    'symbols': 'ONCCCCCHHHHHNCCCCCHHHHNHH',
+    'magmoms': None,
+    'dimer atoms': [12,13],
+    # Optimisation level:  MP2/cc-pVTZ 
+    'positions':[[ -1.3976213, -1.8858368, -0.3673061],
+                 [ -1.4642550,  0.3641828,  0.0192301],
+                 [ -4.1857398,  0.3696669,  0.0360960],
+                 [ -3.4832598,  1.5783111,  0.2500752],
+                 [ -2.1179502,  1.5307048,  0.2338383],
+                 [ -2.0773833, -0.8637492, -0.1899414],
+                 [ -3.5156032, -0.8051950, -0.1757585],
+                 [ -5.2678045,  0.3707428,  0.0411419],
+                 [ -3.9920334,  2.5127560,  0.4214414],
+                 [ -1.4929196,  2.3984096,  0.3885018],
+                 [ -4.0401226, -1.7348452, -0.3379269],
+                 [ -0.4265266,  0.3612127,  0.0073538],
+                 [  1.4327616,  0.3639703, -0.0159508],
+                 [  2.1154200, -0.7803450,  0.1681099],
+                 [  3.5237586, -0.8016096,  0.1545027],
+                 [  4.2185897,  0.3735783, -0.0525929],
+                 [  3.5099708,  1.5615014, -0.2449763],
+                 [  2.1280138,  1.4953324, -0.2175374],
+                 [  4.0459206, -1.7361356,  0.3076883],
+                 [  5.2999426,  0.3666009, -0.0663349],
+                 [  4.0110923,  2.5024313, -0.4130052],
+                 [  1.5339878,  2.3893837, -0.3670565],
+                 [  1.3883123, -1.9083038,  0.4198149],
+                 [  1.8694714, -2.7812773,  0.2940385],
+                 [  0.4089067, -1.9079942,  0.1300860]],
+    'positions 0.9':[[  -0.969652624 ,  -2.245611164 ,  -0.386822525 ],
+                     [  -1.037789793 ,  0.004508753 ,  -0.001131127 ],
+                     [  -3.759261297 ,  0.014028068 ,  -0.018375760 ],
+                     [  -3.057727058 ,  1.221631156 ,  0.204402100 ],
+                     [  -1.692392879 ,  1.172000703 ,  0.205277859 ],
+                     [  -1.650068007 ,  -1.222514751 ,  -0.217981663 ],
+                     [  -3.088264390 ,  -1.161828225 ,  -0.221825966 ],
+                     [  -4.841300764 ,  0.016708498 ,  -0.026892047 ],
+                     [  -3.567221821 ,  2.156831083 ,  0.369386687 ],
+                     [  -1.068064568 ,  2.038779450 ,  0.367771502 ],
+                     [  -3.612088503 ,  -2.090701001 ,  -0.390563867 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  1.673493386 ,  0.000000000 ,  0.000000000 ],
+                     [  2.352093429 ,  -1.145324213 ,  0.192591910 ],
+                     [  3.760459273 ,  -1.168677470 ,  0.196637005 ],
+                     [  4.459573002 ,  0.005477083 ,  -0.001723239 ],
+                     [  3.755182987 ,  1.194447664 ,  -0.202961469 ],
+                     [  2.372894041 ,  1.130328028 ,  -0.192845808 ],
+                     [  4.279274134 ,  -2.103975233 ,  0.356345736 ],
+                     [  5.541001766 ,  -0.003103367 ,  -0.001911235 ],
+                     [  4.259765167 ,  2.134632052 ,  -0.364687797 ],
+                     [  1.782114958 ,  2.025258423 ,  -0.349790900 ],
+                     [  1.620216197 ,  -2.272201547 ,  0.435153550 ],
+                     [  2.101618920 ,  -3.145888174 ,  0.315408858 ],
+                     [  0.644520940 ,  -2.270442069 ,  0.133172072 ]],
+    'positions 1.0':[[  -0.969652624000000 ,  -2.245611164000000 ,  -0.386822525000000 ],
+                     [  -1.037789793000000 ,  0.004508753000000 ,  -0.001131127000000 ],
+                     [  -3.759261297000000 ,  0.014028068000000 ,  -0.018375760000000 ],
+                     [  -3.057727058000000 ,  1.221631156000000 ,  0.204402100000000 ],
+                     [  -1.692392879000000 ,  1.172000703000000 ,  0.205277859000000 ],
+                     [  -1.650068007000000 ,  -1.222514751000000 ,  -0.217981663000000 ],
+                     [  -3.088264390000000 ,  -1.161828225000000 ,  -0.221825966000000 ],
+                     [  -4.841300764000000 ,  0.016708498000000 ,  -0.026892047000000 ],
+                     [  -3.567221821000000 ,  2.156831083000000 ,  0.369386687000000 ],
+                     [  -1.068064568000000 ,  2.038779450000000 ,  0.367771502000000 ],
+                     [  -3.612088503000000 ,  -2.090701001000000 ,  -0.390563867000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  1.859437095454546 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.538037138454545 ,  -1.145324213000000 ,  0.192591910000000 ],
+                     [  3.946402982454545 ,  -1.168677470000000 ,  0.196637005000000 ],
+                     [  4.645516711454546 ,  0.005477083000000 ,  -0.001723239000000 ],
+                     [  3.941126696454545 ,  1.194447664000000 ,  -0.202961469000000 ],
+                     [  2.558837750454545 ,  1.130328028000000 ,  -0.192845808000000 ],
+                     [  4.465217843454545 ,  -2.103975233000000 ,  0.356345736000000 ],
+                     [  5.726945475454546 ,  -0.003103367000000 ,  -0.001911235000000 ],
+                     [  4.445708876454546 ,  2.134632052000000 ,  -0.364687797000000 ],
+                     [  1.968058667454545 ,  2.025258423000000 ,  -0.349790900000000 ],
+                     [  1.806159906454545 ,  -2.272201547000000 ,  0.435153550000000 ],
+                     [  2.287562629454545 ,  -3.145888174000000 ,  0.315408858000000 ],
+                     [  0.830464649454546 ,  -2.270442069000000 ,  0.133172072000000 ]],
+    'positions 1.2':[[  -0.969652624 ,  -2.245611164 ,  -0.386822525 ],
+                     [  -1.037789793 ,  0.004508753 ,  -0.001131127 ],
+                     [  -3.759261297 ,  0.014028068 ,  -0.018375760 ],
+                     [  -3.057727058 ,  1.221631156 ,  0.204402100 ],
+                     [  -1.692392879 ,  1.172000703 ,  0.205277859 ],
+                     [  -1.650068007 ,  -1.222514751 ,  -0.217981663 ],
+                     [  -3.088264390 ,  -1.161828225 ,  -0.221825966 ],
+                     [  -4.841300764 ,  0.016708498 ,  -0.026892047 ],
+                     [  -3.567221821 ,  2.156831083 ,  0.369386687 ],
+                     [  -1.068064568 ,  2.038779450 ,  0.367771502 ],
+                     [  -3.612088503 ,  -2.090701001 ,  -0.390563867 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.231324514 ,  0.000000000 ,  0.000000000 ],
+                     [  2.909924557 ,  -1.145324213 ,  0.192591910 ],
+                     [  4.318290401 ,  -1.168677470 ,  0.196637005 ],
+                     [  5.017404130 ,  0.005477083 ,  -0.001723239 ],
+                     [  4.313014115 ,  1.194447664 ,  -0.202961469 ],
+                     [  2.930725169 ,  1.130328028 ,  -0.192845808 ],
+                     [  4.837105262 ,  -2.103975233 ,  0.356345736 ],
+                     [  6.098832894 ,  -0.003103367 ,  -0.001911235 ],
+                     [  4.817596295 ,  2.134632052 ,  -0.364687797 ],
+                     [  2.339946086 ,  2.025258423 ,  -0.349790900 ],
+                     [  2.178047325 ,  -2.272201547 ,  0.435153550 ],
+                     [  2.659450048 ,  -3.145888174 ,  0.315408858 ],
+                     [  1.202352068 ,  -2.270442069 ,  0.133172072 ]],
+    'positions 1.5':[[  -0.969652624 ,  -2.245611164 ,  -0.386822525 ],
+                     [  -1.037789793 ,  0.004508753 ,  -0.001131127 ],
+                     [  -3.759261297 ,  0.014028068 ,  -0.018375760 ],
+                     [  -3.057727058 ,  1.221631156 ,  0.204402100 ],
+                     [  -1.692392879 ,  1.172000703 ,  0.205277859 ],
+                     [  -1.650068007 ,  -1.222514751 ,  -0.217981663 ],
+                     [  -3.088264390 ,  -1.161828225 ,  -0.221825966 ],
+                     [  -4.841300764 ,  0.016708498 ,  -0.026892047 ],
+                     [  -3.567221821 ,  2.156831083 ,  0.369386687 ],
+                     [  -1.068064568 ,  2.038779450 ,  0.367771502 ],
+                     [  -3.612088503 ,  -2.090701001 ,  -0.390563867 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.789155642 ,  0.000000000 ,  0.000000000 ],
+                     [  3.467755685 ,  -1.145324213 ,  0.192591910 ],
+                     [  4.876121529 ,  -1.168677470 ,  0.196637005 ],
+                     [  5.575235258 ,  0.005477083 ,  -0.001723239 ],
+                     [  4.870845243 ,  1.194447664 ,  -0.202961469 ],
+                     [  3.488556297 ,  1.130328028 ,  -0.192845808 ],
+                     [  5.394936390 ,  -2.103975233 ,  0.356345736 ],
+                     [  6.656664022 ,  -0.003103367 ,  -0.001911235 ],
+                     [  5.375427423 ,  2.134632052 ,  -0.364687797 ],
+                     [  2.897777214 ,  2.025258423 ,  -0.349790900 ],
+                     [  2.735878453 ,  -2.272201547 ,  0.435153550 ],
+                     [  3.217281176 ,  -3.145888174 ,  0.315408858 ],
+                     [  1.760183196 ,  -2.270442069 ,  0.133172072 ]],
+    'positions 2.0':[[  -0.969652624 ,  -2.245611164 ,  -0.386822525 ],
+                     [  -1.037789793 ,  0.004508753 ,  -0.001131127 ],
+                     [  -3.759261297 ,  0.014028068 ,  -0.018375760 ],
+                     [  -3.057727058 ,  1.221631156 ,  0.204402100 ],
+                     [  -1.692392879 ,  1.172000703 ,  0.205277859 ],
+                     [  -1.650068007 ,  -1.222514751 ,  -0.217981663 ],
+                     [  -3.088264390 ,  -1.161828225 ,  -0.221825966 ],
+                     [  -4.841300764 ,  0.016708498 ,  -0.026892047 ],
+                     [  -3.567221821 ,  2.156831083 ,  0.369386687 ],
+                     [  -1.068064568 ,  2.038779450 ,  0.367771502 ],
+                     [  -3.612088503 ,  -2.090701001 ,  -0.390563867 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  3.718874190 ,  0.000000000 ,  0.000000000 ],
+                     [  4.397474233 ,  -1.145324213 ,  0.192591910 ],
+                     [  5.805840077 ,  -1.168677470 ,  0.196637005 ],
+                     [  6.504953806 ,  0.005477083 ,  -0.001723239 ],
+                     [  5.800563791 ,  1.194447664 ,  -0.202961469 ],
+                     [  4.418274845 ,  1.130328028 ,  -0.192845808 ],
+                     [  6.324654938 ,  -2.103975233 ,  0.356345736 ],
+                     [  7.586382570 ,  -0.003103367 ,  -0.001911235 ],
+                     [  6.305145971 ,  2.134632052 ,  -0.364687797 ],
+                     [  3.827495762 ,  2.025258423 ,  -0.349790900 ],
+                     [  3.665597001 ,  -2.272201547 ,  0.435153550 ],
+                     [  4.146999724 ,  -3.145888174 ,  0.315408858 ],
+                     [  2.689901744 ,  -2.270442069 ,  0.133172072 ]]},
+
+'Adenine-thymine_complex_stack': {
+    'description': "Complex, S22, S26, stack, dispersion bonded, nucleic base",
+    'name': "Adenine-thymine_complex_stack",
+    's26_number': "15",
+    'interaction energy CC': -0.5056,
+    'interaction energies s22x5':[-0.3465,-0.5299,-0.3569,-0.1409,-0.0399],
+    'offset': -0.0243,
+    'symbols': 'NCHNCCNHHNCHNCHNCHCCHHHCONHCOH',
+    'magmoms': None,
+    'dimer atoms': [15,15],
+    # Optimisation level:  MP2/cc-pVTZ
+    'positions':[[  0.2793014,  2.4068393, -0.6057517],
+                 [ -1.0848570,  2.4457461, -0.5511608],
+                 [ -1.6594403,  3.0230294, -1.2560905],
+                 [ -1.5977117,  1.7179877,  0.4287543],
+                 [ -0.4897255,  1.1714358,  1.0301910],
+                 [ -0.3461366,  0.2914710,  2.1172343],
+                 [ -1.4187090, -0.1677767,  2.8101441],
+                 [ -1.2388750, -0.9594802,  3.4047578],
+                 [ -2.2918734, -0.1788223,  2.3073619],
+                 [  0.8857630, -0.0700763,  2.4919494],
+                 [  1.9352348,  0.4072878,  1.7968022],
+                 [  2.9060330,  0.0788414,  2.1458181],
+                 [  1.9409775,  1.2242019,  0.7402202],
+                 [  0.6952186,  1.5779858,  0.4063984],
+                 [  0.8610073,  2.8298045, -1.3104502],
+                 [  1.2754606, -0.6478993, -1.9779104],
+                 [  1.4130533, -1.5536850, -0.9550667],
+                 [  2.4258769, -1.8670780, -0.7468778],
+                 [  0.3575976, -2.0239499, -0.2530575],
+                 [  0.4821292, -3.0179494,  0.8521221],
+                 [  0.1757705, -2.5756065,  1.7986281],
+                 [ -0.1601691, -3.8770412,  0.6639498],
+                 [  1.5112443, -3.3572767,  0.9513659],
+                 [ -0.9684711, -1.5298112, -0.5939792],
+                 [ -2.0029280, -1.8396957, -0.0199453],
+                 [ -0.9956916, -0.6383870, -1.6720420],
+                 [ -1.9014057, -0.2501720, -1.8985760],
+                 [  0.0684702, -0.1191762, -2.3763759],
+                 [ -0.0397875,  0.7227006, -3.2531083],
+                 [  2.0853289, -0.2760176, -2.4454577]],
+    'positions 0.9':[[  0.067390759 ,  1.213806097 ,  -1.171192513 ],
+                     [  -0.034440687 ,  0.160916029 ,  -2.035179690 ],
+                     [  -0.037909102 ,  0.307694674 ,  -3.102311444 ],
+                     [  -0.122286497 ,  -1.014214485 ,  -1.431659388 ],
+                     [  -0.061278153 ,  -0.690156063 ,  -0.097738525 ],
+                     [  -0.083866474 ,  -1.480006435 ,  1.065121981 ],
+                     [  -0.207551291 ,  -2.830167865 ,  1.008466281 ],
+                     [  0.020236002 ,  -3.318294510 ,  1.858492777 ],
+                     [  0.100823981 ,  -3.261839820 ,  0.151791829 ],
+                     [  -0.015107287 ,  -0.872886238 ,  2.254820437 ],
+                     [  0.095534438 ,  0.468473589 ,  2.286592142 ],
+                     [  0.148443656 ,  0.902433537 ,  3.277055537 ],
+                     [  0.150791629 ,  1.330817541 ,  1.268232413 ],
+                     [  0.061278153 ,  0.690156063 ,  0.097738525 ],
+                     [  0.213123816 ,  2.178532043 ,  -1.420082564 ],
+                     [  2.995457244 ,  1.318912569 ,  0.115169333 ],
+                     [  3.033773997 ,  0.544134785 ,  1.248235461 ],
+                     [  3.166936649 ,  1.084216460 ,  2.174491246 ],
+                     [  2.913123372 ,  -0.802036026 ,  1.213306349 ],
+                     [  2.965573998 ,  -1.664227788 ,  2.429380731 ],
+                     [  2.009790775 ,  -2.161867438 ,  2.585037720 ],
+                     [  3.726416066 ,  -2.435033978 ,  2.315487569 ],
+                     [  3.189128467 ,  -1.070628980 ,  3.313538183 ],
+                     [  2.718644614 ,  -1.440326451 ,  -0.080379664 ],
+                     [  2.558245305 ,  -2.640081851 ,  -0.255033817 ],
+                     [  2.729839539 ,  -0.560837886 ,  -1.168484485 ],
+                     [  2.554150647 ,  -0.977998743 ,  -2.072617562 ],
+                     [  2.814781928 ,  0.814169728 ,  -1.152798148 ],
+                     [  2.732113465 ,  1.513854058 ,  -2.149163262 ],
+                     [  3.033823338 ,  2.322516737 ,  0.179118562 ]],
+    'positions 1.0':[[  0.067390759000000 ,  1.213806097000000 ,  -1.171192513000000 ],
+                     [  -0.034440687000000 ,  0.160916029000000 ,  -2.035179690000000 ],
+                     [  -0.037909102000000 ,  0.307694674000000 ,  -3.102311444000000 ],
+                     [  -0.122286497000000 ,  -1.014214485000000 ,  -1.431659388000000 ],
+                     [  -0.061278153000000 ,  -0.690156063000000 ,  -0.097738525000000 ],
+                     [  -0.083866474000000 ,  -1.480006435000000 ,  1.065121981000000 ],
+                     [  -0.207551291000000 ,  -2.830167865000000 ,  1.008466281000000 ],
+                     [  0.020236002000000 ,  -3.318294510000000 ,  1.858492777000000 ],
+                     [  0.100823981000000 ,  -3.261839820000000 ,  0.151791829000000 ],
+                     [  -0.015107287000000 ,  -0.872886238000000 ,  2.254820437000000 ],
+                     [  0.095534438000000 ,  0.468473589000000 ,  2.286592142000000 ],
+                     [  0.148443656000000 ,  0.902433537000000 ,  3.277055537000000 ],
+                     [  0.150791629000000 ,  1.330817541000000 ,  1.268232413000000 ],
+                     [  0.061278153000000 ,  0.690156063000000 ,  0.097738525000000 ],
+                     [  0.213123816000000 ,  2.178532043000000 ,  -1.420082564000000 ],
+                     [  3.314050951181818 ,  1.318912569000000 ,  0.115169333000000 ],
+                     [  3.352367704181818 ,  0.544134785000000 ,  1.248235461000000 ],
+                     [  3.485530356181818 ,  1.084216460000000 ,  2.174491246000000 ],
+                     [  3.231717079181818 ,  -0.802036026000000 ,  1.213306349000000 ],
+                     [  3.284167705181818 ,  -1.664227788000000 ,  2.429380731000000 ],
+                     [  2.328384482181818 ,  -2.161867438000000 ,  2.585037720000000 ],
+                     [  4.045009773181818 ,  -2.435033978000000 ,  2.315487569000000 ],
+                     [  3.507722174181819 ,  -1.070628980000000 ,  3.313538183000000 ],
+                     [  3.037238321181818 ,  -1.440326451000000 ,  -0.080379664000000 ],
+                     [  2.876839012181818 ,  -2.640081851000000 ,  -0.255033817000000 ],
+                     [  3.048433246181818 ,  -0.560837886000000 ,  -1.168484485000000 ],
+                     [  2.872744354181818 ,  -0.977998743000000 ,  -2.072617562000000 ],
+                     [  3.133375635181818 ,  0.814169728000000 ,  -1.152798148000000 ],
+                     [  3.050707172181818 ,  1.513854058000000 ,  -2.149163262000000 ],
+                     [  3.352417045181818 ,  2.322516737000000 ,  0.179118562000000 ]],
+    'positions 1.2':[[  0.067390759 ,  1.213806097 ,  -1.171192513 ],
+                     [  -0.034440687 ,  0.160916029 ,  -2.035179690 ],
+                     [  -0.037909102 ,  0.307694674 ,  -3.102311444 ],
+                     [  -0.122286497 ,  -1.014214485 ,  -1.431659388 ],
+                     [  -0.061278153 ,  -0.690156063 ,  -0.097738525 ],
+                     [  -0.083866474 ,  -1.480006435 ,  1.065121981 ],
+                     [  -0.207551291 ,  -2.830167865 ,  1.008466281 ],
+                     [  0.020236002 ,  -3.318294510 ,  1.858492777 ],
+                     [  0.100823981 ,  -3.261839820 ,  0.151791829 ],
+                     [  -0.015107287 ,  -0.872886238 ,  2.254820437 ],
+                     [  0.095534438 ,  0.468473589 ,  2.286592142 ],
+                     [  0.148443656 ,  0.902433537 ,  3.277055537 ],
+                     [  0.150791629 ,  1.330817541 ,  1.268232413 ],
+                     [  0.061278153 ,  0.690156063 ,  0.097738525 ],
+                     [  0.213123816 ,  2.178532043 ,  -1.420082564 ],
+                     [  3.951238365 ,  1.318912569 ,  0.115169333 ],
+                     [  3.989555118 ,  0.544134785 ,  1.248235461 ],
+                     [  4.122717770 ,  1.084216460 ,  2.174491246 ],
+                     [  3.868904493 ,  -0.802036026 ,  1.213306349 ],
+                     [  3.921355119 ,  -1.664227788 ,  2.429380731 ],
+                     [  2.965571896 ,  -2.161867438 ,  2.585037720 ],
+                     [  4.682197187 ,  -2.435033978 ,  2.315487569 ],
+                     [  4.144909588 ,  -1.070628980 ,  3.313538183 ],
+                     [  3.674425735 ,  -1.440326451 ,  -0.080379664 ],
+                     [  3.514026426 ,  -2.640081851 ,  -0.255033817 ],
+                     [  3.685620660 ,  -0.560837886 ,  -1.168484485 ],
+                     [  3.509931768 ,  -0.977998743 ,  -2.072617562 ],
+                     [  3.770563049 ,  0.814169728 ,  -1.152798148 ],
+                     [  3.687894586 ,  1.513854058 ,  -2.149163262 ],
+                     [  3.989604459 ,  2.322516737 ,  0.179118562 ]],
+    'positions 1.5':[[  0.067390759 ,  1.213806097 ,  -1.171192513 ],
+                     [  -0.034440687 ,  0.160916029 ,  -2.035179690 ],
+                     [  -0.037909102 ,  0.307694674 ,  -3.102311444 ],
+                     [  -0.122286497 ,  -1.014214485 ,  -1.431659388 ],
+                     [  -0.061278153 ,  -0.690156063 ,  -0.097738525 ],
+                     [  -0.083866474 ,  -1.480006435 ,  1.065121981 ],
+                     [  -0.207551291 ,  -2.830167865 ,  1.008466281 ],
+                     [  0.020236002 ,  -3.318294510 ,  1.858492777 ],
+                     [  0.100823981 ,  -3.261839820 ,  0.151791829 ],
+                     [  -0.015107287 ,  -0.872886238 ,  2.254820437 ],
+                     [  0.095534438 ,  0.468473589 ,  2.286592142 ],
+                     [  0.148443656 ,  0.902433537 ,  3.277055537 ],
+                     [  0.150791629 ,  1.330817541 ,  1.268232413 ],
+                     [  0.061278153 ,  0.690156063 ,  0.097738525 ],
+                     [  0.213123816 ,  2.178532043 ,  -1.420082564 ],
+                     [  4.907019487 ,  1.318912569 ,  0.115169333 ],
+                     [  4.945336240 ,  0.544134785 ,  1.248235461 ],
+                     [  5.078498892 ,  1.084216460 ,  2.174491246 ],
+                     [  4.824685615 ,  -0.802036026 ,  1.213306349 ],
+                     [  4.877136241 ,  -1.664227788 ,  2.429380731 ],
+                     [  3.921353018 ,  -2.161867438 ,  2.585037720 ],
+                     [  5.637978309 ,  -2.435033978 ,  2.315487569 ],
+                     [  5.100690710 ,  -1.070628980 ,  3.313538183 ],
+                     [  4.630206857 ,  -1.440326451 ,  -0.080379664 ],
+                     [  4.469807548 ,  -2.640081851 ,  -0.255033817 ],
+                     [  4.641401782 ,  -0.560837886 ,  -1.168484485 ],
+                     [  4.465712890 ,  -0.977998743 ,  -2.072617562 ],
+                     [  4.726344171 ,  0.814169728 ,  -1.152798148 ],
+                     [  4.643675708 ,  1.513854058 ,  -2.149163262 ],
+                     [  4.945385581 ,  2.322516737 ,  0.179118562 ]],
+    'positions 2.0':[[  0.067390759 ,  1.213806097 ,  -1.171192513 ],
+                     [  -0.034440687 ,  0.160916029 ,  -2.035179690 ],
+                     [  -0.037909102 ,  0.307694674 ,  -3.102311444 ],
+                     [  -0.122286497 ,  -1.014214485 ,  -1.431659388 ],
+                     [  -0.061278153 ,  -0.690156063 ,  -0.097738525 ],
+                     [  -0.083866474 ,  -1.480006435 ,  1.065121981 ],
+                     [  -0.207551291 ,  -2.830167865 ,  1.008466281 ],
+                     [  0.020236002 ,  -3.318294510 ,  1.858492777 ],
+                     [  0.100823981 ,  -3.261839820 ,  0.151791829 ],
+                     [  -0.015107287 ,  -0.872886238 ,  2.254820437 ],
+                     [  0.095534438 ,  0.468473589 ,  2.286592142 ],
+                     [  0.148443656 ,  0.902433537 ,  3.277055537 ],
+                     [  0.150791629 ,  1.330817541 ,  1.268232413 ],
+                     [  0.061278153 ,  0.690156063 ,  0.097738525 ],
+                     [  0.213123816 ,  2.178532043 ,  -1.420082564 ],
+                     [  6.499988023 ,  1.318912569 ,  0.115169333 ],
+                     [  6.538304776 ,  0.544134785 ,  1.248235461 ],
+                     [  6.671467428 ,  1.084216460 ,  2.174491246 ],
+                     [  6.417654151 ,  -0.802036026 ,  1.213306349 ],
+                     [  6.470104777 ,  -1.664227788 ,  2.429380731 ],
+                     [  5.514321554 ,  -2.161867438 ,  2.585037720 ],
+                     [  7.230946845 ,  -2.435033978 ,  2.315487569 ],
+                     [  6.693659246 ,  -1.070628980 ,  3.313538183 ],
+                     [  6.223175393 ,  -1.440326451 ,  -0.080379664 ],
+                     [  6.062776084 ,  -2.640081851 ,  -0.255033817 ],
+                     [  6.234370318 ,  -0.560837886 ,  -1.168484485 ],
+                     [  6.058681426 ,  -0.977998743 ,  -2.072617562 ],
+                     [  6.319312707 ,  0.814169728 ,  -1.152798148 ],
+                     [  6.236644244 ,  1.513854058 ,  -2.149163262 ],
+                     [  6.538354117 ,  2.322516737 ,  0.179118562 ]]},
+
+'Adenine-thymine_Watson-Crick_complex': {
+    'description': "Complex, S22, S26, 2 h-bonds, double h-bond, nucleic base",
+    'name': "Adenine-thymine_Watson-Crick_complex",
+    's26_number': "07",
+    'interaction energy CC':-0.7259,
+    'interaction energies s22x5':[-0.6513,-0.7099,-0.5767,-0.3222,-0.1123],
+    'offset': 0.0160,
+    'symbols': 'NCCCNCNCNNHHHHHNCCCNCCOOHHHHHH',
+    'magmoms': None,
+    'dimer atoms': [15,15],
+    # Optimisation level: MP2/cc-pVTZ 
+    'positions':[[  0.9350155, -0.0279801, -0.3788916],
+                 [  1.6739638, -0.0357766,  0.7424316],
+                 [  3.0747955, -0.0094480,  0.5994562],
+                 [  3.5646109,  0.0195446, -0.7059872],
+                 [  2.8531510,  0.0258031, -1.8409596],
+                 [  1.5490760,  0.0012569, -1.5808009],
+                 [  4.0885824, -0.0054429,  1.5289786],
+                 [  5.1829921,  0.0253971,  0.7872176],
+                 [  4.9294871,  0.0412404, -0.5567274],
+                 [  1.0716177, -0.0765366,  1.9391390],
+                 [  0.8794435,  0.0050260, -2.4315709],
+                 [  6.1882591,  0.0375542,  1.1738824],
+                 [  5.6035368,  0.0648755, -1.3036811],
+                 [  0.0586915, -0.0423765,  2.0039181],
+                 [  1.6443796, -0.0347395,  2.7619159],
+                 [ -3.9211729, -0.0009646, -1.5163659],
+                 [ -4.6136833,  0.0169051, -0.3336520],
+                 [ -3.9917387,  0.0219348,  0.8663338],
+                 [ -2.5361367,  0.0074651,  0.8766724],
+                 [ -1.9256484, -0.0110593, -0.3638948],
+                 [ -2.5395897, -0.0149474, -1.5962357],
+                 [ -4.7106131,  0.0413373,  2.1738637],
+                 [ -1.8674730,  0.0112093,  1.9120833],
+                 [ -1.9416783, -0.0291878, -2.6573783],
+                 [ -4.4017172, -0.0036078, -2.4004924],
+                 [ -0.8838255, -0.0216168, -0.3784269],
+                 [ -5.6909220,  0.0269347, -0.4227183],
+                 [ -4.4439282, -0.8302573,  2.7695655],
+                 [ -4.4267056,  0.9186178,  2.7530256],
+                 [ -5.7883971,  0.0505530,  2.0247280]],
+    'positions 0.9':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.738685058 ,  -0.157889771 ,  1.110355410 ],
+                     [  -2.139452884 ,  -0.168053559 ,  0.964712563 ],
+                     [  -2.629497187 ,  -0.008665792 ,  -0.331201352 ],
+                     [  -1.918309833 ,  0.152634753 ,  -1.454844039 ],
+                     [  -0.614262216 ,  0.143659867 ,  -1.193547121 ],
+                     [  -3.152980999 ,  -0.310697201 ,  1.883518666 ],
+                     [  -4.247466012 ,  -0.237200328 ,  1.144874976 ],
+                     [  -3.994250734 ,  -0.056604504 ,  -0.187030096 ],
+                     [  -0.136179412 ,  -0.289433845 ,  2.300428025 ],
+                     [  0.055161346 ,  0.265959015 ,  -2.035655088 ],
+                     [  -5.252585445 ,  -0.308958331 ,  1.525406574 ],
+                     [  -4.668404863 ,  0.026245320 ,  -0.929656824 ],
+                     [  0.876876426 ,  -0.329105732 ,  2.359811410 ],
+                     [  -0.708581316 ,  -0.452407073 ,  3.108240602 ],
+                     [  4.674076612 ,  0.155627547 ,  -1.128075158 ],
+                     [  5.366947235 ,  -0.031573530 ,  0.039652507 ],
+                     [  4.745331442 ,  -0.213180550 ,  1.225999310 ],
+                     [  3.289690418 ,  -0.205459536 ,  1.237959001 ],
+                     [  2.678823212 ,  -0.008913767 ,  0.013109028 ],
+                     [  3.292432779 ,  0.176239188 ,  -1.205417098 ],
+                     [  5.464603172 ,  -0.419950938 ,  2.517000917 ],
+                     [  2.621308338 ,  -0.362031655 ,  2.261654302 ],
+                     [  2.694203350 ,  0.342506569 ,  -2.253367774 ],
+                     [  5.154382378 ,  0.288458351 ,  -2.002300903 ],
+                     [  1.636966971 ,  0.000000000 ,  0.000000000 ],
+                     [  6.444191927 ,  -0.024779868 ,  -0.049650000 ],
+                     [  5.195022957 ,  0.354841198 ,  3.233018736 ],
+                     [  5.183915029 ,  -1.373098243 ,  2.962397530 ],
+                     [  6.542374655 ,  -0.403617008 ,  2.368385087 ]],
+    'positions 1.0':[[  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -0.738685058000000 ,  -0.157889771000000 ,  1.110355410000000 ],
+                     [  -2.139452884000000 ,  -0.168053559000000 ,  0.964712563000000 ],
+                     [  -2.629497187000000 ,  -0.008665792000000 ,  -0.331201352000000 ],
+                     [  -1.918309833000000 ,  0.152634753000000 ,  -1.454844039000000 ],
+                     [  -0.614262216000000 ,  0.143659867000000 ,  -1.193547121000000 ],
+                     [  -3.152980999000000 ,  -0.310697201000000 ,  1.883518666000000 ],
+                     [  -4.247466012000000 ,  -0.237200328000000 ,  1.144874976000000 ],
+                     [  -3.994250734000000 ,  -0.056604504000000 ,  -0.187030096000000 ],
+                     [  -0.136179412000000 ,  -0.289433845000000 ,  2.300428025000000 ],
+                     [  0.055161346000000 ,  0.265959015000000 ,  -2.035655088000000 ],
+                     [  -5.252585445000000 ,  -0.308958331000000 ,  1.525406574000000 ],
+                     [  -4.668404863000000 ,  0.026245320000000 ,  -0.929656824000000 ],
+                     [  0.876876426000000 ,  -0.329105732000000 ,  2.359811410000000 ],
+                     [  -0.708581316000000 ,  -0.452407073000000 ,  3.108240602000000 ],
+                     [  4.855961831000000 ,  0.155627547000000 ,  -1.128075158000000 ],
+                     [  5.548832453999999 ,  -0.031573530000000 ,  0.039652507000000 ],
+                     [  4.927216661000000 ,  -0.213180550000000 ,  1.225999310000000 ],
+                     [  3.471575637000000 ,  -0.205459536000000 ,  1.237959001000000 ],
+                     [  2.860708431000000 ,  -0.008913767000000 ,  0.013109028000000 ],
+                     [  3.474317998000000 ,  0.176239188000000 ,  -1.205417098000000 ],
+                     [  5.646488391000000 ,  -0.419950938000000 ,  2.517000917000000 ],
+                     [  2.803193557000000 ,  -0.362031655000000 ,  2.261654302000000 ],
+                     [  2.876088569000000 ,  0.342506569000000 ,  -2.253367774000000 ],
+                     [  5.336267597000000 ,  0.288458351000000 ,  -2.002300903000000 ],
+                     [  1.818852190000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  6.626077146000000 ,  -0.024779868000000 ,  -0.049650000000000 ],
+                     [  5.376908176000000 ,  0.354841198000000 ,  3.233018736000000 ],
+                     [  5.365800247999999 ,  -1.373098243000000 ,  2.962397530000000 ],
+                     [  6.724259873999999 ,  -0.403617008000000 ,  2.368385087000000 ]],
+    'positions 1.2':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.738685058 ,  -0.157889771 ,  1.110355410 ],
+                     [  -2.139452884 ,  -0.168053559 ,  0.964712563 ],
+                     [  -2.629497187 ,  -0.008665792 ,  -0.331201352 ],
+                     [  -1.918309833 ,  0.152634753 ,  -1.454844039 ],
+                     [  -0.614262216 ,  0.143659867 ,  -1.193547121 ],
+                     [  -3.152980999 ,  -0.310697201 ,  1.883518666 ],
+                     [  -4.247466012 ,  -0.237200328 ,  1.144874976 ],
+                     [  -3.994250734 ,  -0.056604504 ,  -0.187030096 ],
+                     [  -0.136179412 ,  -0.289433845 ,  2.300428025 ],
+                     [  0.055161346 ,  0.265959015 ,  -2.035655088 ],
+                     [  -5.252585445 ,  -0.308958331 ,  1.525406574 ],
+                     [  -4.668404863 ,  0.026245320 ,  -0.929656824 ],
+                     [  0.876876426 ,  -0.329105732 ,  2.359811410 ],
+                     [  -0.708581316 ,  -0.452407073 ,  3.108240602 ],
+                     [  5.219732269 ,  0.155627547 ,  -1.128075158 ],
+                     [  5.912602892 ,  -0.031573530 ,  0.039652507 ],
+                     [  5.290987099 ,  -0.213180550 ,  1.225999310 ],
+                     [  3.835346075 ,  -0.205459536 ,  1.237959001 ],
+                     [  3.224478869 ,  -0.008913767 ,  0.013109028 ],
+                     [  3.838088436 ,  0.176239188 ,  -1.205417098 ],
+                     [  6.010258829 ,  -0.419950938 ,  2.517000917 ],
+                     [  3.166963995 ,  -0.362031655 ,  2.261654302 ],
+                     [  3.239859007 ,  0.342506569 ,  -2.253367774 ],
+                     [  5.700038035 ,  0.288458351 ,  -2.002300903 ],
+                     [  2.182622628 ,  0.000000000 ,  0.000000000 ],
+                     [  6.989847584 ,  -0.024779868 ,  -0.049650000 ],
+                     [  5.740678614 ,  0.354841198 ,  3.233018736 ],
+                     [  5.729570686 ,  -1.373098243 ,  2.962397530 ],
+                     [  7.088030312 ,  -0.403617008 ,  2.368385087 ]],
+    'positions 1.5':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.738685058 ,  -0.157889771 ,  1.110355410 ],
+                     [  -2.139452884 ,  -0.168053559 ,  0.964712563 ],
+                     [  -2.629497187 ,  -0.008665792 ,  -0.331201352 ],
+                     [  -1.918309833 ,  0.152634753 ,  -1.454844039 ],
+                     [  -0.614262216 ,  0.143659867 ,  -1.193547121 ],
+                     [  -3.152980999 ,  -0.310697201 ,  1.883518666 ],
+                     [  -4.247466012 ,  -0.237200328 ,  1.144874976 ],
+                     [  -3.994250734 ,  -0.056604504 ,  -0.187030096 ],
+                     [  -0.136179412 ,  -0.289433845 ,  2.300428025 ],
+                     [  0.055161346 ,  0.265959015 ,  -2.035655088 ],
+                     [  -5.252585445 ,  -0.308958331 ,  1.525406574 ],
+                     [  -4.668404863 ,  0.026245320 ,  -0.929656824 ],
+                     [  0.876876426 ,  -0.329105732 ,  2.359811410 ],
+                     [  -0.708581316 ,  -0.452407073 ,  3.108240602 ],
+                     [  5.765387926 ,  0.155627547 ,  -1.128075158 ],
+                     [  6.458258549 ,  -0.031573530 ,  0.039652507 ],
+                     [  5.836642756 ,  -0.213180550 ,  1.225999310 ],
+                     [  4.381001732 ,  -0.205459536 ,  1.237959001 ],
+                     [  3.770134526 ,  -0.008913767 ,  0.013109028 ],
+                     [  4.383744093 ,  0.176239188 ,  -1.205417098 ],
+                     [  6.555914486 ,  -0.419950938 ,  2.517000917 ],
+                     [  3.712619652 ,  -0.362031655 ,  2.261654302 ],
+                     [  3.785514664 ,  0.342506569 ,  -2.253367774 ],
+                     [  6.245693692 ,  0.288458351 ,  -2.002300903 ],
+                     [  2.728278285 ,  0.000000000 ,  0.000000000 ],
+                     [  7.535503241 ,  -0.024779868 ,  -0.049650000 ],
+                     [  6.286334271 ,  0.354841198 ,  3.233018736 ],
+                     [  6.275226343 ,  -1.373098243 ,  2.962397530 ],
+                     [  7.633685969 ,  -0.403617008 ,  2.368385087 ]],
+    'positions 2.0':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.738685058 ,  -0.157889771 ,  1.110355410 ],
+                     [  -2.139452884 ,  -0.168053559 ,  0.964712563 ],
+                     [  -2.629497187 ,  -0.008665792 ,  -0.331201352 ],
+                     [  -1.918309833 ,  0.152634753 ,  -1.454844039 ],
+                     [  -0.614262216 ,  0.143659867 ,  -1.193547121 ],
+                     [  -3.152980999 ,  -0.310697201 ,  1.883518666 ],
+                     [  -4.247466012 ,  -0.237200328 ,  1.144874976 ],
+                     [  -3.994250734 ,  -0.056604504 ,  -0.187030096 ],
+                     [  -0.136179412 ,  -0.289433845 ,  2.300428025 ],
+                     [  0.055161346 ,  0.265959015 ,  -2.035655088 ],
+                     [  -5.252585445 ,  -0.308958331 ,  1.525406574 ],
+                     [  -4.668404863 ,  0.026245320 ,  -0.929656824 ],
+                     [  0.876876426 ,  -0.329105732 ,  2.359811410 ],
+                     [  -0.708581316 ,  -0.452407073 ,  3.108240602 ],
+                     [  6.674814021 ,  0.155627547 ,  -1.128075158 ],
+                     [  7.367684644 ,  -0.031573530 ,  0.039652507 ],
+                     [  6.746068851 ,  -0.213180550 ,  1.225999310 ],
+                     [  5.290427827 ,  -0.205459536 ,  1.237959001 ],
+                     [  4.679560621 ,  -0.008913767 ,  0.013109028 ],
+                     [  5.293170188 ,  0.176239188 ,  -1.205417098 ],
+                     [  7.465340581 ,  -0.419950938 ,  2.517000917 ],
+                     [  4.622045747 ,  -0.362031655 ,  2.261654302 ],
+                     [  4.694940759 ,  0.342506569 ,  -2.253367774 ],
+                     [  7.155119787 ,  0.288458351 ,  -2.002300903 ],
+                     [  3.637704380 ,  0.000000000 ,  0.000000000 ],
+                     [  8.444929336 ,  -0.024779868 ,  -0.049650000 ],
+                     [  7.195760366 ,  0.354841198 ,  3.233018736 ],
+                     [  7.184652438 ,  -1.373098243 ,  2.962397530 ],
+                     [  8.543112064 ,  -0.403617008 ,  2.368385087 ]]},
+
+'Ammonia_dimer': {
+    'description': "Complex, S22, S26, 2 h-bonds",
+    'name': "Ammonia_dimer",
+    's26_number': "01",
+    'interaction energy CC':-0.1375,
+    'interaction energies s22x5':[-0.1045,-0.1362,-0.1023,-0.0481,-0.0156],
+    'offset': 0.0013,
+    'symbols': 'NHHHNHHH',
+    'magmoms': None,
+    'dimer atoms': [4,4],
+    # Optimisation level: CCSD(T)/cc-pVQZ 
+    'positions':[[ -1.578718, -0.046611,  0.000000],
+                 [ -2.158621,  0.136396, -0.809565],
+                 [ -2.158621,  0.136396,  0.809565],
+                 [ -0.849471,  0.658193,  0.000000],
+                 [  1.578718,  0.046611,  0.000000],
+                 [  2.158621, -0.136396, -0.809565],
+                 [  0.849471, -0.658193,  0.000000],
+                 [  2.158621, -0.136396,  0.809565]],
+    'positions 0.9':[[  -0.535020551 ,  -0.861570006 ,  0.000000000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  -0.809565000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  0.809565000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.253621272 ,  0.000000000 ,  0.000000000 ],
+                     [  2.860659421 ,  -0.035829274 ,  -0.809565000 ],
+                     [  1.718600721 ,  -0.861570006 ,  0.000000000 ],
+                     [  2.860659421 ,  -0.035829274 ,  0.809565000 ]],
+    'positions 1.0':[[  -0.535020551000000 ,  -0.861570006000000 ,  0.000000000000000 ],
+                     [  -1.142058700000000 ,  -0.825740733000000 ,  -0.809565000000000 ],
+                     [  -1.142058700000000 ,  -0.825740733000000 ,  0.809565000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.504023635454546 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  3.111061784454545 ,  -0.035829274000000 ,  -0.809565000000000 ],
+                     [  1.969003084454545 ,  -0.861570006000000 ,  0.000000000000000 ],
+                     [  3.111061784454545 ,  -0.035829274000000 ,  0.809565000000000 ]],
+    'positions 1.2':[[  -0.535020551 ,  -0.861570006 ,  0.000000000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  -0.809565000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  0.809565000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  3.004828362 ,  0.000000000 ,  0.000000000 ],
+                     [  3.611866511 ,  -0.035829274 ,  -0.809565000 ],
+                     [  2.469807811 ,  -0.861570006 ,  0.000000000 ],
+                     [  3.611866511 ,  -0.035829274 ,  0.809565000 ]],
+    'positions 1.5':[[  -0.535020551 ,  -0.861570006 ,  0.000000000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  -0.809565000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  0.809565000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  3.756035452 ,  0.000000000 ,  0.000000000 ],
+                     [  4.363073601 ,  -0.035829274 ,  -0.809565000 ],
+                     [  3.221014901 ,  -0.861570006 ,  0.000000000 ],
+                     [  4.363073601 ,  -0.035829274 ,  0.809565000 ]],
+    'positions 2.0':[[  -0.535020551 ,  -0.861570006 ,  0.000000000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  -0.809565000 ],
+                     [  -1.142058700 ,  -0.825740733 ,  0.809565000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  5.008047270 ,  0.000000000 ,  0.000000000 ],
+                     [  5.615085419 ,  -0.035829274 ,  -0.809565000 ],
+                     [  4.473026719 ,  -0.861570006 ,  0.000000000 ],
+                     [  5.615085419 ,  -0.035829274 ,  0.809565000 ]]},
+
+'Benzene-methane_complex': {
+    'description': "Complex, S22, S26, stack, dispersion bonded",
+    'name': "Benzene-methane_complex",
+    's26_number': "10",
+    'interaction energy CC':-0.0629,
+    'interaction energies s22x5':[-0.0473,-0.0650,-0.0490,-0.0208,-0.0052],
+    'offset': -0.0021,
+    'symbols': 'CCCCCCHHHHHHCHHHH',
+    'magmoms': None,
+    'dimer atoms': [12,5],
+    # Optimisation level:  MP2/cc-pVTZ
+    'positions':[[  1.3932178,  0.0362913, -0.6332803],
+       		 [  0.7280364, -1.1884015, -0.6333017],
+        	 [ -0.6651797, -1.2247077, -0.6332803],
+        	 [ -1.3932041, -0.0362972, -0.6333017],
+        	 [ -0.7280381,  1.1884163, -0.6332803],
+         	 [  0.6651677,  1.2246987, -0.6333017],
+         	 [  2.4742737,  0.0644484, -0.6317240],
+         	 [  1.2929588, -2.1105409, -0.6317401],
+        	 [ -1.1813229, -2.1750081, -0.6317240],
+        	 [ -2.4742614, -0.0644647, -0.6317401],
+        	 [ -1.2929508,  2.1105596, -0.6317240],
+         	 [  1.1813026,  2.1750056, -0.6317401],
+         	 [  0.0000000,  0.0000000,  3.0826195],
+         	 [  0.5868776,  0.8381742,  3.4463772],
+        	 [ -1.0193189,  0.0891638,  3.4463772],
+        	 [  0.0000000,  0.0000000,  1.9966697],
+         	 [  0.4324413, -0.9273380,  3.446377]],
+    'positions 0.9':[[  0.000011002 ,  0.036291078 ,  -1.393218002 ],
+                     [  -0.000011075 ,  -1.188401879 ,  -0.728035925 ],
+                     [  0.000010922 ,  -1.224707791 ,  0.665180078 ],
+                     [  -0.000011002 ,  -0.036296745 ,  1.393204002 ],
+                     [  0.000011075 ,  1.188416213 ,  0.728037925 ],
+                     [  -0.000010922 ,  1.224699125 ,  -0.665168078 ],
+                     [  0.001567004 ,  0.064448010 ,  -2.474274004 ],
+                     [  0.001550866 ,  -2.110540915 ,  -1.292958866 ],
+                     [  0.001566862 ,  -2.175007759 ,  1.181323138 ],
+                     [  0.001550996 ,  -0.064464677 ,  2.474261004 ],
+                     [  0.001567134 ,  2.110560249 ,  1.292950866 ],
+                     [  0.001551138 ,  2.175006092 ,  -1.181303138 ],
+                     [  3.452913900 ,  -0.000000069 ,  0.000000000 ],
+                     [  3.816671953 ,  0.838173871 ,  -0.586878053 ],
+                     [  3.816671906 ,  0.089163973 ,  1.019318994 ],
+                     [  2.366964900 ,  0.000000000 ,  0.000000000 ],
+                     [  3.816671841 ,  -0.927338119 ,  -0.432440941 ]],
+    'positions 1.0':[[  0.000011002000000 ,  0.036291078000000 ,  -1.393218002000000 ],
+                     [  -0.000011075000000 ,  -1.188401879000000 ,  -0.728035925000000 ],
+                     [  0.000010922000000 ,  -1.224707791000000 ,  0.665180078000000 ],
+                     [  -0.000011002000000 ,  -0.036296745000000 ,  1.393204002000000 ],
+                     [  0.000011075000000 ,  1.188416213000000 ,  0.728037925000000 ],
+                     [  -0.000010922000000 ,  1.224699125000000 ,  -0.665168078000000 ],
+                     [  0.001567004000000 ,  0.064448010000000 ,  -2.474274004000000 ],
+                     [  0.001550866000000 ,  -2.110540915000000 ,  -1.292958866000000 ],
+                     [  0.001566862000000 ,  -2.175007759000000 ,  1.181323138000000 ],
+                     [  0.001550996000000 ,  -0.064464677000000 ,  2.474261004000000 ],
+                     [  0.001567134000000 ,  2.110560249000000 ,  1.292950866000000 ],
+                     [  0.001551138000000 ,  2.175006092000000 ,  -1.181303138000000 ],
+                     [  3.715910000000000 ,  -0.000000069000000 ,  0.000000000000000 ],
+                     [  4.079668053000000 ,  0.838173871000000 ,  -0.586878053000000 ],
+                     [  4.079668005999999 ,  0.089163973000000 ,  1.019318994000000 ],
+                     [  2.629961000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  4.079667940999999 ,  -0.927338119000000 ,  -0.432440941000000 ]],
+    'positions 1.2':[[  0.000011002 ,  0.036291078 ,  -1.393218002 ],
+                     [  -0.000011075 ,  -1.188401879 ,  -0.728035925 ],
+                     [  0.000010922 ,  -1.224707791 ,  0.665180078 ],
+                     [  -0.000011002 ,  -0.036296745 ,  1.393204002 ],
+                     [  0.000011075 ,  1.188416213 ,  0.728037925 ],
+                     [  -0.000010922 ,  1.224699125 ,  -0.665168078 ],
+                     [  0.001567004 ,  0.064448010 ,  -2.474274004 ],
+                     [  0.001550866 ,  -2.110540915 ,  -1.292958866 ],
+                     [  0.001566862 ,  -2.175007759 ,  1.181323138 ],
+                     [  0.001550996 ,  -0.064464677 ,  2.474261004 ],
+                     [  0.001567134 ,  2.110560249 ,  1.292950866 ],
+                     [  0.001551138 ,  2.175006092 ,  -1.181303138 ],
+                     [  4.241902200 ,  -0.000000069 ,  0.000000000 ],
+                     [  4.605660253 ,  0.838173871 ,  -0.586878053 ],
+                     [  4.605660206 ,  0.089163973 ,  1.019318994 ],
+                     [  3.155953200 ,  0.000000000 ,  0.000000000 ],
+                     [  4.605660141 ,  -0.927338119 ,  -0.432440941 ]],
+    'positions 1.5':[[  0.000011002 ,  0.036291078 ,  -1.393218002 ],
+                     [  -0.000011075 ,  -1.188401879 ,  -0.728035925 ],
+                     [  0.000010922 ,  -1.224707791 ,  0.665180078 ],
+                     [  -0.000011002 ,  -0.036296745 ,  1.393204002 ],
+                     [  0.000011075 ,  1.188416213 ,  0.728037925 ],
+                     [  -0.000010922 ,  1.224699125 ,  -0.665168078 ],
+                     [  0.001567004 ,  0.064448010 ,  -2.474274004 ],
+                     [  0.001550866 ,  -2.110540915 ,  -1.292958866 ],
+                     [  0.001566862 ,  -2.175007759 ,  1.181323138 ],
+                     [  0.001550996 ,  -0.064464677 ,  2.474261004 ],
+                     [  0.001567134 ,  2.110560249 ,  1.292950866 ],
+                     [  0.001551138 ,  2.175006092 ,  -1.181303138 ],
+                     [  5.030890500 ,  -0.000000069 ,  0.000000000 ],
+                     [  5.394648553 ,  0.838173871 ,  -0.586878053 ],
+                     [  5.394648506 ,  0.089163973 ,  1.019318994 ],
+                     [  3.944941500 ,  0.000000000 ,  0.000000000 ],
+                     [  5.394648441 ,  -0.927338119 ,  -0.432440941 ]],
+    'positions 2.0':[[  0.000011002 ,  0.036291078 ,  -1.393218002 ],
+                     [  -0.000011075 ,  -1.188401879 ,  -0.728035925 ],
+                     [  0.000010922 ,  -1.224707791 ,  0.665180078 ],
+                     [  -0.000011002 ,  -0.036296745 ,  1.393204002 ],
+                     [  0.000011075 ,  1.188416213 ,  0.728037925 ],
+                     [  -0.000010922 ,  1.224699125 ,  -0.665168078 ],
+                     [  0.001567004 ,  0.064448010 ,  -2.474274004 ],
+                     [  0.001550866 ,  -2.110540915 ,  -1.292958866 ],
+                     [  0.001566862 ,  -2.175007759 ,  1.181323138 ],
+                     [  0.001550996 ,  -0.064464677 ,  2.474261004 ],
+                     [  0.001567134 ,  2.110560249 ,  1.292950866 ],
+                     [  0.001551138 ,  2.175006092 ,  -1.181303138 ],
+                     [  6.345871000 ,  -0.000000069 ,  0.000000000 ],
+                     [  6.709629053 ,  0.838173871 ,  -0.586878053 ],
+                     [  6.709629006 ,  0.089163973 ,  1.019318994 ],
+                     [  5.259922000 ,  0.000000000 ,  0.000000000 ],
+                     [  6.709628941 ,  -0.927338119 ,  -0.432440941 ]]},
+
+'Benzene-ammonia_complex': {
+    'description': "Complex, S22, S26",
+    'name': "Benzene-ammonia_complex",
+    's26_number': "18",
+    'interaction energy CC':-0.1006,
+    'interaction energies s22x5':[-0.0885,-0.1019,-0.0759,-0.0369,-0.0121],
+    'offset': -0.0013,
+    'symbols': 'CCCCCCHHHHHHNHHH',
+    'magmoms': None,
+    'dimer atoms': [12,4],
+    # Optimisation level:   MP2/cc-pVTZ
+    'positions':[[ -0.7392810,  0.5158785, -1.2071079],
+    		 [ -1.4261442,  0.3965455,  0.0000000],
+    		 [ -0.7392810,  0.5158785,  1.2071079],
+     		 [  0.6342269,  0.7546398,  1.2070735],
+     		 [  1.3210434,  0.8737566,  0.0000000],
+     		 [  0.6342269,  0.7546398, -1.2070735],
+    		 [ -1.2719495,  0.4206316, -2.1432894],
+    		 [ -2.4902205,  0.2052381,  0.0000000],
+    		 [ -1.2719495,  0.4206316,  2.1432894],
+     		 [  1.1668005,  0.8474885,  2.1436950],
+    		 [  2.3863585,  1.0596312,  0.0000000],
+     		 [  1.1668005,  0.8474885, -2.1436950],
+     		 [  0.1803930, -2.9491231,  0.0000000],
+     		 [  0.7595495, -3.1459477, -0.8060729],
+     		 [  0.7595495, -3.1459477,  0.8060729],
+     		 [  0.0444167, -1.9449399,  0.0000000]],
+    'positions 0.9':[[  0.000000000 ,  0.000000000 ,  -1.207108000 ],
+                     [  -0.094723910 ,  -0.690687169 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207108000 ],
+                     [  0.189293052 ,  1.381194838 ,  1.207073000 ],
+                     [  0.284209467 ,  2.071771374 ,  0.000000000 ],
+                     [  0.189293052 ,  1.381194838 ,  -1.207073000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  -2.143289000 ],
+                     [  -0.235335157 ,  -1.762640796 ,  0.000000000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  2.143289000 ],
+                     [  0.262434233 ,  1.916830087 ,  2.143695000 ],
+                     [  0.430373810 ,  3.143257869 ,  0.000000000 ],
+                     [  0.262434233 ,  1.916830087 ,  -2.143695000 ],
+                     [  3.322432676 ,  -0.175158455 ,  0.000000000 ],
+                     [  3.685723470 ,  0.316960994 ,  -0.806073000 ],
+                     [  3.685723470 ,  0.316960994 ,  0.806073000 ],
+                     [  2.324338249 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.0':[[  0.000000000000000 ,  0.000000000000000 ,  -1.207108000000000 ],
+                     [  -0.094723910000000 ,  -0.690687169000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  1.207108000000000 ],
+                     [  0.189293052000000 ,  1.381194838000000 ,  1.207073000000000 ],
+                     [  0.284209467000000 ,  2.071771374000000 ,  0.000000000000000 ],
+                     [  0.189293052000000 ,  1.381194838000000 ,  -1.207073000000000 ],
+                     [  -0.070884435000000 ,  -0.536454706000000 ,  -2.143289000000000 ],
+                     [  -0.235335157000000 ,  -1.762640796000000 ,  0.000000000000000 ],
+                     [  -0.070884435000000 ,  -0.536454706000000 ,  2.143289000000000 ],
+                     [  0.262434233000000 ,  1.916830087000000 ,  2.143695000000000 ],
+                     [  0.430373810000000 ,  3.143257869000000 ,  0.000000000000000 ],
+                     [  0.262434233000000 ,  1.916830087000000 ,  -2.143695000000000 ],
+                     [  3.580692481363636 ,  -0.175158455000000 ,  0.000000000000000 ],
+                     [  3.943983275363637 ,  0.316960994000000 ,  -0.806073000000000 ],
+                     [  3.943983275363637 ,  0.316960994000000 ,  0.806073000000000 ],
+                     [  2.582598054363637 ,  0.000000000000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  0.000000000 ,  0.000000000 ,  -1.207108000 ],
+                     [  -0.094723910 ,  -0.690687169 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207108000 ],
+                     [  0.189293052 ,  1.381194838 ,  1.207073000 ],
+                     [  0.284209467 ,  2.071771374 ,  0.000000000 ],
+                     [  0.189293052 ,  1.381194838 ,  -1.207073000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  -2.143289000 ],
+                     [  -0.235335157 ,  -1.762640796 ,  0.000000000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  2.143289000 ],
+                     [  0.262434233 ,  1.916830087 ,  2.143695000 ],
+                     [  0.430373810 ,  3.143257869 ,  0.000000000 ],
+                     [  0.262434233 ,  1.916830087 ,  -2.143695000 ],
+                     [  4.097212092 ,  -0.175158455 ,  0.000000000 ],
+                     [  4.460502886 ,  0.316960994 ,  -0.806073000 ],
+                     [  4.460502886 ,  0.316960994 ,  0.806073000 ],
+                     [  3.099117665 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.5':[[  0.000000000 ,  0.000000000 ,  -1.207108000 ],
+                     [  -0.094723910 ,  -0.690687169 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207108000 ],
+                     [  0.189293052 ,  1.381194838 ,  1.207073000 ],
+                     [  0.284209467 ,  2.071771374 ,  0.000000000 ],
+                     [  0.189293052 ,  1.381194838 ,  -1.207073000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  -2.143289000 ],
+                     [  -0.235335157 ,  -1.762640796 ,  0.000000000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  2.143289000 ],
+                     [  0.262434233 ,  1.916830087 ,  2.143695000 ],
+                     [  0.430373810 ,  3.143257869 ,  0.000000000 ],
+                     [  0.262434233 ,  1.916830087 ,  -2.143695000 ],
+                     [  4.871991508 ,  -0.175158455 ,  0.000000000 ],
+                     [  5.235282302 ,  0.316960994 ,  -0.806073000 ],
+                     [  5.235282302 ,  0.316960994 ,  0.806073000 ],
+                     [  3.873897081 ,  0.000000000 ,  0.000000000 ]],
+    'positions 2.0':[[  0.000000000 ,  0.000000000 ,  -1.207108000 ],
+                     [  -0.094723910 ,  -0.690687169 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207108000 ],
+                     [  0.189293052 ,  1.381194838 ,  1.207073000 ],
+                     [  0.284209467 ,  2.071771374 ,  0.000000000 ],
+                     [  0.189293052 ,  1.381194838 ,  -1.207073000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  -2.143289000 ],
+                     [  -0.235335157 ,  -1.762640796 ,  0.000000000 ],
+                     [  -0.070884435 ,  -0.536454706 ,  2.143289000 ],
+                     [  0.262434233 ,  1.916830087 ,  2.143695000 ],
+                     [  0.430373810 ,  3.143257869 ,  0.000000000 ],
+                     [  0.262434233 ,  1.916830087 ,  -2.143695000 ],
+                     [  6.163290535 ,  -0.175158455 ,  0.000000000 ],
+                     [  6.526581329 ,  0.316960994 ,  -0.806073000 ],
+                     [  6.526581329 ,  0.316960994 ,  0.806073000 ],
+                     [  5.165196108 ,  0.000000000 ,  0.000000000 ]]},
+     		 
+'Benzene_dimer_parallel_displaced': {
+    'description': "Complex, S22, S26, stack, dispersion bonded",
+    'name': "Benzene_dimer_parallel_displaced",
+    's26_number': "11",
+    'interaction energy CC':-0.1136,
+    'interaction energies s22x5':[-0.0065,-0.1219,-0.0833,-0.0230,-0.0030],
+    'offset': -0.0083,
+    'symbols': 'CCCCCCHHHHHHCCCCCCHHHHHH',
+    'magmoms': None,
+    'dimer atoms': [12,12],
+    # Optimisation level:   MP2/cc-pVTZ
+    'positions':[[ -1.0478252, -1.4216736,  0.0000000],
+                 [ -1.4545034, -0.8554459,  1.2062048],
+                 [ -1.4545034, -0.8554459, -1.2062048],
+                 [ -2.2667970,  0.2771610,  1.2069539],
+                 [ -2.6714781,  0.8450211,  0.0000000],
+                 [ -2.2667970,  0.2771610, -1.2069539],
+                 [ -1.1338534, -1.2920593, -2.1423150],
+                 [ -2.5824943,  0.7163066, -2.1437977],
+                 [ -3.3030422,  1.7232700,  0.0000000],
+                 [ -2.5824943,  0.7163066,  2.1437977],
+                 [ -1.1338534, -1.2920593,  2.1423150],
+                 [ -0.4060253, -2.2919049,  0.0000000],
+                 [  1.0478252,  1.4216736,  0.0000000],
+                 [  1.4545034,  0.8554459, -1.2062048],
+                 [  1.4545034,  0.8554459,  1.2062048],
+                 [  2.2667970, -0.2771610, -1.2069539],
+                 [  2.6714781, -0.8450211,  0.0000000],
+                 [  2.2667970, -0.2771610,  1.2069539],
+                 [  0.4060253,  2.2919049,  0.0000000],
+                 [  1.1338534,  1.2920593,  2.1423150],
+                 [  2.5824943, -0.7163066,  2.1437977],
+                 [  3.3030422, -1.7232700,  0.0000000],
+                 [  2.5824943, -0.7163066, -2.1437977],
+                 [  1.1338534,  1.2920593, -2.1423150]],
+    'positions 0.9':[[  0.629051507 ,  -1.244058476 ,  0.000000000 ],
+                     [  0.314072291 ,  -0.622134657 ,  1.206205000 ],
+                     [  0.314072291 ,  -0.622134657 ,  -1.206205000 ],
+                     [  -0.314813547 ,  0.621699240 ,  1.206954000 ],
+                     [  -0.627568995 ,  1.244929310 ,  0.000000000 ],
+                     [  -0.314813547 ,  0.621699240 ,  -1.206954000 ],
+                     [  0.563930576 ,  -1.102778154 ,  -2.142315000 ],
+                     [  -0.559388819 ,  1.104085746 ,  -2.143798000 ],
+                     [  -1.116894124 ,  2.209685917 ,  0.000000000 ],
+                     [  -0.559388819 ,  1.104085746 ,  2.143798000 ],
+                     [  0.563930576 ,  -1.102778154 ,  2.142315000 ],
+                     [  1.129721711 ,  -2.202462660 ,  0.000000000 ],
+                     [  2.759649224 ,  1.244058476 ,  0.000000000 ],
+                     [  3.074628440 ,  0.622134657 ,  -1.206205000 ],
+                     [  3.074628440 ,  0.622134657 ,  1.206205000 ],
+                     [  3.703514278 ,  -0.621699240 ,  -1.206954000 ],
+                     [  4.016269727 ,  -1.244929310 ,  0.000000000 ],
+                     [  3.703514278 ,  -0.621699240 ,  1.206954000 ],
+                     [  2.258979020 ,  2.202462660 ,  0.000000000 ],
+                     [  2.824770156 ,  1.102778154 ,  2.142315000 ],
+                     [  3.948089550 ,  -1.104085746 ,  2.143798000 ],
+                     [  4.505594855 ,  -2.209685917 ,  0.000000000 ],
+                     [  3.948089550 ,  -1.104085746 ,  -2.143798000 ],
+                     [  2.824770156 ,  1.102778154 ,  -2.142315000 ]],
+    'positions 1.0':[[  0.629051507000000 ,  -1.244058476000000 ,  0.000000000000000 ],
+                     [  0.314072291000000 ,  -0.622134657000000 ,  1.206205000000000 ],
+                     [  0.314072291000000 ,  -0.622134657000000 ,  -1.206205000000000 ],
+                     [  -0.314813547000000 ,  0.621699240000000 ,  1.206954000000000 ],
+                     [  -0.627568995000000 ,  1.244929310000000 ,  0.000000000000000 ],
+                     [  -0.314813547000000 ,  0.621699240000000 ,  -1.206954000000000 ],
+                     [  0.563930576000000 ,  -1.102778154000000 ,  -2.142315000000000 ],
+                     [  -0.559388819000000 ,  1.104085746000000 ,  -2.143798000000000 ],
+                     [  -1.116894124000000 ,  2.209685917000000 ,  0.000000000000000 ],
+                     [  -0.559388819000000 ,  1.104085746000000 ,  2.143798000000000 ],
+                     [  0.563930576000000 ,  -1.102778154000000 ,  2.142315000000000 ],
+                     [  1.129721711000000 ,  -2.202462660000000 ,  0.000000000000000 ],
+                     [  3.136171527545454 ,  1.244058476000000 ,  0.000000000000000 ],
+                     [  3.451150743545455 ,  0.622134657000000 ,  -1.206205000000000 ],
+                     [  3.451150743545455 ,  0.622134657000000 ,  1.206205000000000 ],
+                     [  4.080036581545454 ,  -0.621699240000000 ,  -1.206954000000000 ],
+                     [  4.392792030545455 ,  -1.244929310000000 ,  0.000000000000000 ],
+                     [  4.080036581545454 ,  -0.621699240000000 ,  1.206954000000000 ],
+                     [  2.635501323545455 ,  2.202462660000000 ,  0.000000000000000 ],
+                     [  3.201292459545455 ,  1.102778154000000 ,  2.142315000000000 ],
+                     [  4.324611853545455 ,  -1.104085746000000 ,  2.143798000000000 ],
+                     [  4.882117158545454 ,  -2.209685917000000 ,  0.000000000000000 ],
+                     [  4.324611853545455 ,  -1.104085746000000 ,  -2.143798000000000 ],
+                     [  3.201292459545455 ,  1.102778154000000 ,  -2.142315000000000 ]],
+    'positions 1.2':[[  0.629051507 ,  -1.244058476 ,  0.000000000 ],
+                     [  0.314072291 ,  -0.622134657 ,  1.206205000 ],
+                     [  0.314072291 ,  -0.622134657 ,  -1.206205000 ],
+                     [  -0.314813547 ,  0.621699240 ,  1.206954000 ],
+                     [  -0.627568995 ,  1.244929310 ,  0.000000000 ],
+                     [  -0.314813547 ,  0.621699240 ,  -1.206954000 ],
+                     [  0.563930576 ,  -1.102778154 ,  -2.142315000 ],
+                     [  -0.559388819 ,  1.104085746 ,  -2.143798000 ],
+                     [  -1.116894124 ,  2.209685917 ,  0.000000000 ],
+                     [  -0.559388819 ,  1.104085746 ,  2.143798000 ],
+                     [  0.563930576 ,  -1.102778154 ,  2.142315000 ],
+                     [  1.129721711 ,  -2.202462660 ,  0.000000000 ],
+                     [  3.889216135 ,  1.244058476 ,  0.000000000 ],
+                     [  4.204195351 ,  0.622134657 ,  -1.206205000 ],
+                     [  4.204195351 ,  0.622134657 ,  1.206205000 ],
+                     [  4.833081189 ,  -0.621699240 ,  -1.206954000 ],
+                     [  5.145836638 ,  -1.244929310 ,  0.000000000 ],
+                     [  4.833081189 ,  -0.621699240 ,  1.206954000 ],
+                     [  3.388545931 ,  2.202462660 ,  0.000000000 ],
+                     [  3.954337067 ,  1.102778154 ,  2.142315000 ],
+                     [  5.077656461 ,  -1.104085746 ,  2.143798000 ],
+                     [  5.635161766 ,  -2.209685917 ,  0.000000000 ],
+                     [  5.077656461 ,  -1.104085746 ,  -2.143798000 ],
+                     [  3.954337067 ,  1.102778154 ,  -2.142315000 ]],
+    'positions 1.5':[[  0.629051507 ,  -1.244058476 ,  0.000000000 ],
+                     [  0.314072291 ,  -0.622134657 ,  1.206205000 ],
+                     [  0.314072291 ,  -0.622134657 ,  -1.206205000 ],
+                     [  -0.314813547 ,  0.621699240 ,  1.206954000 ],
+                     [  -0.627568995 ,  1.244929310 ,  0.000000000 ],
+                     [  -0.314813547 ,  0.621699240 ,  -1.206954000 ],
+                     [  0.563930576 ,  -1.102778154 ,  -2.142315000 ],
+                     [  -0.559388819 ,  1.104085746 ,  -2.143798000 ],
+                     [  -1.116894124 ,  2.209685917 ,  0.000000000 ],
+                     [  -0.559388819 ,  1.104085746 ,  2.143798000 ],
+                     [  0.563930576 ,  -1.102778154 ,  2.142315000 ],
+                     [  1.129721711 ,  -2.202462660 ,  0.000000000 ],
+                     [  5.018783046 ,  1.244058476 ,  0.000000000 ],
+                     [  5.333762262 ,  0.622134657 ,  -1.206205000 ],
+                     [  5.333762262 ,  0.622134657 ,  1.206205000 ],
+                     [  5.962648100 ,  -0.621699240 ,  -1.206954000 ],
+                     [  6.275403549 ,  -1.244929310 ,  0.000000000 ],
+                     [  5.962648100 ,  -0.621699240 ,  1.206954000 ],
+                     [  4.518112842 ,  2.202462660 ,  0.000000000 ],
+                     [  5.083903978 ,  1.102778154 ,  2.142315000 ],
+                     [  6.207223372 ,  -1.104085746 ,  2.143798000 ],
+                     [  6.764728677 ,  -2.209685917 ,  0.000000000 ],
+                     [  6.207223372 ,  -1.104085746 ,  -2.143798000 ],
+                     [  5.083903978 ,  1.102778154 ,  -2.142315000 ]],
+    'positions 2.0':[[  0.629051507 ,  -1.244058476 ,  0.000000000 ],
+                     [  0.314072291 ,  -0.622134657 ,  1.206205000 ],
+                     [  0.314072291 ,  -0.622134657 ,  -1.206205000 ],
+                     [  -0.314813547 ,  0.621699240 ,  1.206954000 ],
+                     [  -0.627568995 ,  1.244929310 ,  0.000000000 ],
+                     [  -0.314813547 ,  0.621699240 ,  -1.206954000 ],
+                     [  0.563930576 ,  -1.102778154 ,  -2.142315000 ],
+                     [  -0.559388819 ,  1.104085746 ,  -2.143798000 ],
+                     [  -1.116894124 ,  2.209685917 ,  0.000000000 ],
+                     [  -0.559388819 ,  1.104085746 ,  2.143798000 ],
+                     [  0.563930576 ,  -1.102778154 ,  2.142315000 ],
+                     [  1.129721711 ,  -2.202462660 ,  0.000000000 ],
+                     [  6.901394563 ,  1.244058476 ,  0.000000000 ],
+                     [  7.216373779 ,  0.622134657 ,  -1.206205000 ],
+                     [  7.216373779 ,  0.622134657 ,  1.206205000 ],
+                     [  7.845259617 ,  -0.621699240 ,  -1.206954000 ],
+                     [  8.158015066 ,  -1.244929310 ,  0.000000000 ],
+                     [  7.845259617 ,  -0.621699240 ,  1.206954000 ],
+                     [  6.400724359 ,  2.202462660 ,  0.000000000 ],
+                     [  6.966515495 ,  1.102778154 ,  2.142315000 ],
+                     [  8.089834889 ,  -1.104085746 ,  2.143798000 ],
+                     [  8.647340194 ,  -2.209685917 ,  0.000000000 ],
+                     [  8.089834889 ,  -1.104085746 ,  -2.143798000 ],
+                     [  6.966515495 ,  1.102778154 ,  -2.142315000 ]]},
+    		 
+'Benzene_dimer_T-shaped': {
+    'description': "Complex, S22, S26",
+    'name': "Benzene_dimer_T-shaped",
+    's26_number': "20",
+    'interaction energy CC':-0.1175,
+    'interaction energies s22x5':[-0.0954,-0.1214,-0.0976,-0.0486,-0.0152],
+    'offset': -0.0039,
+    'symbols': 'CCCCCCHHHHHHCCCCCCHHHHHH',
+    'magmoms': None,
+    'dimer atoms': [12,12],
+    # Optimisation level:   MP2/cc-pVTZ
+    'positions':[[  0.0000000,  0.0000000,  1.0590353],
+                 [  0.0000000, -1.2060084,  1.7576742],
+                 [  0.0000000, -1.2071767,  3.1515905],
+                 [  0.0000000,  0.0000000,  3.8485751],
+                 [  0.0000000,  1.2071767,  3.1515905],
+                 [  0.0000000,  1.2060084,  1.7576742],
+                 [  0.0000000,  0.0000000, -0.0215805],
+                 [  0.0000000, -2.1416387,  1.2144217],
+                 [  0.0000000, -2.1435657,  3.6929953],
+                 [  0.0000000,  0.0000000,  4.9301499],
+                 [  0.0000000,  2.1435657,  3.6929953],
+                 [  0.0000000,  2.1416387,  1.2144217],
+                 [ -1.3940633,  0.0000000, -2.4541524],
+                 [ -0.6970468,  1.2072378, -2.4546277],
+                 [  0.6970468,  1.2072378, -2.4546277],
+                 [  1.3940633,  0.0000000, -2.4541524],
+                 [  0.6970468, -1.2072378, -2.4546277],
+                 [ -0.6970468, -1.2072378, -2.4546277],
+                 [ -2.4753995,  0.0000000, -2.4503221],
+                 [ -1.2382321,  2.1435655, -2.4536764],
+                 [  1.2382321,  2.1435655, -2.4536764],
+                 [  2.4753995,  0.0000000, -2.4503221],
+                 [  1.2382321, -2.1435655, -2.4536764],
+                 [ -1.2382321, -2.1435655, -2.4536764]],
+    'positions 0.9':[[  -1.080615000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.779254000 ,  -1.206008000 ,  0.000000000 ],
+                     [  -3.173171000 ,  -1.207177000 ,  0.000000000 ],
+                     [  -3.870155000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.173171000 ,  1.207177000 ,  0.000000000 ],
+                     [  -1.779254000 ,  1.206008000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.236002000 ,  -2.141639000 ,  0.000000000 ],
+                     [  -3.714575000 ,  -2.143566000 ,  0.000000000 ],
+                     [  -4.951730000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.714575000 ,  2.143566000 ,  0.000000000 ],
+                     [  -1.236002000 ,  2.141639000 ,  0.000000000 ],
+                     [  2.189283067 ,  0.000000000 ,  -1.394063000 ],
+                     [  2.189759067 ,  1.207238000 ,  -0.697047000 ],
+                     [  2.189759067 ,  1.207238000 ,  0.697047000 ],
+                     [  2.189283067 ,  0.000000000 ,  1.394063000 ],
+                     [  2.189759067 ,  -1.207238000 ,  0.697047000 ],
+                     [  2.189759067 ,  -1.207238000 ,  -0.697047000 ],
+                     [  2.185453067 ,  0.000000000 ,  -2.475399000 ],
+                     [  2.188807067 ,  2.143565000 ,  -1.238232000 ],
+                     [  2.188807067 ,  2.143565000 ,  1.238232000 ],
+                     [  2.185453067 ,  0.000000000 ,  2.475399000 ],
+                     [  2.188807067 ,  -2.143565000 ,  1.238232000 ],
+                     [  2.188807067 ,  -2.143565000 ,  -1.238232000 ]],
+    'positions 1.0':[[  -1.080615000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -1.779254000000000 ,  -1.206008000000000 ,  0.000000000000000 ],
+                     [  -3.173171000000000 ,  -1.207177000000000 ,  0.000000000000000 ],
+                     [  -3.870155000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -3.173171000000000 ,  1.207177000000000 ,  0.000000000000000 ],
+                     [  -1.779254000000000 ,  1.206008000000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -1.236002000000000 ,  -2.141639000000000 ,  0.000000000000000 ],
+                     [  -3.714575000000000 ,  -2.143566000000000 ,  0.000000000000000 ],
+                     [  -4.951730000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -3.714575000000000 ,  2.143566000000000 ,  0.000000000000000 ],
+                     [  -1.236002000000000 ,  2.141639000000000 ,  0.000000000000000 ],
+                     [  2.432572000272727 ,  0.000000000000000 ,  -1.394063000000000 ],
+                     [  2.433048000272727 ,  1.207238000000000 ,  -0.697047000000000 ],
+                     [  2.433048000272727 ,  1.207238000000000 ,  0.697047000000000 ],
+                     [  2.432572000272727 ,  0.000000000000000 ,  1.394063000000000 ],
+                     [  2.433048000272727 ,  -1.207238000000000 ,  0.697047000000000 ],
+                     [  2.433048000272727 ,  -1.207238000000000 ,  -0.697047000000000 ],
+                     [  2.428742000272727 ,  0.000000000000000 ,  -2.475399000000000 ],
+                     [  2.432096000272727 ,  2.143565000000000 ,  -1.238232000000000 ],
+                     [  2.432096000272727 ,  2.143565000000000 ,  1.238232000000000 ],
+                     [  2.428742000272727 ,  0.000000000000000 ,  2.475399000000000 ],
+                     [  2.432096000272727 ,  -2.143565000000000 ,  1.238232000000000 ],
+                     [  2.432096000272727 ,  -2.143565000000000 ,  -1.238232000000000 ]],
+    'positions 1.2':[[  -1.080615000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.779254000 ,  -1.206008000 ,  0.000000000 ],
+                     [  -3.173171000 ,  -1.207177000 ,  0.000000000 ],
+                     [  -3.870155000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.173171000 ,  1.207177000 ,  0.000000000 ],
+                     [  -1.779254000 ,  1.206008000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.236002000 ,  -2.141639000 ,  0.000000000 ],
+                     [  -3.714575000 ,  -2.143566000 ,  0.000000000 ],
+                     [  -4.951730000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.714575000 ,  2.143566000 ,  0.000000000 ],
+                     [  -1.236002000 ,  2.141639000 ,  0.000000000 ],
+                     [  2.919149867 ,  0.000000000 ,  -1.394063000 ],
+                     [  2.919625867 ,  1.207238000 ,  -0.697047000 ],
+                     [  2.919625867 ,  1.207238000 ,  0.697047000 ],
+                     [  2.919149867 ,  0.000000000 ,  1.394063000 ],
+                     [  2.919625867 ,  -1.207238000 ,  0.697047000 ],
+                     [  2.919625867 ,  -1.207238000 ,  -0.697047000 ],
+                     [  2.915319867 ,  0.000000000 ,  -2.475399000 ],
+                     [  2.918673867 ,  2.143565000 ,  -1.238232000 ],
+                     [  2.918673867 ,  2.143565000 ,  1.238232000 ],
+                     [  2.915319867 ,  0.000000000 ,  2.475399000 ],
+                     [  2.918673867 ,  -2.143565000 ,  1.238232000 ],
+                     [  2.918673867 ,  -2.143565000 ,  -1.238232000 ]],
+    'positions 1.5':[[  -1.080615000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.779254000 ,  -1.206008000 ,  0.000000000 ],
+                     [  -3.173171000 ,  -1.207177000 ,  0.000000000 ],
+                     [  -3.870155000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.173171000 ,  1.207177000 ,  0.000000000 ],
+                     [  -1.779254000 ,  1.206008000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.236002000 ,  -2.141639000 ,  0.000000000 ],
+                     [  -3.714575000 ,  -2.143566000 ,  0.000000000 ],
+                     [  -4.951730000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.714575000 ,  2.143566000 ,  0.000000000 ],
+                     [  -1.236002000 ,  2.141639000 ,  0.000000000 ],
+                     [  3.649016667 ,  0.000000000 ,  -1.394063000 ],
+                     [  3.649492667 ,  1.207238000 ,  -0.697047000 ],
+                     [  3.649492667 ,  1.207238000 ,  0.697047000 ],
+                     [  3.649016667 ,  0.000000000 ,  1.394063000 ],
+                     [  3.649492667 ,  -1.207238000 ,  0.697047000 ],
+                     [  3.649492667 ,  -1.207238000 ,  -0.697047000 ],
+                     [  3.645186667 ,  0.000000000 ,  -2.475399000 ],
+                     [  3.648540667 ,  2.143565000 ,  -1.238232000 ],
+                     [  3.648540667 ,  2.143565000 ,  1.238232000 ],
+                     [  3.645186667 ,  0.000000000 ,  2.475399000 ],
+                     [  3.648540667 ,  -2.143565000 ,  1.238232000 ],
+                     [  3.648540667 ,  -2.143565000 ,  -1.238232000 ]],
+    'positions 2.0':[[  -1.080615000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.779254000 ,  -1.206008000 ,  0.000000000 ],
+                     [  -3.173171000 ,  -1.207177000 ,  0.000000000 ],
+                     [  -3.870155000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.173171000 ,  1.207177000 ,  0.000000000 ],
+                     [  -1.779254000 ,  1.206008000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -1.236002000 ,  -2.141639000 ,  0.000000000 ],
+                     [  -3.714575000 ,  -2.143566000 ,  0.000000000 ],
+                     [  -4.951730000 ,  0.000000000 ,  0.000000000 ],
+                     [  -3.714575000 ,  2.143566000 ,  0.000000000 ],
+                     [  -1.236002000 ,  2.141639000 ,  0.000000000 ],
+                     [  4.865461333 ,  0.000000000 ,  -1.394063000 ],
+                     [  4.865937333 ,  1.207238000 ,  -0.697047000 ],
+                     [  4.865937333 ,  1.207238000 ,  0.697047000 ],
+                     [  4.865461333 ,  0.000000000 ,  1.394063000 ],
+                     [  4.865937333 ,  -1.207238000 ,  0.697047000 ],
+                     [  4.865937333 ,  -1.207238000 ,  -0.697047000 ],
+                     [  4.861631333 ,  0.000000000 ,  -2.475399000 ],
+                     [  4.864985333 ,  2.143565000 ,  -1.238232000 ],
+                     [  4.864985333 ,  2.143565000 ,  1.238232000 ],
+                     [  4.861631333 ,  0.000000000 ,  2.475399000 ],
+                     [  4.864985333 ,  -2.143565000 ,  1.238232000 ],
+                     [  4.864985333 ,  -2.143565000 ,  -1.238232000 ]]},
+
+'Benzene-HCN_complex': {
+    'description': "Complex, S22, S26",
+    'name': "Benzene-HCN_complex",
+    's26_number': "19",
+    'interaction energy CC':-0.1973,
+    'interaction energies s22x5':[-0.1743,-0.1960,-0.1596,-0.0906,-0.0369],
+    'offset': 0.0013,
+    'symbols': 'CCCCCCHHHHHHNCH',
+    'magmoms': None,
+    'dimer atoms': [12,3],
+    # Optimisation level:   MP2/cc-pVTZ
+    'positions':[[ -0.7097741, -0.9904230,  1.2077018],
+                 [ -1.4065340, -0.9653529,  0.0000000],
+                 [ -0.7097741, -0.9904230, -1.2077018],
+                 [  0.6839651, -1.0405105, -1.2078652],
+                 [  1.3809779, -1.0655522,  0.0000000],
+                 [  0.6839651, -1.0405105,  1.2078652],
+                 [ -1.2499482, -0.9686280,  2.1440507],
+                 [ -2.4869197, -0.9237060,  0.0000000],
+                 [ -1.2499482, -0.9686280, -2.1440507],
+                 [  1.2242882, -1.0580753, -2.1442563],
+                 [  2.4615886, -1.1029818,  0.0000000],
+                 [  1.2242882, -1.0580753,  2.1442563],
+                 [ -0.0034118,  3.5353926,  0.0000000],
+                 [  0.0751963,  2.3707040,  0.0000000],
+                 [  0.1476295,  1.3052847,  0.0000000]],
+    'positions 0.9':[[  -0.023100946 ,  0.696978594 ,  1.207702000 ],
+                     [  -0.046160335 ,  1.393808033 ,  0.000000000 ],
+                     [  -0.023100946 ,  0.696978594 ,  -1.207702000 ],
+                     [  0.023085816 ,  -0.696895106 ,  -1.207865000 ],
+                     [  0.046190594 ,  -1.393975010 ,  0.000000000 ],
+                     [  0.023085816 ,  -0.696895106 ,  1.207865000 ],
+                     [  -0.038624622 ,  1.237369182 ,  2.144051000 ],
+                     [  -0.079148681 ,  2.474493071 ,  0.000000000 ],
+                     [  -0.038624622 ,  1.237369182 ,  -2.144051000 ],
+                     [  0.042839694 ,  -1.237142510 ,  -2.144256000 ],
+                     [  0.083401415 ,  -2.474593580 ,  0.000000000 ],
+                     [  0.042839694 ,  -1.237142510 ,  2.144256000 ],
+                     [  4.308034683 ,  0.304536859 ,  0.000000000 ],
+                     [  3.151543935 ,  0.145763954 ,  0.000000000 ],
+                     [  2.093660645 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.0':[[  -0.023100946000000 ,  0.696978594000000 ,  1.207702000000000 ],
+                     [  -0.046160335000000 ,  1.393808033000000 ,  0.000000000000000 ],
+                     [  -0.023100946000000 ,  0.696978594000000 ,  -1.207702000000000 ],
+                     [  0.023085816000000 ,  -0.696895106000000 ,  -1.207865000000000 ],
+                     [  0.046190594000000 ,  -1.393975010000000 ,  0.000000000000000 ],
+                     [  0.023085816000000 ,  -0.696895106000000 ,  1.207865000000000 ],
+                     [  -0.038624622000000 ,  1.237369182000000 ,  2.144051000000000 ],
+                     [  -0.079148681000000 ,  2.474493071000000 ,  0.000000000000000 ],
+                     [  -0.038624622000000 ,  1.237369182000000 ,  -2.144051000000000 ],
+                     [  0.042839694000000 ,  -1.237142510000000 ,  -2.144256000000000 ],
+                     [  0.083401415000000 ,  -2.474593580000000 ,  0.000000000000000 ],
+                     [  0.042839694000000 ,  -1.237142510000000 ,  2.144256000000000 ],
+                     [  4.540663643636363 ,  0.304536859000000 ,  0.000000000000000 ],
+                     [  3.384172895636364 ,  0.145763954000000 ,  0.000000000000000 ],
+                     [  2.326289605636364 ,  0.000000000000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  -0.023100946 ,  0.696978594 ,  1.207702000 ],
+                     [  -0.046160335 ,  1.393808033 ,  0.000000000 ],
+                     [  -0.023100946 ,  0.696978594 ,  -1.207702000 ],
+                     [  0.023085816 ,  -0.696895106 ,  -1.207865000 ],
+                     [  0.046190594 ,  -1.393975010 ,  0.000000000 ],
+                     [  0.023085816 ,  -0.696895106 ,  1.207865000 ],
+                     [  -0.038624622 ,  1.237369182 ,  2.144051000 ],
+                     [  -0.079148681 ,  2.474493071 ,  0.000000000 ],
+                     [  -0.038624622 ,  1.237369182 ,  -2.144051000 ],
+                     [  0.042839694 ,  -1.237142510 ,  -2.144256000 ],
+                     [  0.083401415 ,  -2.474593580 ,  0.000000000 ],
+                     [  0.042839694 ,  -1.237142510 ,  2.144256000 ],
+                     [  5.005921565 ,  0.304536859 ,  0.000000000 ],
+                     [  3.849430817 ,  0.145763954 ,  0.000000000 ],
+                     [  2.791547527 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.5':[[  -0.023100946 ,  0.696978594 ,  1.207702000 ],
+                     [  -0.046160335 ,  1.393808033 ,  0.000000000 ],
+                     [  -0.023100946 ,  0.696978594 ,  -1.207702000 ],
+                     [  0.023085816 ,  -0.696895106 ,  -1.207865000 ],
+                     [  0.046190594 ,  -1.393975010 ,  0.000000000 ],
+                     [  0.023085816 ,  -0.696895106 ,  1.207865000 ],
+                     [  -0.038624622 ,  1.237369182 ,  2.144051000 ],
+                     [  -0.079148681 ,  2.474493071 ,  0.000000000 ],
+                     [  -0.038624622 ,  1.237369182 ,  -2.144051000 ],
+                     [  0.042839694 ,  -1.237142510 ,  -2.144256000 ],
+                     [  0.083401415 ,  -2.474593580 ,  0.000000000 ],
+                     [  0.042839694 ,  -1.237142510 ,  2.144256000 ],
+                     [  5.703808447 ,  0.304536859 ,  0.000000000 ],
+                     [  4.547317699 ,  0.145763954 ,  0.000000000 ],
+                     [  3.489434409 ,  0.000000000 ,  0.000000000 ]],
+    'positions 2.0':[[  -0.023100946 ,  0.696978594 ,  1.207702000 ],
+                     [  -0.046160335 ,  1.393808033 ,  0.000000000 ],
+                     [  -0.023100946 ,  0.696978594 ,  -1.207702000 ],
+                     [  0.023085816 ,  -0.696895106 ,  -1.207865000 ],
+                     [  0.046190594 ,  -1.393975010 ,  0.000000000 ],
+                     [  0.023085816 ,  -0.696895106 ,  1.207865000 ],
+                     [  -0.038624622 ,  1.237369182 ,  2.144051000 ],
+                     [  -0.079148681 ,  2.474493071 ,  0.000000000 ],
+                     [  -0.038624622 ,  1.237369182 ,  -2.144051000 ],
+                     [  0.042839694 ,  -1.237142510 ,  -2.144256000 ],
+                     [  0.083401415 ,  -2.474593580 ,  0.000000000 ],
+                     [  0.042839694 ,  -1.237142510 ,  2.144256000 ],
+                     [  6.866953250 ,  0.304536859 ,  0.000000000 ],
+                     [  5.710462502 ,  0.145763954 ,  0.000000000 ],
+                     [  4.652579212 ,  0.000000000 ,  0.000000000 ]]},
+
+'Benzene-water_complex': {
+    'description': "Complex, S22, S26",
+    'name': "Benzene-water_complex",
+    's26_number': "17",
+    'interaction energy CC':-0.1427,
+    'interaction energies s22x5':[-0.1305,-0.1418,-0.1071,-0.0564,-0.0212],
+    'offset': 0.0009,
+    'symbols': 'CCCCCCHHHHHHOHH',
+    'magmoms': None,
+    'dimer atoms': [12,3],
+    # Optimisation level:   MP2/cc-pVTZ
+    'positions':[[  0.7806117, -0.6098875, -1.2075426],
+                 [  0.4784039,  0.7510406, -1.2079040],
+                 [  0.3276592,  1.4318573,  0.0000000],
+                 [  0.4784039,  0.7510406,  1.2079040],
+                 [  0.7806117, -0.6098875,  1.2075426],
+                 [  0.9321510, -1.2899614,  0.0000000],
+                 [  0.8966688, -1.1376051, -2.1441482],
+                 [  0.3573895,  1.2782091, -2.1440546],
+                 [  0.0918593,  2.4871407,  0.0000000],
+                 [  0.3573895,  1.2782091,  2.1440546],
+                 [  0.8966688, -1.1376051,  2.1441482],
+                 [  1.1690064, -2.3451668,  0.0000000],
+                 [ -2.7885270, -0.2744854,  0.0000000],
+                 [ -2.6229114, -1.2190831,  0.0000000],
+                 [ -1.9015103,  0.0979110,  0.0000000]],
+    'positions 0.9':[[  0.068736158 ,  1.392383840 ,  -1.207543000 ],
+                     [  0.000000000 ,  0.000000000 ,  -1.207904000 ],
+                     [  -0.034807303 ,  -0.696435878 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207904000 ],
+                     [  0.068736158 ,  1.392383840 ,  1.207543000 ],
+                     [  0.102581137 ,  2.088313342 ,  0.000000000 ],
+                     [  0.096477114 ,  1.931999350 ,  -2.144148000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  -2.144055000 ],
+                     [  -0.086694943 ,  -1.776497744 ,  0.000000000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  2.144055000 ],
+                     [  0.096477114 ,  1.931999350 ,  2.144148000 ],
+                     [  0.153430751 ,  3.168579194 ,  0.000000000 ],
+                     [  3.175061618 ,  0.124369730 ,  0.000000000 ],
+                     [  3.265337861 ,  1.079117991 ,  0.000000000 ],
+                     [  2.221117117 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.0':[[  0.068736158000000 ,  1.392383840000000 ,  -1.207543000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  -1.207904000000000 ],
+                     [  -0.034807303000000 ,  -0.696435878000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  1.207904000000000 ],
+                     [  0.068736158000000 ,  1.392383840000000 ,  1.207543000000000 ],
+                     [  0.102581137000000 ,  2.088313342000000 ,  0.000000000000000 ],
+                     [  0.096477114000000 ,  1.931999350000000 ,  -2.144148000000000 ],
+                     [  -0.022815407000000 ,  -0.540397951000000 ,  -2.144055000000000 ],
+                     [  -0.086694943000000 ,  -1.776497744000000 ,  0.000000000000000 ],
+                     [  -0.022815407000000 ,  -0.540397951000000 ,  2.144055000000000 ],
+                     [  0.096477114000000 ,  1.931999350000000 ,  2.144148000000000 ],
+                     [  0.153430751000000 ,  3.168579194000000 ,  0.000000000000000 ],
+                     [  3.421852408818182 ,  0.124369730000000 ,  0.000000000000000 ],
+                     [  3.512128651818182 ,  1.079117991000000 ,  0.000000000000000 ],
+                     [  2.467907907818182 ,  0.000000000000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  0.068736158 ,  1.392383840 ,  -1.207543000 ],
+                     [  0.000000000 ,  0.000000000 ,  -1.207904000 ],
+                     [  -0.034807303 ,  -0.696435878 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207904000 ],
+                     [  0.068736158 ,  1.392383840 ,  1.207543000 ],
+                     [  0.102581137 ,  2.088313342 ,  0.000000000 ],
+                     [  0.096477114 ,  1.931999350 ,  -2.144148000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  -2.144055000 ],
+                     [  -0.086694943 ,  -1.776497744 ,  0.000000000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  2.144055000 ],
+                     [  0.096477114 ,  1.931999350 ,  2.144148000 ],
+                     [  0.153430751 ,  3.168579194 ,  0.000000000 ],
+                     [  3.915433991 ,  0.124369730 ,  0.000000000 ],
+                     [  4.005710234 ,  1.079117991 ,  0.000000000 ],
+                     [  2.961489490 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.5':[[  0.068736158 ,  1.392383840 ,  -1.207543000 ],
+                     [  0.000000000 ,  0.000000000 ,  -1.207904000 ],
+                     [  -0.034807303 ,  -0.696435878 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207904000 ],
+                     [  0.068736158 ,  1.392383840 ,  1.207543000 ],
+                     [  0.102581137 ,  2.088313342 ,  0.000000000 ],
+                     [  0.096477114 ,  1.931999350 ,  -2.144148000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  -2.144055000 ],
+                     [  -0.086694943 ,  -1.776497744 ,  0.000000000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  2.144055000 ],
+                     [  0.096477114 ,  1.931999350 ,  2.144148000 ],
+                     [  0.153430751 ,  3.168579194 ,  0.000000000 ],
+                     [  4.655806363 ,  0.124369730 ,  0.000000000 ],
+                     [  4.746082606 ,  1.079117991 ,  0.000000000 ],
+                     [  3.701861862 ,  0.000000000 ,  0.000000000 ]],
+    'positions 2.0':[[  0.068736158 ,  1.392383840 ,  -1.207543000 ],
+                     [  0.000000000 ,  0.000000000 ,  -1.207904000 ],
+                     [  -0.034807303 ,  -0.696435878 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  1.207904000 ],
+                     [  0.068736158 ,  1.392383840 ,  1.207543000 ],
+                     [  0.102581137 ,  2.088313342 ,  0.000000000 ],
+                     [  0.096477114 ,  1.931999350 ,  -2.144148000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  -2.144055000 ],
+                     [  -0.086694943 ,  -1.776497744 ,  0.000000000 ],
+                     [  -0.022815407 ,  -0.540397951 ,  2.144055000 ],
+                     [  0.096477114 ,  1.931999350 ,  2.144148000 ],
+                     [  0.153430751 ,  3.168579194 ,  0.000000000 ],
+                     [  5.889760317 ,  0.124369730 ,  0.000000000 ],
+                     [  5.980036560 ,  1.079117991 ,  0.000000000 ],
+                     [  4.935815816 ,  0.000000000 ,  0.000000000 ]]},
+
+'Ethene_dimer': {
+    'description': "Complex, S22, S26, stack, dispersion bonded",
+    'name': "Ethene_dimer",
+    's26_number': "09",
+    'interaction energy CC':-0.0650,
+    'interaction energies s22x5':[-0.0295,-0.0642,-0.0351,-0.0087,-0.0013],
+    'offset': 0.0008,
+    'symbols': 'CCHHHHCCHHHH',
+    'magmoms': None,
+    'dimer atoms': [6,6],
+    # Optimisation level: CCSD(T)/cc-pVQZ
+    'positions':[[ -0.471925, -0.471925, -1.859111],
+                 [  0.471925,  0.471925, -1.859111],
+                 [ -0.872422, -0.872422, -0.936125],
+                 [  0.872422,  0.872422, -0.936125],
+                 [ -0.870464, -0.870464, -2.783308],
+                 [  0.870464,  0.870464, -2.783308],
+                 [ -0.471925,  0.471925,  1.859111],
+                 [  0.471925, -0.471925,  1.859111],
+                 [ -0.872422,  0.872422,  0.936125],
+                 [  0.872422, -0.872422,  0.936125],
+                 [ -0.870464,  0.870464,  2.783308],
+                 [  0.870464, -0.870464,  2.783308]],
+     'positions 0.9':[[  0.000000000 ,  -0.471925000 ,  0.471925000 ],
+                     [  0.000000000 ,  0.471925000 ,  -0.471925000 ],
+                     [  0.922986000 ,  -0.872422000 ,  0.872422000 ],
+                     [  0.922986000 ,  0.872422000 ,  -0.872422000 ],
+                     [  -0.924197000 ,  -0.870464000 ,  0.870464000 ],
+                     [  -0.924197000 ,  0.870464000 ,  -0.870464000 ],
+                     [  3.346399800 ,  0.471925000 ,  0.471925000 ],
+                     [  3.346399800 ,  -0.471925000 ,  -0.471925000 ],
+                     [  2.423413800 ,  0.872422000 ,  0.872422000 ],
+                     [  2.423413800 ,  -0.872422000 ,  -0.872422000 ],
+                     [  4.270596800 ,  0.870464000 ,  0.870464000 ],
+                     [  4.270596800 ,  -0.870464000 ,  -0.870464000 ]],
+    'positions 1.0':[[  0.000000000000000 ,  -0.471925000000000 ,  0.471925000000000 ],
+                     [  0.000000000000000 ,  0.471925000000000 ,  -0.471925000000000 ],
+                     [  0.922986000000000 ,  -0.872422000000000 ,  0.872422000000000 ],
+                     [  0.922986000000000 ,  0.872422000000000 ,  -0.872422000000000 ],
+                     [  -0.924197000000000 ,  -0.870464000000000 ,  0.870464000000000 ],
+                     [  -0.924197000000000 ,  0.870464000000000 ,  -0.870464000000000 ],
+                     [  3.718222000000000 ,  0.471925000000000 ,  0.471925000000000 ],
+                     [  3.718222000000000 ,  -0.471925000000000 ,  -0.471925000000000 ],
+                     [  2.795236000000000 ,  0.872422000000000 ,  0.872422000000000 ],
+                     [  2.795236000000000 ,  -0.872422000000000 ,  -0.872422000000000 ],
+                     [  4.642418999999999 ,  0.870464000000000 ,  0.870464000000000 ],
+                     [  4.642418999999999 ,  -0.870464000000000 ,  -0.870464000000000 ]],
+    'positions 1.2':[[  0.000000000 ,  -0.471925000 ,  0.471925000 ],
+                     [  0.000000000 ,  0.471925000 ,  -0.471925000 ],
+                     [  0.922986000 ,  -0.872422000 ,  0.872422000 ],
+                     [  0.922986000 ,  0.872422000 ,  -0.872422000 ],
+                     [  -0.924197000 ,  -0.870464000 ,  0.870464000 ],
+                     [  -0.924197000 ,  0.870464000 ,  -0.870464000 ],
+                     [  4.461866400 ,  0.471925000 ,  0.471925000 ],
+                     [  4.461866400 ,  -0.471925000 ,  -0.471925000 ],
+                     [  3.538880400 ,  0.872422000 ,  0.872422000 ],
+                     [  3.538880400 ,  -0.872422000 ,  -0.872422000 ],
+                     [  5.386063400 ,  0.870464000 ,  0.870464000 ],
+                     [  5.386063400 ,  -0.870464000 ,  -0.870464000 ]],
+    'positions 1.5':[[  0.000000000 ,  -0.471925000 ,  0.471925000 ],
+                     [  0.000000000 ,  0.471925000 ,  -0.471925000 ],
+                     [  0.922986000 ,  -0.872422000 ,  0.872422000 ],
+                     [  0.922986000 ,  0.872422000 ,  -0.872422000 ],
+                     [  -0.924197000 ,  -0.870464000 ,  0.870464000 ],
+                     [  -0.924197000 ,  0.870464000 ,  -0.870464000 ],
+                     [  5.577333000 ,  0.471925000 ,  0.471925000 ],
+                     [  5.577333000 ,  -0.471925000 ,  -0.471925000 ],
+                     [  4.654347000 ,  0.872422000 ,  0.872422000 ],
+                     [  4.654347000 ,  -0.872422000 ,  -0.872422000 ],
+                     [  6.501530000 ,  0.870464000 ,  0.870464000 ],
+                     [  6.501530000 ,  -0.870464000 ,  -0.870464000 ]],
+    'positions 2.0':[[  0.000000000 ,  -0.471925000 ,  0.471925000 ],
+                     [  0.000000000 ,  0.471925000 ,  -0.471925000 ],
+                     [  0.922986000 ,  -0.872422000 ,  0.872422000 ],
+                     [  0.922986000 ,  0.872422000 ,  -0.872422000 ],
+                     [  -0.924197000 ,  -0.870464000 ,  0.870464000 ],
+                     [  -0.924197000 ,  0.870464000 ,  -0.870464000 ],
+                     [  7.436444000 ,  0.471925000 ,  0.471925000 ],
+                     [  7.436444000 ,  -0.471925000 ,  -0.471925000 ],
+                     [  6.513458000 ,  0.872422000 ,  0.872422000 ],
+                     [  6.513458000 ,  -0.872422000 ,  -0.872422000 ],
+                     [  8.360641000 ,  0.870464000 ,  0.870464000 ],
+                     [  8.360641000 ,  -0.870464000 ,  -0.870464000 ]]},
+
+'Ethene-ethyne_complex': {
+    'description': "Complex, S22, S26",
+    'name': "Ethene-ethyne_complex",
+    's26_number': "16",
+    'interaction energy CC':-0.0655,
+    'interaction energies s22x5':[-0.0507,-0.0646,-0.0468,-0.0212,-0.0065],
+    'offset': 0.0009,
+    'symbols': 'CCHHHHCCHH',
+    'magmoms': None,
+    'dimer atoms': [6,4],
+    # Optimisation level: CCSD(T)/cc-pVQZ
+    'positions':[[  0.000000, -0.667578, -2.124659],
+                 [  0.000000,  0.667578, -2.124659],
+                 [  0.923621, -1.232253, -2.126185],
+                 [ -0.923621, -1.232253, -2.126185],
+                 [ -0.923621,  1.232253, -2.126185],
+                 [  0.923621,  1.232253, -2.126185],
+                 [  0.000000,  0.000000,  2.900503],
+                 [  0.000000,  0.000000,  1.693240],
+                 [  0.000000,  0.000000,  0.627352],
+                 [  0.000000,  0.000000,  3.963929]],
+    'positions 0.9':[[  0.000000000 ,  -0.667578000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.667578000 ,  0.000000000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  -0.923621000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  -0.923621000 ],
+                     [  4.749960900 ,  0.000000000 ,  0.000000000 ],
+                     [  3.542697900 ,  0.000000000 ,  0.000000000 ],
+                     [  2.476809900 ,  0.000000000 ,  0.000000000 ],
+                     [  5.813386900 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.0':[[  0.000000000000000 ,  -0.667578000000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.667578000000000 ,  0.000000000000000 ],
+                     [  -0.001526000000000 ,  -1.232253000000000 ,  -0.923621000000000 ],
+                     [  -0.001526000000000 ,  -1.232253000000000 ,  0.923621000000000 ],
+                     [  -0.001526000000000 ,  1.232253000000000 ,  0.923621000000000 ],
+                     [  -0.001526000000000 ,  1.232253000000000 ,  -0.923621000000000 ],
+                     [  5.025162000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  3.817899000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.752011000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  6.088588000000001 ,  0.000000000000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  0.000000000 ,  -0.667578000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.667578000 ,  0.000000000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  -0.923621000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  -0.923621000 ],
+                     [  5.575564200 ,  0.000000000 ,  0.000000000 ],
+                     [  4.368301200 ,  0.000000000 ,  0.000000000 ],
+                     [  3.302413200 ,  0.000000000 ,  0.000000000 ],
+                     [  6.638990200 ,  0.000000000 ,  0.000000000 ]],
+    'positions 1.5':[[  0.000000000 ,  -0.667578000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.667578000 ,  0.000000000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  -0.923621000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  -0.923621000 ],
+                     [  6.401167500 ,  0.000000000 ,  0.000000000 ],
+                     [  5.193904500 ,  0.000000000 ,  0.000000000 ],
+                     [  4.128016500 ,  0.000000000 ,  0.000000000 ],
+                     [  7.464593500 ,  0.000000000 ,  0.000000000 ]],
+    'positions 2.0':[[  0.000000000 ,  -0.667578000 ,  0.000000000 ],
+                     [  0.000000000 ,  0.667578000 ,  0.000000000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  -0.923621000 ],
+                     [  -0.001526000 ,  -1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  0.923621000 ],
+                     [  -0.001526000 ,  1.232253000 ,  -0.923621000 ],
+                     [  7.777173000 ,  0.000000000 ,  0.000000000 ],
+                     [  6.569910000 ,  0.000000000 ,  0.000000000 ],
+                     [  5.504022000 ,  0.000000000 ,  0.000000000 ],
+                     [  8.840599000 ,  0.000000000 ,  0.000000000 ]]},
+
+'Formamide_dimer': {
+    'description': "Complex, S22, S26, 2 h-bonds, double h-bond",
+    'name': "Formamide_dimer",
+    's26_number': "04",
+    'interaction energy CC':-0.6990,
+    'interaction energies s22x5':[-0.6132,-0.6917,-0.5811,-0.3512,-0.1522],
+    'offset': 0.0073,
+    'symbols': 'CONHHHCONHHH',
+    'magmoms': None,
+    'dimer atoms': [6,6],
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[ -2.018649,  0.052883,  0.000000],
+                 [ -1.452200,  1.143634,  0.000000],
+                 [ -1.407770, -1.142484,  0.000000],
+                 [ -1.964596, -1.977036,  0.000000],
+                 [ -0.387244, -1.207782,  0.000000],
+                 [ -3.117061, -0.013701,  0.000000],
+                 [  2.018649, -0.052883,  0.000000],
+                 [  1.452200, -1.143634,  0.000000],
+                 [  1.407770,  1.142484,  0.000000],
+                 [  1.964596,  1.977036,  0.000000],
+                 [  0.387244,  1.207782,  0.000000],
+                 [  3.117061,  0.013701,  0.000000]],
+    'positions 0.9':[[  -0.604120150 ,  -1.070346233 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.035273679 ,  -2.286277608 ,  0.000000000 ],
+                     [  -0.620847527 ,  -3.100915874 ,  0.000000000 ],
+                     [  0.982356530 ,  -2.387103713 ,  0.000000000 ],
+                     [  -1.704185444 ,  -1.098607493 ,  0.000000000 ],
+                     [  3.242982655 ,  -1.316757480 ,  0.000000000 ],
+                     [  2.638862505 ,  -2.387103713 ,  0.000000000 ],
+                     [  2.674136184 ,  -0.100826104 ,  0.000000000 ],
+                     [  3.259710032 ,  0.713812161 ,  0.000000000 ],
+                     [  1.656505975 ,  0.000000000 ,  0.000000000 ],
+                     [  4.343047949 ,  -1.288496220 ,  0.000000000 ]],
+    'positions 1.0':[[  -0.604120150000000 ,  -1.070346233000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -0.035273679000000 ,  -2.286277608000000 ,  0.000000000000000 ],
+                     [  -0.620847527000000 ,  -3.100915874000000 ,  0.000000000000000 ],
+                     [  0.982356530000000 ,  -2.387103713000000 ,  0.000000000000000 ],
+                     [  -1.704185444000000 ,  -1.098607493000000 ,  0.000000000000000 ],
+                     [  3.427038874545455 ,  -1.316757480000000 ,  0.000000000000000 ],
+                     [  2.822918724545455 ,  -2.387103713000000 ,  0.000000000000000 ],
+                     [  2.858192403545455 ,  -0.100826104000000 ,  0.000000000000000 ],
+                     [  3.443766251545455 ,  0.713812161000000 ,  0.000000000000000 ],
+                     [  1.840562194545454 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  4.527104168545454 ,  -1.288496220000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  -0.604120150 ,  -1.070346233 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.035273679 ,  -2.286277608 ,  0.000000000 ],
+                     [  -0.620847527 ,  -3.100915874 ,  0.000000000 ],
+                     [  0.982356530 ,  -2.387103713 ,  0.000000000 ],
+                     [  -1.704185444 ,  -1.098607493 ,  0.000000000 ],
+                     [  3.795151314 ,  -1.316757480 ,  0.000000000 ],
+                     [  3.191031164 ,  -2.387103713 ,  0.000000000 ],
+                     [  3.226304843 ,  -0.100826104 ,  0.000000000 ],
+                     [  3.811878691 ,  0.713812161 ,  0.000000000 ],
+                     [  2.208674634 ,  0.000000000 ,  0.000000000 ],
+                     [  4.895216608 ,  -1.288496220 ,  0.000000000 ]],
+    'positions 1.5':[[  -0.604120150 ,  -1.070346233 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.035273679 ,  -2.286277608 ,  0.000000000 ],
+                     [  -0.620847527 ,  -3.100915874 ,  0.000000000 ],
+                     [  0.982356530 ,  -2.387103713 ,  0.000000000 ],
+                     [  -1.704185444 ,  -1.098607493 ,  0.000000000 ],
+                     [  4.347319973 ,  -1.316757480 ,  0.000000000 ],
+                     [  3.743199823 ,  -2.387103713 ,  0.000000000 ],
+                     [  3.778473502 ,  -0.100826104 ,  0.000000000 ],
+                     [  4.364047350 ,  0.713812161 ,  0.000000000 ],
+                     [  2.760843293 ,  0.000000000 ,  0.000000000 ],
+                     [  5.447385267 ,  -1.288496220 ,  0.000000000 ]],
+    'positions 2.0':[[  -0.604120150 ,  -1.070346233 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.035273679 ,  -2.286277608 ,  0.000000000 ],
+                     [  -0.620847527 ,  -3.100915874 ,  0.000000000 ],
+                     [  0.982356530 ,  -2.387103713 ,  0.000000000 ],
+                     [  -1.704185444 ,  -1.098607493 ,  0.000000000 ],
+                     [  5.267601070 ,  -1.316757480 ,  0.000000000 ],
+                     [  4.663480920 ,  -2.387103713 ,  0.000000000 ],
+                     [  4.698754599 ,  -0.100826104 ,  0.000000000 ],
+                     [  5.284328447 ,  0.713812161 ,  0.000000000 ],
+                     [  3.681124390 ,  0.000000000 ,  0.000000000 ],
+                     [  6.367666364 ,  -1.288496220 ,  0.000000000 ]]},
+
+'Formic_acid_dimer': {
+    'description': "Complex, S22, S26, 2 h-bonds, double h-bond",
+    'name': "Formic_acid_dimer",
+    's26_number': "03",
+    'interaction energy CC':-0.8152,
+    'interaction energies s22x5':[-0.7086,-0.8061,-0.6773,-0.4007,-0.1574],
+    'offset': 0.0091,
+    'symbols': 'COOHHCOOHH',
+    'magmoms': None,
+    'dimer atoms': [5,5],
+    # Optimisation level:  CCSD(T)/cc-pVTZ
+    'positions':[[ -1.888896, -0.179692,  0.000000],
+                 [ -1.493280,  1.073689,  0.000000],
+                 [ -1.170435, -1.166590,  0.000000],
+                 [ -2.979488, -0.258829,  0.000000],
+                 [ -0.498833,  1.107195,  0.000000],
+                 [  1.888896,  0.179692,  0.000000],
+                 [  1.493280, -1.073689,  0.000000],
+                 [  1.170435,  1.166590,  0.000000],
+                 [  2.979488,  0.258829,  0.000000],
+                 [  0.498833, -1.107195,  0.000000]],
+    'positions 0.9':[[  -1.434944263 ,  -1.236643950 ,  0.000000000 ],
+                     [  -0.995009531 ,  0.001876693 ,  0.000000000 ],
+                     [  -0.752030700 ,  -2.248465543 ,  0.000000000 ],
+                     [  -2.527660580 ,  -1.276950582 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.186205474 ,  -1.011821594 ,  0.000000000 ],
+                     [  1.746270742 ,  -2.250342236 ,  0.000000000 ],
+                     [  1.503291911 ,  0.000000000 ,  0.000000000 ],
+                     [  3.278921791 ,  -0.971514961 ,  0.000000000 ],
+                     [  0.751261211 ,  -2.248465543 ,  0.000000000 ]],
+    'positions 1.0':[[  -1.434944263000000 ,  -1.236643950000000 ,  0.000000000000000 ],
+                     [  -0.995009531000000 ,  0.001876693000000 ,  0.000000000000000 ],
+                     [  -0.752030700000000 ,  -2.248465543000000 ,  0.000000000000000 ],
+                     [  -2.527660580000000 ,  -1.276950582000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.353237908636364 ,  -1.011821594000000 ,  0.000000000000000 ],
+                     [  1.913303176636364 ,  -2.250342236000000 ,  0.000000000000000 ],
+                     [  1.670324345636364 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  3.445954225636364 ,  -0.971514961000000 ,  0.000000000000000 ],
+                     [  0.918293645636364 ,  -2.248465543000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  -1.434944263 ,  -1.236643950 ,  0.000000000 ],
+                     [  -0.995009531 ,  0.001876693 ,  0.000000000 ],
+                     [  -0.752030700 ,  -2.248465543 ,  0.000000000 ],
+                     [  -2.527660580 ,  -1.276950582 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.687302778 ,  -1.011821594 ,  0.000000000 ],
+                     [  2.247368046 ,  -2.250342236 ,  0.000000000 ],
+                     [  2.004389215 ,  0.000000000 ,  0.000000000 ],
+                     [  3.780019095 ,  -0.971514961 ,  0.000000000 ],
+                     [  1.252358515 ,  -2.248465543 ,  0.000000000 ]],
+    'positions 1.5':[[  -1.434944263 ,  -1.236643950 ,  0.000000000 ],
+                     [  -0.995009531 ,  0.001876693 ,  0.000000000 ],
+                     [  -0.752030700 ,  -2.248465543 ,  0.000000000 ],
+                     [  -2.527660580 ,  -1.276950582 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  3.188400082 ,  -1.011821594 ,  0.000000000 ],
+                     [  2.748465350 ,  -2.250342236 ,  0.000000000 ],
+                     [  2.505486519 ,  0.000000000 ,  0.000000000 ],
+                     [  4.281116399 ,  -0.971514961 ,  0.000000000 ],
+                     [  1.753455819 ,  -2.248465543 ,  0.000000000 ]],
+    'positions 2.0':[[  -1.434944263 ,  -1.236643950 ,  0.000000000 ],
+                     [  -0.995009531 ,  0.001876693 ,  0.000000000 ],
+                     [  -0.752030700 ,  -2.248465543 ,  0.000000000 ],
+                     [  -2.527660580 ,  -1.276950582 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  4.023562255 ,  -1.011821594 ,  0.000000000 ],
+                     [  3.583627523 ,  -2.250342236 ,  0.000000000 ],
+                     [  3.340648692 ,  0.000000000 ,  0.000000000 ],
+                     [  5.116278572 ,  -0.971514961 ,  0.000000000 ],
+                     [  2.588617992 ,  -2.248465543 ,  0.000000000 ]]},
+
+'Indole-benzene_complex_stack': {
+    'description': "Complex, S22, S26, stack, dispersion bonded",
+    'name': "Indole-benzene_complex_stack",
+    's26_number': "14",
+    'interaction energy CC':-0.1990,
+    'interaction energies s22x5':[-0.0924,-0.2246,-0.1565,-0.0468,-0.0043],
+    'offset': -0.0256,
+    'symbols': 'CCCCCCHHHHHHHCCHCCHCNCCHCHHH',
+    'magmoms': None,
+    'dimer atoms': [12,16],
+    # Optimisation level:  MP2/cc-pVTZ
+    'positions':[[ -0.0210742,  1.5318615, -1.3639345],
+                 [ -1.2746794,  0.9741030, -1.6074097],
+                 [ -1.3783055, -0.2256981, -2.3084154],
+                 [ -0.2289426, -0.8664053, -2.7687944],
+                 [  1.0247882, -0.3035171, -2.5312410],
+                 [  1.1289996,  0.8966787, -1.8299830],
+                 [  0.0600740,  2.4565627, -0.8093957],
+                 [ -2.1651002,  1.4654521, -1.2405676],
+                 [ -2.3509735, -0.6616122, -2.4926698],
+                 [ -0.3103419, -1.7955762, -3.3172704],
+                 [  1.9165847, -0.7940845, -2.8993942],
+                 [  2.1000347,  1.3326757, -1.6400420],
+                 [ -2.9417647,  0.8953834,  2.2239054],
+                 [ -2.0220674,  0.4258540,  1.9013549],
+                 [ -0.8149418,  1.0740453,  2.1066982],
+                 [ -0.7851529,  2.0443812,  2.5856086],
+                 [  0.3704286,  0.4492852,  1.6847458],
+                 [  1.7508619,  0.8038935,  1.7194004],
+                 [  2.1870108,  1.6998281,  2.1275903],
+                 [  2.4451359, -0.2310742,  1.1353313],
+                 [  1.5646462, -1.2137812,  0.7555384],
+                 [  0.2861214, -0.8269486,  1.0618752],
+                 [ -0.9284667, -1.4853121,  0.8606937],
+                 [ -0.9729200, -2.4554847,  0.3834013],
+                 [ -2.0792848, -0.8417668,  1.2876443],
+                 [ -3.0389974, -1.3203846,  1.1468400],
+                 [  1.8075741, -2.0366963,  0.2333038],
+                 [  3.5028794, -0.3485344,  0.969523]],
+    'positions 0.9':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.044485647 ,  -1.177978626 ,  0.743160105 ],
+                     [  -0.010824638 ,  -2.411208517 ,  0.095333145 ],
+                     [  0.064150773 ,  -2.466933785 ,  -1.295623602 ],
+                     [  0.100950904 ,  -1.287437054 ,  -2.038959973 ],
+                     [  0.067356799 ,  -0.053500209 ,  -1.391376263 ],
+                     [  -0.013797739 ,  0.956881587 ,  0.503348328 ],
+                     [  -0.091346970 ,  -1.134458005 ,  1.822398921 ],
+                     [  -0.039754009 ,  -3.325680275 ,  0.672358669 ],
+                     [  0.085389531 ,  -3.424849020 ,  -1.798373823 ],
+                     [  0.146442780 ,  -1.330172544 ,  -3.119514770 ],
+                     [  0.100852832 ,  0.862456237 ,  -1.964945566 ],
+                     [  2.717766027 ,  -0.578056849 ,  3.494904751 ],
+                     [  2.793508398 ,  -0.571969873 ,  2.415753956 ],
+                     [  2.753054336 ,  0.633650134 ,  1.734349558 ],
+                     [  2.645935858 ,  1.567038531 ,  2.272036098 ],
+                     [  2.855804852 ,  0.624347564 ,  0.333339655 ],
+                     [  2.845637545 ,  1.633662034 ,  -0.673499279 ],
+                     [  2.762013625 ,  2.698030593 ,  -0.533251753 ],
+                     [  2.976224608 ,  0.992808148 ,  -1.884517470 ],
+                     [  3.081930238 ,  -0.360086596 ,  -1.675422891 ],
+                     [  2.997750328 ,  -0.624347564 ,  -0.333339655 ],
+                     [  3.046288127 ,  -1.839842986 ,  0.351754941 ],
+                     [  3.153106953 ,  -2.780217935 ,  -0.172940228 ],
+                     [  2.941516868 ,  -1.796211682 ,  1.733036170 ],
+                     [  2.973148444 ,  -2.718261443 ,  2.297634930 ],
+                     [  3.103876306 ,  -1.056446212 ,  -2.398978775 ],
+                     [  3.012441631 ,  1.398036276 ,  -2.881807744 ]],
+    'positions 1.0':[[  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -0.044485647000000 ,  -1.177978626000000 ,  0.743160105000000 ],
+                     [  -0.010824638000000 ,  -2.411208517000000 ,  0.095333145000000 ],
+                     [  0.064150773000000 ,  -2.466933785000000 ,  -1.295623602000000 ],
+                     [  0.100950904000000 ,  -1.287437054000000 ,  -2.038959973000000 ],
+                     [  0.067356799000000 ,  -0.053500209000000 ,  -1.391376263000000 ],
+                     [  -0.013797739000000 ,  0.956881587000000 ,  0.503348328000000 ],
+                     [  -0.091346970000000 ,  -1.134458005000000 ,  1.822398921000000 ],
+                     [  -0.039754009000000 ,  -3.325680275000000 ,  0.672358669000000 ],
+                     [  0.085389531000000 ,  -3.424849020000000 ,  -1.798373823000000 ],
+                     [  0.146442780000000 ,  -1.330172544000000 ,  -3.119514770000000 ],
+                     [  0.100852832000000 ,  0.862456237000000 ,  -1.964945566000000 ],
+                     [  3.042963537000000 ,  -0.578056849000000 ,  3.494904751000000 ],
+                     [  3.118705908000000 ,  -0.571969873000000 ,  2.415753956000000 ],
+                     [  3.078251846000000 ,  0.633650134000000 ,  1.734349558000000 ],
+                     [  2.971133368000000 ,  1.567038531000000 ,  2.272036098000000 ],
+                     [  3.181002362000000 ,  0.624347564000000 ,  0.333339655000000 ],
+                     [  3.170835055000000 ,  1.633662034000000 ,  -0.673499279000000 ],
+                     [  3.087211135000000 ,  2.698030593000000 ,  -0.533251753000000 ],
+                     [  3.301422118000000 ,  0.992808148000000 ,  -1.884517470000000 ],
+                     [  3.407127748000000 ,  -0.360086596000000 ,  -1.675422891000000 ],
+                     [  3.322947838000000 ,  -0.624347564000000 ,  -0.333339655000000 ],
+                     [  3.371485637000000 ,  -1.839842986000000 ,  0.351754941000000 ],
+                     [  3.478304463000000 ,  -2.780217935000000 ,  -0.172940228000000 ],
+                     [  3.266714378000000 ,  -1.796211682000000 ,  1.733036170000000 ],
+                     [  3.298345954000000 ,  -2.718261443000000 ,  2.297634930000000 ],
+                     [  3.429073816000000 ,  -1.056446212000000 ,  -2.398978775000000 ],
+                     [  3.337639141000000 ,  1.398036276000000 ,  -2.881807744000000 ]],
+    'positions 1.2':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.044485647 ,  -1.177978626 ,  0.743160105 ],
+                     [  -0.010824638 ,  -2.411208517 ,  0.095333145 ],
+                     [  0.064150773 ,  -2.466933785 ,  -1.295623602 ],
+                     [  0.100950904 ,  -1.287437054 ,  -2.038959973 ],
+                     [  0.067356799 ,  -0.053500209 ,  -1.391376263 ],
+                     [  -0.013797739 ,  0.956881587 ,  0.503348328 ],
+                     [  -0.091346970 ,  -1.134458005 ,  1.822398921 ],
+                     [  -0.039754009 ,  -3.325680275 ,  0.672358669 ],
+                     [  0.085389531 ,  -3.424849020 ,  -1.798373823 ],
+                     [  0.146442780 ,  -1.330172544 ,  -3.119514770 ],
+                     [  0.100852832 ,  0.862456237 ,  -1.964945566 ],
+                     [  3.693358557 ,  -0.578056849 ,  3.494904751 ],
+                     [  3.769100928 ,  -0.571969873 ,  2.415753956 ],
+                     [  3.728646866 ,  0.633650134 ,  1.734349558 ],
+                     [  3.621528388 ,  1.567038531 ,  2.272036098 ],
+                     [  3.831397382 ,  0.624347564 ,  0.333339655 ],
+                     [  3.821230075 ,  1.633662034 ,  -0.673499279 ],
+                     [  3.737606155 ,  2.698030593 ,  -0.533251753 ],
+                     [  3.951817138 ,  0.992808148 ,  -1.884517470 ],
+                     [  4.057522768 ,  -0.360086596 ,  -1.675422891 ],
+                     [  3.973342858 ,  -0.624347564 ,  -0.333339655 ],
+                     [  4.021880657 ,  -1.839842986 ,  0.351754941 ],
+                     [  4.128699483 ,  -2.780217935 ,  -0.172940228 ],
+                     [  3.917109398 ,  -1.796211682 ,  1.733036170 ],
+                     [  3.948740974 ,  -2.718261443 ,  2.297634930 ],
+                     [  4.079468836 ,  -1.056446212 ,  -2.398978775 ],
+                     [  3.988034161 ,  1.398036276 ,  -2.881807744 ]],
+    'positions 1.5':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.044485647 ,  -1.177978626 ,  0.743160105 ],
+                     [  -0.010824638 ,  -2.411208517 ,  0.095333145 ],
+                     [  0.064150773 ,  -2.466933785 ,  -1.295623602 ],
+                     [  0.100950904 ,  -1.287437054 ,  -2.038959973 ],
+                     [  0.067356799 ,  -0.053500209 ,  -1.391376263 ],
+                     [  -0.013797739 ,  0.956881587 ,  0.503348328 ],
+                     [  -0.091346970 ,  -1.134458005 ,  1.822398921 ],
+                     [  -0.039754009 ,  -3.325680275 ,  0.672358669 ],
+                     [  0.085389531 ,  -3.424849020 ,  -1.798373823 ],
+                     [  0.146442780 ,  -1.330172544 ,  -3.119514770 ],
+                     [  0.100852832 ,  0.862456237 ,  -1.964945566 ],
+                     [  4.668951087 ,  -0.578056849 ,  3.494904751 ],
+                     [  4.744693458 ,  -0.571969873 ,  2.415753956 ],
+                     [  4.704239396 ,  0.633650134 ,  1.734349558 ],
+                     [  4.597120918 ,  1.567038531 ,  2.272036098 ],
+                     [  4.806989912 ,  0.624347564 ,  0.333339655 ],
+                     [  4.796822605 ,  1.633662034 ,  -0.673499279 ],
+                     [  4.713198685 ,  2.698030593 ,  -0.533251753 ],
+                     [  4.927409668 ,  0.992808148 ,  -1.884517470 ],
+                     [  5.033115298 ,  -0.360086596 ,  -1.675422891 ],
+                     [  4.948935388 ,  -0.624347564 ,  -0.333339655 ],
+                     [  4.997473187 ,  -1.839842986 ,  0.351754941 ],
+                     [  5.104292013 ,  -2.780217935 ,  -0.172940228 ],
+                     [  4.892701928 ,  -1.796211682 ,  1.733036170 ],
+                     [  4.924333504 ,  -2.718261443 ,  2.297634930 ],
+                     [  5.055061366 ,  -1.056446212 ,  -2.398978775 ],
+                     [  4.963626691 ,  1.398036276 ,  -2.881807744 ]],
+    'positions 2.0':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.044485647 ,  -1.177978626 ,  0.743160105 ],
+                     [  -0.010824638 ,  -2.411208517 ,  0.095333145 ],
+                     [  0.064150773 ,  -2.466933785 ,  -1.295623602 ],
+                     [  0.100950904 ,  -1.287437054 ,  -2.038959973 ],
+                     [  0.067356799 ,  -0.053500209 ,  -1.391376263 ],
+                     [  -0.013797739 ,  0.956881587 ,  0.503348328 ],
+                     [  -0.091346970 ,  -1.134458005 ,  1.822398921 ],
+                     [  -0.039754009 ,  -3.325680275 ,  0.672358669 ],
+                     [  0.085389531 ,  -3.424849020 ,  -1.798373823 ],
+                     [  0.146442780 ,  -1.330172544 ,  -3.119514770 ],
+                     [  0.100852832 ,  0.862456237 ,  -1.964945566 ],
+                     [  6.294938637 ,  -0.578056849 ,  3.494904751 ],
+                     [  6.370681008 ,  -0.571969873 ,  2.415753956 ],
+                     [  6.330226946 ,  0.633650134 ,  1.734349558 ],
+                     [  6.223108468 ,  1.567038531 ,  2.272036098 ],
+                     [  6.432977462 ,  0.624347564 ,  0.333339655 ],
+                     [  6.422810155 ,  1.633662034 ,  -0.673499279 ],
+                     [  6.339186235 ,  2.698030593 ,  -0.533251753 ],
+                     [  6.553397218 ,  0.992808148 ,  -1.884517470 ],
+                     [  6.659102848 ,  -0.360086596 ,  -1.675422891 ],
+                     [  6.574922938 ,  -0.624347564 ,  -0.333339655 ],
+                     [  6.623460737 ,  -1.839842986 ,  0.351754941 ],
+                     [  6.730279563 ,  -2.780217935 ,  -0.172940228 ],
+                     [  6.518689478 ,  -1.796211682 ,  1.733036170 ],
+                     [  6.550321054 ,  -2.718261443 ,  2.297634930 ],
+                     [  6.681048916 ,  -1.056446212 ,  -2.398978775 ],
+                     [  6.589614241 ,  1.398036276 ,  -2.881807744 ]]},
+
+'Indole-benzene_T-shape_complex': {
+    'description': "Complex, S22, S26",
+    'name': "Indole-benzene_T-shape_complex",
+    's26_number': "21",
+    'interaction energy CC':-0.2437,
+    'interaction energies s22x5':[-0.2164,-0.2489,-0.2116,-0.1214,-0.0477],
+    'offset': -0.0052,
+    'symbols': 'CCCCCCHHHHHHHNCCCCCCCCHHHHHH',
+    'magmoms': None,
+    'dimer atoms': [12,16],
+    # Optimisation level:  MP2/cc-pVTZ
+    'positions':[[  2.5118997,  1.6250148,  0.0000000],
+                 [  2.7130094,  0.9578537, -1.2082918],
+                 [  3.1177821, -0.3767436, -1.2083647],
+                 [  3.3213848, -1.0437307,  0.0000000],
+                 [  3.1177821, -0.3767436,  1.2083647],
+                 [  2.7130094,  0.9578537,  1.2082918],
+                 [  2.2024038,  2.6611358,  0.0000000],
+                 [  2.5511760,  1.4736908, -2.1445900],
+                 [  3.2702999, -0.8951406, -2.1448379],
+                 [  3.6368139, -2.0781521,  0.0000000],
+                 [  3.2702999, -0.8951406,  2.1448379],
+                 [  2.5511760,  1.4736908,  2.1445900],
+                 [  0.8065245, -0.4358866,  0.0000000],
+                 [ -0.1442408, -0.7686927,  0.0000000],
+                 [ -0.5161122, -2.0893220,  0.0000000],
+                 [ -1.8898755, -2.1814495,  0.0000000],
+                 [ -2.3932317, -0.8470830,  0.0000000],
+                 [ -1.2640653,  0.0195887,  0.0000000],
+                 [ -1.3896004,  1.4117668,  0.0000000],
+                 [ -2.6726501,  1.9366450,  0.0000000],
+                 [ -3.8054511,  1.0974790,  0.0000000],
+                 [ -3.6798167, -0.2817209,  0.0000000],
+                 [  0.2310024, -2.8653173,  0.0000000],
+                 [ -2.4585759, -3.0956052,  0.0000000],
+                 [ -0.5188733,  2.0539520,  0.0000000],
+                 [ -2.8077570,  3.0097859,  0.0000000],
+                 [ -4.7905991,  1.5439372,  0.0000000],
+                 [ -4.5580187, -0.9142916,  0.0000000]],
+    'positions 0.9':[[  -0.052652077 ,  -1.393225783 ,  0.000000000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  -1.208292000 ],
+                     [  0.026348254 ,  0.696724226 ,  -1.208365000 ],
+                     [  0.051042263 ,  1.393657541 ,  0.000000000 ],
+                     [  0.026348254 ,  0.696724226 ,  1.208365000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  1.208292000 ],
+                     [  -0.097430661 ,  -2.473655966 ,  0.000000000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  -2.144590000 ],
+                     [  0.050955575 ,  1.236531293 ,  -2.144838000 ],
+                     [  0.089657645 ,  2.474412421 ,  0.000000000 ],
+                     [  0.050955575 ,  1.236531293 ,  2.144838000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  2.144590000 ],
+                     [  2.007797424 ,  0.000000000 ,  0.000000000 ],
+                     [  3.015114828 ,  0.005056388 ,  0.000000000 ],
+                     [  3.796769012 ,  1.132604937 ,  0.000000000 ],
+                     [  5.125653739 ,  0.772354616 ,  0.000000000 ],
+                     [  5.167047225 ,  -0.653193161 ,  0.000000000 ],
+                     [  3.817202589 ,  -1.104920876 ,  0.000000000 ],
+                     [  3.482542920 ,  -2.462094972 ,  0.000000000 ],
+                     [  4.524735226 ,  -3.376178892 ,  0.000000000 ],
+                     [  5.869058665 ,  -2.951641292 ,  0.000000000 ],
+                     [  6.199398544 ,  -1.606705567 ,  0.000000000 ],
+                     [  3.343074787 ,  2.109594763 ,  0.000000000 ],
+                     [  5.961043541 ,  1.451489921 ,  0.000000000 ],
+                     [  2.450153978 ,  -2.785730808 ,  0.000000000 ],
+                     [  4.303017780 ,  -4.434822780 ,  0.000000000 ],
+                     [  6.655123584 ,  -3.694570139 ,  0.000000000 ],
+                     [  7.235724321 ,  -1.294593877 ,  0.000000000 ]],
+    'positions 1.0':[[  -0.052652077000000 ,  -1.393225783000000 ,  0.000000000000000 ],
+                     [  -0.025543347000000 ,  -0.696940104000000 ,  -1.208292000000000 ],
+                     [  0.026348254000000 ,  0.696724226000000 ,  -1.208365000000000 ],
+                     [  0.051042263000000 ,  1.393657541000000 ,  0.000000000000000 ],
+                     [  0.026348254000000 ,  0.696724226000000 ,  1.208365000000000 ],
+                     [  -0.025543347000000 ,  -0.696940104000000 ,  1.208292000000000 ],
+                     [  -0.097430661000000 ,  -2.473655966000000 ,  0.000000000000000 ],
+                     [  -0.040509756000000 ,  -1.237360068000000 ,  -2.144590000000000 ],
+                     [  0.050955575000000 ,  1.236531293000000 ,  -2.144838000000000 ],
+                     [  0.089657645000000 ,  2.474412421000000 ,  0.000000000000000 ],
+                     [  0.050955575000000 ,  1.236531293000000 ,  2.144838000000000 ],
+                     [  -0.040509756000000 ,  -1.237360068000000 ,  2.144590000000000 ],
+                     [  2.230886026727273 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  3.238203430727273 ,  0.005056388000000 ,  0.000000000000000 ],
+                     [  4.019857614727273 ,  1.132604937000000 ,  0.000000000000000 ],
+                     [  5.348742341727273 ,  0.772354616000000 ,  0.000000000000000 ],
+                     [  5.390135827727273 ,  -0.653193161000000 ,  0.000000000000000 ],
+                     [  4.040291191727273 ,  -1.104920876000000 ,  0.000000000000000 ],
+                     [  3.705631522727273 ,  -2.462094972000000 ,  0.000000000000000 ],
+                     [  4.747823828727273 ,  -3.376178892000000 ,  0.000000000000000 ],
+                     [  6.092147267727273 ,  -2.951641292000000 ,  0.000000000000000 ],
+                     [  6.422487146727273 ,  -1.606705567000000 ,  0.000000000000000 ],
+                     [  3.566163389727273 ,  2.109594763000000 ,  0.000000000000000 ],
+                     [  6.184132143727273 ,  1.451489921000000 ,  0.000000000000000 ],
+                     [  2.673242580727273 ,  -2.785730808000000 ,  0.000000000000000 ],
+                     [  4.526106382727273 ,  -4.434822780000000 ,  0.000000000000000 ],
+                     [  6.878212186727272 ,  -3.694570139000000 ,  0.000000000000000 ],
+                     [  7.458812923727273 ,  -1.294593877000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  -0.052652077 ,  -1.393225783 ,  0.000000000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  -1.208292000 ],
+                     [  0.026348254 ,  0.696724226 ,  -1.208365000 ],
+                     [  0.051042263 ,  1.393657541 ,  0.000000000 ],
+                     [  0.026348254 ,  0.696724226 ,  1.208365000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  1.208292000 ],
+                     [  -0.097430661 ,  -2.473655966 ,  0.000000000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  -2.144590000 ],
+                     [  0.050955575 ,  1.236531293 ,  -2.144838000 ],
+                     [  0.089657645 ,  2.474412421 ,  0.000000000 ],
+                     [  0.050955575 ,  1.236531293 ,  2.144838000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  2.144590000 ],
+                     [  2.677063232 ,  0.000000000 ,  0.000000000 ],
+                     [  3.684380636 ,  0.005056388 ,  0.000000000 ],
+                     [  4.466034820 ,  1.132604937 ,  0.000000000 ],
+                     [  5.794919547 ,  0.772354616 ,  0.000000000 ],
+                     [  5.836313033 ,  -0.653193161 ,  0.000000000 ],
+                     [  4.486468397 ,  -1.104920876 ,  0.000000000 ],
+                     [  4.151808728 ,  -2.462094972 ,  0.000000000 ],
+                     [  5.194001034 ,  -3.376178892 ,  0.000000000 ],
+                     [  6.538324473 ,  -2.951641292 ,  0.000000000 ],
+                     [  6.868664352 ,  -1.606705567 ,  0.000000000 ],
+                     [  4.012340595 ,  2.109594763 ,  0.000000000 ],
+                     [  6.630309349 ,  1.451489921 ,  0.000000000 ],
+                     [  3.119419786 ,  -2.785730808 ,  0.000000000 ],
+                     [  4.972283588 ,  -4.434822780 ,  0.000000000 ],
+                     [  7.324389392 ,  -3.694570139 ,  0.000000000 ],
+                     [  7.904990129 ,  -1.294593877 ,  0.000000000 ]],
+    'positions 1.5':[[  -0.052652077 ,  -1.393225783 ,  0.000000000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  -1.208292000 ],
+                     [  0.026348254 ,  0.696724226 ,  -1.208365000 ],
+                     [  0.051042263 ,  1.393657541 ,  0.000000000 ],
+                     [  0.026348254 ,  0.696724226 ,  1.208365000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  1.208292000 ],
+                     [  -0.097430661 ,  -2.473655966 ,  0.000000000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  -2.144590000 ],
+                     [  0.050955575 ,  1.236531293 ,  -2.144838000 ],
+                     [  0.089657645 ,  2.474412421 ,  0.000000000 ],
+                     [  0.050955575 ,  1.236531293 ,  2.144838000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  2.144590000 ],
+                     [  3.346329040 ,  0.000000000 ,  0.000000000 ],
+                     [  4.353646444 ,  0.005056388 ,  0.000000000 ],
+                     [  5.135300628 ,  1.132604937 ,  0.000000000 ],
+                     [  6.464185355 ,  0.772354616 ,  0.000000000 ],
+                     [  6.505578841 ,  -0.653193161 ,  0.000000000 ],
+                     [  5.155734205 ,  -1.104920876 ,  0.000000000 ],
+                     [  4.821074536 ,  -2.462094972 ,  0.000000000 ],
+                     [  5.863266842 ,  -3.376178892 ,  0.000000000 ],
+                     [  7.207590281 ,  -2.951641292 ,  0.000000000 ],
+                     [  7.537930160 ,  -1.606705567 ,  0.000000000 ],
+                     [  4.681606403 ,  2.109594763 ,  0.000000000 ],
+                     [  7.299575157 ,  1.451489921 ,  0.000000000 ],
+                     [  3.788685594 ,  -2.785730808 ,  0.000000000 ],
+                     [  5.641549396 ,  -4.434822780 ,  0.000000000 ],
+                     [  7.993655200 ,  -3.694570139 ,  0.000000000 ],
+                     [  8.574255937 ,  -1.294593877 ,  0.000000000 ]],
+    'positions 2.0':[[  -0.052652077 ,  -1.393225783 ,  0.000000000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  -1.208292000 ],
+                     [  0.026348254 ,  0.696724226 ,  -1.208365000 ],
+                     [  0.051042263 ,  1.393657541 ,  0.000000000 ],
+                     [  0.026348254 ,  0.696724226 ,  1.208365000 ],
+                     [  -0.025543347 ,  -0.696940104 ,  1.208292000 ],
+                     [  -0.097430661 ,  -2.473655966 ,  0.000000000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  -2.144590000 ],
+                     [  0.050955575 ,  1.236531293 ,  -2.144838000 ],
+                     [  0.089657645 ,  2.474412421 ,  0.000000000 ],
+                     [  0.050955575 ,  1.236531293 ,  2.144838000 ],
+                     [  -0.040509756 ,  -1.237360068 ,  2.144590000 ],
+                     [  4.461772054 ,  0.000000000 ,  0.000000000 ],
+                     [  5.469089458 ,  0.005056388 ,  0.000000000 ],
+                     [  6.250743642 ,  1.132604937 ,  0.000000000 ],
+                     [  7.579628369 ,  0.772354616 ,  0.000000000 ],
+                     [  7.621021855 ,  -0.653193161 ,  0.000000000 ],
+                     [  6.271177219 ,  -1.104920876 ,  0.000000000 ],
+                     [  5.936517550 ,  -2.462094972 ,  0.000000000 ],
+                     [  6.978709856 ,  -3.376178892 ,  0.000000000 ],
+                     [  8.323033295 ,  -2.951641292 ,  0.000000000 ],
+                     [  8.653373174 ,  -1.606705567 ,  0.000000000 ],
+                     [  5.797049417 ,  2.109594763 ,  0.000000000 ],
+                     [  8.415018171 ,  1.451489921 ,  0.000000000 ],
+                     [  4.904128608 ,  -2.785730808 ,  0.000000000 ],
+                     [  6.756992410 ,  -4.434822780 ,  0.000000000 ],
+                     [  9.109098214 ,  -3.694570139 ,  0.000000000 ],
+                     [  9.689698951 ,  -1.294593877 ,  0.000000000 ]]},
+
+'Methane_dimer': {
+    'description': "Complex, S22, S26, dispersion bonded",
+    'name': "Methane_dimer",
+    's26_number': "08",
+    'interaction energy CC':-0.0230,
+    'interaction energies s22x5':[-0.0147,-0.0230,-0.0108,-0.0026,-0.0004],
+    'offset': 0.0000,
+    'symbols': 'CHHHHCHHHH',
+    'magmoms': None,
+    'dimer atoms': [5,5],
+    # Optimisation level:  CCSD(T)/cc-pVTZ
+    'positions':[[  0.000000, -0.000140,  1.859161],
+                 [ -0.888551,  0.513060,  1.494685],
+                 [  0.888551,  0.513060,  1.494685],
+                 [  0.000000, -1.026339,  1.494868],
+                 [  0.000000,  0.000089,  2.948284],
+                 [  0.000000,  0.000140, -1.859161],
+                 [  0.000000, -0.000089, -2.948284],
+                 [ -0.888551, -0.513060, -1.494685],
+                 [  0.888551, -0.513060, -1.494685],
+                 [  0.000000,  1.026339, -1.494868]],
+    'positions 0.9':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  0.364514644 ,  0.513239461 ,  -0.888512354 ],
+                     [  0.364514644 ,  0.513105641 ,  0.888589641 ],
+                     [  0.364215723 ,  -1.026226426 ,  -0.000077278 ],
+                     [  -1.089122980 ,  0.000311014 ,  0.000000023 ],
+                     [  3.346489810 ,  0.000000000 ,  0.000000000 ],
+                     [  4.435612789 ,  -0.000311014 ,  -0.000000023 ],
+                     [  2.981975165 ,  -0.513105641 ,  -0.888589641 ],
+                     [  2.981975165 ,  -0.513239461 ,  0.888512354 ],
+                     [  2.982274086 ,  1.026226426 ,  0.000077278 ]],
+    'positions 1.0':[[  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  0.364514644000000 ,  0.513239461000000 ,  -0.888512354000000 ],
+                     [  0.364514644000000 ,  0.513105641000000 ,  0.888589641000000 ],
+                     [  0.364215723000000 ,  -1.026226426000000 ,  -0.000077278000000 ],
+                     [  -1.089122980000000 ,  0.000311014000000 ,  0.000000023000000 ],
+                     [  3.718322011090909 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  4.807444990090909 ,  -0.000311014000000 ,  -0.000000023000000 ],
+                     [  3.353807366090909 ,  -0.513105641000000 ,  -0.888589641000000 ],
+                     [  3.353807366090909 ,  -0.513239461000000 ,  0.888512354000000 ],
+                     [  3.354106287090909 ,  1.026226426000000 ,  0.000077278000000 ]],
+    'positions 1.2':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  0.364514644 ,  0.513239461 ,  -0.888512354 ],
+                     [  0.364514644 ,  0.513105641 ,  0.888589641 ],
+                     [  0.364215723 ,  -1.026226426 ,  -0.000077278 ],
+                     [  -1.089122980 ,  0.000311014 ,  0.000000023 ],
+                     [  4.461986413 ,  0.000000000 ,  0.000000000 ],
+                     [  5.551109392 ,  -0.000311014 ,  -0.000000023 ],
+                     [  4.097471768 ,  -0.513105641 ,  -0.888589641 ],
+                     [  4.097471768 ,  -0.513239461 ,  0.888512354 ],
+                     [  4.097770689 ,  1.026226426 ,  0.000077278 ]],
+    'positions 1.5':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  0.364514644 ,  0.513239461 ,  -0.888512354 ],
+                     [  0.364514644 ,  0.513105641 ,  0.888589641 ],
+                     [  0.364215723 ,  -1.026226426 ,  -0.000077278 ],
+                     [  -1.089122980 ,  0.000311014 ,  0.000000023 ],
+                     [  5.577483016 ,  0.000000000 ,  0.000000000 ],
+                     [  6.666605995 ,  -0.000311014 ,  -0.000000023 ],
+                     [  5.212968371 ,  -0.513105641 ,  -0.888589641 ],
+                     [  5.212968371 ,  -0.513239461 ,  0.888512354 ],
+                     [  5.213267292 ,  1.026226426 ,  0.000077278 ]],
+    'positions 2.0':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  0.364514644 ,  0.513239461 ,  -0.888512354 ],
+                     [  0.364514644 ,  0.513105641 ,  0.888589641 ],
+                     [  0.364215723 ,  -1.026226426 ,  -0.000077278 ],
+                     [  -1.089122980 ,  0.000311014 ,  0.000000023 ],
+                     [  7.436644022 ,  0.000000000 ,  0.000000000 ],
+                     [  8.525767001 ,  -0.000311014 ,  -0.000000023 ],
+                     [  7.072129377 ,  -0.513105641 ,  -0.888589641 ],
+                     [  7.072129377 ,  -0.513239461 ,  0.888512354 ],
+                     [  7.072428298 ,  1.026226426 ,  0.000077278 ]]},
+
+'Phenol_dimer': {
+    'description': "Complex, S22, S26",
+    'name': "Phenol_dimer",
+    's26_number': "22",
+    'interaction energy CC':-0.3075,
+    'interaction energies s22x5':[-0.2784,-0.3057,-0.2511,-0.1479,-0.0598],
+    'offset': 0.0018,
+    'symbols': 'COHCCCCCHHHHHOCHCCCCCHHHHH',
+    'magmoms': None,
+    'dimer atoms': [13,13],
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[ -2.0071056,  0.7638459, -0.1083509],
+                 [ -1.3885044,  1.9298523, -0.4431206],
+                 [ -0.5238121,  1.9646519, -0.0064609],
+                 [ -1.4630807, -0.1519120,  0.7949930],
+                 [ -2.1475789, -1.3295094,  1.0883677],
+                 [ -3.3743208, -1.6031427,  0.4895864],
+                 [ -3.9143727, -0.6838545, -0.4091028],
+                 [ -3.2370496,  0.4929609, -0.7096126],
+                 [ -0.5106510,  0.0566569,  1.2642563],
+                 [ -1.7151135, -2.0321452,  1.7878417],
+                 [ -3.9024664, -2.5173865,  0.7197947],
+                 [ -4.8670730, -0.8822939, -0.8811319],
+                 [ -3.6431662,  1.2134345, -1.4057590],
+                 [  1.3531168,  1.9382724,  0.4723133],
+                 [  2.0369747,  0.7865043,  0.1495491],
+                 [  1.7842846,  2.3487495,  1.2297110],
+                 [  1.5904026,  0.0696860, -0.9574153],
+                 [  2.2417367, -1.1069765, -1.3128110],
+                 [  3.3315674, -1.5665603, -0.5748636],
+                 [  3.7696838, -0.8396901,  0.5286439],
+                 [  3.1224836,  0.3383498,  0.8960491],
+                 [  0.7445512,  0.4367983, -1.5218583],
+                 [  1.8921463, -1.6649726, -2.1701843],
+                 [  3.8330227, -2.4811537, -0.8566666],
+                 [  4.6137632, -1.1850101,  1.1092635],
+                 [  3.4598854,  0.9030376,  1.7569489]],
+    'positions 0.9':[[  -1.445967355 ,  -1.221065858 ,  0.265808750 ],
+                     [  -0.945229913 ,  -0.047318091 ,  -0.209467563 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.683142700 ,  -2.127785201 ,  1.005109011 ],
+                     [  -1.257798399 ,  -3.314090975 ,  1.456540663 ],
+                     [  -2.590627730 ,  -3.605427919 ,  1.179051667 ],
+                     [  -3.348500619 ,  -2.695116849 ,  0.443286115 ],
+                     [  -2.782549405 ,  -1.509701903 ,  -0.013287247 ],
+                     [  0.352786431 ,  -1.905463972 ,  1.224781047 ],
+                     [  -0.656349187 ,  -4.009576034 ,  2.026231320 ],
+                     [  -3.032993188 ,  -4.526384329 ,  1.531085059 ],
+                     [  -4.385512900 ,  -2.907317436 ,  0.221017935 ],
+                     [  -3.357888956 ,  -0.796017014 ,  -0.586234960 ],
+                     [  1.743489077 ,  0.000000000 ,  0.000000000 ],
+                     [  2.341981491 ,  -1.142898789 ,  -0.483732445 ],
+                     [  2.342838533 ,  0.417604441 ,  0.628041164 ],
+                     [  1.645485086 ,  -1.867622674 ,  -1.447211527 ],
+                     [  2.204739700 ,  -3.035912794 ,  -1.954567993 ],
+                     [  3.449296078 ,  -3.479350313 ,  -1.509647408 ],
+                     [  4.136609561 ,  -2.744696418 ,  -0.547410307 ],
+                     [  3.584309534 ,  -1.574952605 ,  -0.029436748 ],
+                     [  0.681454799 ,  -1.513028491 ,  -1.784467064 ],
+                     [  1.661729182 ,  -3.600082357 ,  -2.699896207 ],
+                     [  3.877956013 ,  -4.387511286 ,  -1.908204233 ],
+                     [  5.102623102 ,  -3.077497147 ,  -0.194005162 ],
+                     [  4.116289930 ,  -1.004251641 ,  0.722333197 ]],
+    'positions 1.0':[[  -1.445967355000000 ,  -1.221065858000000 ,  0.265808750000000 ],
+                     [  -0.945229913000000 ,  -0.047318091000000 ,  -0.209467563000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -0.683142700000000 ,  -2.127785201000000 ,  1.005109011000000 ],
+                     [  -1.257798399000000 ,  -3.314090975000000 ,  1.456540663000000 ],
+                     [  -2.590627730000000 ,  -3.605427919000000 ,  1.179051667000000 ],
+                     [  -3.348500619000000 ,  -2.695116849000000 ,  0.443286115000000 ],
+                     [  -2.782549405000000 ,  -1.509701903000000 ,  -0.013287247000000 ],
+                     [  0.352786431000000 ,  -1.905463972000000 ,  1.224781047000000 ],
+                     [  -0.656349187000000 ,  -4.009576034000000 ,  2.026231320000000 ],
+                     [  -3.032993188000000 ,  -4.526384329000000 ,  1.531085059000000 ],
+                     [  -4.385512900000000 ,  -2.907317436000000 ,  0.221017935000000 ],
+                     [  -3.357888956000000 ,  -0.796017014000000 ,  -0.586234960000000 ],
+                     [  1.937210085636364 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.535702499636364 ,  -1.142898789000000 ,  -0.483732445000000 ],
+                     [  2.536559541636364 ,  0.417604441000000 ,  0.628041164000000 ],
+                     [  1.839206094636364 ,  -1.867622674000000 ,  -1.447211527000000 ],
+                     [  2.398460708636364 ,  -3.035912794000000 ,  -1.954567993000000 ],
+                     [  3.643017086636364 ,  -3.479350313000000 ,  -1.509647408000000 ],
+                     [  4.330330569636364 ,  -2.744696418000000 ,  -0.547410307000000 ],
+                     [  3.778030542636364 ,  -1.574952605000000 ,  -0.029436748000000 ],
+                     [  0.875175807636364 ,  -1.513028491000000 ,  -1.784467064000000 ],
+                     [  1.855450190636364 ,  -3.600082357000000 ,  -2.699896207000000 ],
+                     [  4.071677021636363 ,  -4.387511286000000 ,  -1.908204233000000 ],
+                     [  5.296344110636364 ,  -3.077497147000000 ,  -0.194005162000000 ],
+                     [  4.310010938636363 ,  -1.004251641000000 ,  0.722333197000000 ]],
+    'positions 1.2':[[  -1.445967355 ,  -1.221065858 ,  0.265808750 ],
+                     [  -0.945229913 ,  -0.047318091 ,  -0.209467563 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.683142700 ,  -2.127785201 ,  1.005109011 ],
+                     [  -1.257798399 ,  -3.314090975 ,  1.456540663 ],
+                     [  -2.590627730 ,  -3.605427919 ,  1.179051667 ],
+                     [  -3.348500619 ,  -2.695116849 ,  0.443286115 ],
+                     [  -2.782549405 ,  -1.509701903 ,  -0.013287247 ],
+                     [  0.352786431 ,  -1.905463972 ,  1.224781047 ],
+                     [  -0.656349187 ,  -4.009576034 ,  2.026231320 ],
+                     [  -3.032993188 ,  -4.526384329 ,  1.531085059 ],
+                     [  -4.385512900 ,  -2.907317436 ,  0.221017935 ],
+                     [  -3.357888956 ,  -0.796017014 ,  -0.586234960 ],
+                     [  2.324652103 ,  0.000000000 ,  0.000000000 ],
+                     [  2.923144517 ,  -1.142898789 ,  -0.483732445 ],
+                     [  2.924001559 ,  0.417604441 ,  0.628041164 ],
+                     [  2.226648112 ,  -1.867622674 ,  -1.447211527 ],
+                     [  2.785902726 ,  -3.035912794 ,  -1.954567993 ],
+                     [  4.030459104 ,  -3.479350313 ,  -1.509647408 ],
+                     [  4.717772587 ,  -2.744696418 ,  -0.547410307 ],
+                     [  4.165472560 ,  -1.574952605 ,  -0.029436748 ],
+                     [  1.262617825 ,  -1.513028491 ,  -1.784467064 ],
+                     [  2.242892208 ,  -3.600082357 ,  -2.699896207 ],
+                     [  4.459119039 ,  -4.387511286 ,  -1.908204233 ],
+                     [  5.683786128 ,  -3.077497147 ,  -0.194005162 ],
+                     [  4.697452956 ,  -1.004251641 ,  0.722333197 ]],
+    'positions 1.5':[[  -1.445967355 ,  -1.221065858 ,  0.265808750 ],
+                     [  -0.945229913 ,  -0.047318091 ,  -0.209467563 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.683142700 ,  -2.127785201 ,  1.005109011 ],
+                     [  -1.257798399 ,  -3.314090975 ,  1.456540663 ],
+                     [  -2.590627730 ,  -3.605427919 ,  1.179051667 ],
+                     [  -3.348500619 ,  -2.695116849 ,  0.443286115 ],
+                     [  -2.782549405 ,  -1.509701903 ,  -0.013287247 ],
+                     [  0.352786431 ,  -1.905463972 ,  1.224781047 ],
+                     [  -0.656349187 ,  -4.009576034 ,  2.026231320 ],
+                     [  -3.032993188 ,  -4.526384329 ,  1.531085059 ],
+                     [  -4.385512900 ,  -2.907317436 ,  0.221017935 ],
+                     [  -3.357888956 ,  -0.796017014 ,  -0.586234960 ],
+                     [  2.905815129 ,  0.000000000 ,  0.000000000 ],
+                     [  3.504307543 ,  -1.142898789 ,  -0.483732445 ],
+                     [  3.505164585 ,  0.417604441 ,  0.628041164 ],
+                     [  2.807811138 ,  -1.867622674 ,  -1.447211527 ],
+                     [  3.367065752 ,  -3.035912794 ,  -1.954567993 ],
+                     [  4.611622130 ,  -3.479350313 ,  -1.509647408 ],
+                     [  5.298935613 ,  -2.744696418 ,  -0.547410307 ],
+                     [  4.746635586 ,  -1.574952605 ,  -0.029436748 ],
+                     [  1.843780851 ,  -1.513028491 ,  -1.784467064 ],
+                     [  2.824055234 ,  -3.600082357 ,  -2.699896207 ],
+                     [  5.040282065 ,  -4.387511286 ,  -1.908204233 ],
+                     [  6.264949154 ,  -3.077497147 ,  -0.194005162 ],
+                     [  5.278615982 ,  -1.004251641 ,  0.722333197 ]],
+    'positions 2.0':[[  -1.445967355 ,  -1.221065858 ,  0.265808750 ],
+                     [  -0.945229913 ,  -0.047318091 ,  -0.209467563 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.683142700 ,  -2.127785201 ,  1.005109011 ],
+                     [  -1.257798399 ,  -3.314090975 ,  1.456540663 ],
+                     [  -2.590627730 ,  -3.605427919 ,  1.179051667 ],
+                     [  -3.348500619 ,  -2.695116849 ,  0.443286115 ],
+                     [  -2.782549405 ,  -1.509701903 ,  -0.013287247 ],
+                     [  0.352786431 ,  -1.905463972 ,  1.224781047 ],
+                     [  -0.656349187 ,  -4.009576034 ,  2.026231320 ],
+                     [  -3.032993188 ,  -4.526384329 ,  1.531085059 ],
+                     [  -4.385512900 ,  -2.907317436 ,  0.221017935 ],
+                     [  -3.357888956 ,  -0.796017014 ,  -0.586234960 ],
+                     [  3.874420172 ,  0.000000000 ,  0.000000000 ],
+                     [  4.472912586 ,  -1.142898789 ,  -0.483732445 ],
+                     [  4.473769628 ,  0.417604441 ,  0.628041164 ],
+                     [  3.776416181 ,  -1.867622674 ,  -1.447211527 ],
+                     [  4.335670795 ,  -3.035912794 ,  -1.954567993 ],
+                     [  5.580227173 ,  -3.479350313 ,  -1.509647408 ],
+                     [  6.267540656 ,  -2.744696418 ,  -0.547410307 ],
+                     [  5.715240629 ,  -1.574952605 ,  -0.029436748 ],
+                     [  2.812385894 ,  -1.513028491 ,  -1.784467064 ],
+                     [  3.792660277 ,  -3.600082357 ,  -2.699896207 ],
+                     [  6.008887108 ,  -4.387511286 ,  -1.908204233 ],
+                     [  7.233554197 ,  -3.077497147 ,  -0.194005162 ],
+                     [  6.247221025 ,  -1.004251641 ,  0.722333197 ]]},
+
+'Pyrazine_dimer': {
+    'description': "Complex, S22, S26, dispersion bonded",
+    'name': "Pyrazine_dimer",
+    's26_number': "12",
+    'interaction energy CC':-0.1821,
+    'interaction energies s22x5':[-0.0733,-0.1956,-0.1310,-0.0425,-0.0082],
+    'offset': -0.0135,
+    'symbols': 'CCNCCNHHHHCCNCCNHHHH',
+    'magmoms': None,
+    'dimer atoms': [10,10],
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[ -1.2471894, -1.1718212, -0.6961388],
+                 [ -1.2471894, -1.1718212,  0.6961388],
+                 [ -0.2589510, -1.7235771,  1.4144796],
+                 [  0.7315327, -2.2652221,  0.6967288],
+                 [  0.7315327, -2.2652221, -0.6967288],
+                 [ -0.2589510, -1.7235771, -1.4144796],
+                 [ -2.0634363, -0.7223199, -1.2472797],
+                 [ -2.0634363, -0.7223199,  1.2472797],
+                 [  1.5488004, -2.7128282,  1.2475604],
+                 [  1.5488004, -2.7128282, -1.2475604],
+                 [ -0.3380031,  2.0800608,  1.1300452],
+                 [  0.8540254,  1.3593471,  1.1306308],
+                 [  1.4701787,  0.9907598,  0.0000000],
+                 [  0.8540254,  1.3593471, -1.1306308],
+                 [ -0.3380031,  2.0800608, -1.1300452],
+                 [ -0.9523059,  2.4528836,  0.0000000],
+                 [ -0.8103758,  2.3643033,  2.0618643],
+                 [  1.3208583,  1.0670610,  2.0623986],
+                 [  1.3208583,  1.0670610, -2.0623986],
+                 [ -0.8103758,  2.3643033, -2.0618643]],
+    'positions 0.9':[[  0.395653045 ,  1.059432142 ,  -0.696139000 ],
+                     [  0.395653045 ,  1.059432142 ,  0.696139000 ],
+                     [  -0.003263357 ,  0.000227377 ,  1.414480000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  0.696729000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  -0.696729000 ],
+                     [  -0.003263357 ,  0.000227377 ,  -1.414480000 ],
+                     [  0.718983381 ,  1.933370245 ,  -1.247280000 ],
+                     [  0.718983381 ,  1.933370245 ,  1.247280000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  1.247560000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  -1.247560000 ],
+                     [  3.398538200 ,  0.643131999 ,  1.130045000 ],
+                     [  2.862793235 ,  -0.642689433 ,  1.130631000 ],
+                     [  2.589772167 ,  -1.306738847 ,  0.000000000 ],
+                     [  2.862793235 ,  -0.642689433 ,  -1.130631000 ],
+                     [  3.398538200 ,  0.643131999 ,  -1.130045000 ],
+                     [  3.676023139 ,  1.305979850 ,  0.000000000 ],
+                     [  3.609496345 ,  1.152471205 ,  2.061864000 ],
+                     [  2.643057716 ,  -1.147744338 ,  2.062399000 ],
+                     [  2.643057716 ,  -1.147744338 ,  -2.062399000 ],
+                     [  3.609496345 ,  1.152471205 ,  -2.061864000 ]],
+    'positions 1.0':[[  0.395653045000000 ,  1.059432142000000 ,  -0.696139000000000 ],
+                     [  0.395653045000000 ,  1.059432142000000 ,  0.696139000000000 ],
+                     [  -0.003263357000000 ,  0.000227377000000 ,  1.414480000000000 ],
+                     [  -0.391847355000000 ,  -1.059697307000000 ,  0.696729000000000 ],
+                     [  -0.391847355000000 ,  -1.059697307000000 ,  -0.696729000000000 ],
+                     [  -0.003263357000000 ,  0.000227377000000 ,  -1.414480000000000 ],
+                     [  0.718983381000000 ,  1.933370245000000 ,  -1.247280000000000 ],
+                     [  0.718983381000000 ,  1.933370245000000 ,  1.247280000000000 ],
+                     [  -0.713152254000000 ,  -1.934362753000000 ,  1.247560000000000 ],
+                     [  -0.713152254000000 ,  -1.934362753000000 ,  -1.247560000000000 ],
+                     [  3.746481288363636 ,  0.643131999000000 ,  1.130045000000000 ],
+                     [  3.210736323363636 ,  -0.642689433000000 ,  1.130631000000000 ],
+                     [  2.937715255363636 ,  -1.306738847000000 ,  0.000000000000000 ],
+                     [  3.210736323363636 ,  -0.642689433000000 ,  -1.130631000000000 ],
+                     [  3.746481288363636 ,  0.643131999000000 ,  -1.130045000000000 ],
+                     [  4.023966227363637 ,  1.305979850000000 ,  0.000000000000000 ],
+                     [  3.957439433363636 ,  1.152471205000000 ,  2.061864000000000 ],
+                     [  2.991000804363636 ,  -1.147744338000000 ,  2.062399000000000 ],
+                     [  2.991000804363636 ,  -1.147744338000000 ,  -2.062399000000000 ],
+                     [  3.957439433363636 ,  1.152471205000000 ,  -2.061864000000000 ]],
+    'positions 1.2':[[  0.395653045 ,  1.059432142 ,  -0.696139000 ],
+                     [  0.395653045 ,  1.059432142 ,  0.696139000 ],
+                     [  -0.003263357 ,  0.000227377 ,  1.414480000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  0.696729000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  -0.696729000 ],
+                     [  -0.003263357 ,  0.000227377 ,  -1.414480000 ],
+                     [  0.718983381 ,  1.933370245 ,  -1.247280000 ],
+                     [  0.718983381 ,  1.933370245 ,  1.247280000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  1.247560000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  -1.247560000 ],
+                     [  4.442367465 ,  0.643131999 ,  1.130045000 ],
+                     [  3.906622500 ,  -0.642689433 ,  1.130631000 ],
+                     [  3.633601432 ,  -1.306738847 ,  0.000000000 ],
+                     [  3.906622500 ,  -0.642689433 ,  -1.130631000 ],
+                     [  4.442367465 ,  0.643131999 ,  -1.130045000 ],
+                     [  4.719852404 ,  1.305979850 ,  0.000000000 ],
+                     [  4.653325610 ,  1.152471205 ,  2.061864000 ],
+                     [  3.686886981 ,  -1.147744338 ,  2.062399000 ],
+                     [  3.686886981 ,  -1.147744338 ,  -2.062399000 ],
+                     [  4.653325610 ,  1.152471205 ,  -2.061864000 ]],
+    'positions 1.5':[[  0.395653045 ,  1.059432142 ,  -0.696139000 ],
+                     [  0.395653045 ,  1.059432142 ,  0.696139000 ],
+                     [  -0.003263357 ,  0.000227377 ,  1.414480000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  0.696729000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  -0.696729000 ],
+                     [  -0.003263357 ,  0.000227377 ,  -1.414480000 ],
+                     [  0.718983381 ,  1.933370245 ,  -1.247280000 ],
+                     [  0.718983381 ,  1.933370245 ,  1.247280000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  1.247560000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  -1.247560000 ],
+                     [  5.486196730 ,  0.643131999 ,  1.130045000 ],
+                     [  4.950451765 ,  -0.642689433 ,  1.130631000 ],
+                     [  4.677430697 ,  -1.306738847 ,  0.000000000 ],
+                     [  4.950451765 ,  -0.642689433 ,  -1.130631000 ],
+                     [  5.486196730 ,  0.643131999 ,  -1.130045000 ],
+                     [  5.763681669 ,  1.305979850 ,  0.000000000 ],
+                     [  5.697154875 ,  1.152471205 ,  2.061864000 ],
+                     [  4.730716246 ,  -1.147744338 ,  2.062399000 ],
+                     [  4.730716246 ,  -1.147744338 ,  -2.062399000 ],
+                     [  5.697154875 ,  1.152471205 ,  -2.061864000 ]],
+    'positions 2.0':[[  0.395653045 ,  1.059432142 ,  -0.696139000 ],
+                     [  0.395653045 ,  1.059432142 ,  0.696139000 ],
+                     [  -0.003263357 ,  0.000227377 ,  1.414480000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  0.696729000 ],
+                     [  -0.391847355 ,  -1.059697307 ,  -0.696729000 ],
+                     [  -0.003263357 ,  0.000227377 ,  -1.414480000 ],
+                     [  0.718983381 ,  1.933370245 ,  -1.247280000 ],
+                     [  0.718983381 ,  1.933370245 ,  1.247280000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  1.247560000 ],
+                     [  -0.713152254 ,  -1.934362753 ,  -1.247560000 ],
+                     [  7.225912172 ,  0.643131999 ,  1.130045000 ],
+                     [  6.690167207 ,  -0.642689433 ,  1.130631000 ],
+                     [  6.417146139 ,  -1.306738847 ,  0.000000000 ],
+                     [  6.690167207 ,  -0.642689433 ,  -1.130631000 ],
+                     [  7.225912172 ,  0.643131999 ,  -1.130045000 ],
+                     [  7.503397111 ,  1.305979850 ,  0.000000000 ],
+                     [  7.436870317 ,  1.152471205 ,  2.061864000 ],
+                     [  6.470431688 ,  -1.147744338 ,  2.062399000 ],
+                     [  6.470431688 ,  -1.147744338 ,  -2.062399000 ],
+                     [  7.436870317 ,  1.152471205 ,  -2.061864000 ]]},
+
+'Uracil_dimer_h-bonded': {
+    'description': "Complex, S22, S26, 2 h-bonds, double h-bond, nucleic base",
+    'name': "Uracil_dimer_h-bonded",
+    's26_number': "05",
+    'interaction energy CC':-0.8972,
+    'interaction energies s22x5':[-0.8122,-0.8872,-0.7441,-0.4536,-0.1986],
+    'offset': 0.0100,
+    'symbols': 'OCNCCCNOHHHHOCNCCCNOHHHH',
+    'magmoms': None,
+    'dimer atoms': [12,12],
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[ -1.4663316,  1.0121693,  0.0000000],
+                 [ -0.6281464,  1.9142678,  0.0000000],
+                 [  0.7205093,  1.6882688,  0.0000000],
+                 [  1.6367290,  2.7052764,  0.0000000],
+                 [  1.2769036,  4.0061763,  0.0000000],
+                 [ -0.1286005,  4.3621549,  0.0000000],
+                 [ -0.9777230,  3.2396433,  0.0000000],
+                 [ -0.5972229,  5.4864066,  0.0000000],
+                 [  2.0103504,  4.7938642,  0.0000000],
+                 [  1.0232515,  0.7061820,  0.0000000],
+                 [ -1.9700268,  3.4323850,  0.0000000],
+                 [  2.6690620,  2.3883417,  0.0000000],
+                 [  1.4663316, -1.0121693,  0.0000000],
+                 [  0.6281464, -1.9142678,  0.0000000],
+                 [ -0.7205093, -1.6882688,  0.0000000],
+                 [ -1.6367290, -2.7052764,  0.0000000],
+                 [ -1.2769036, -4.0061763,  0.0000000],
+                 [  0.1286005, -4.3621549,  0.0000000],
+                 [  0.9777230, -3.2396433,  0.0000000],
+                 [  0.5972229, -5.4864066,  0.0000000],
+                 [ -2.0103504, -4.7938642,  0.0000000],
+                 [ -1.0232515, -0.7061820,  0.0000000],
+                 [  1.9700268, -3.4323850,  0.0000000],
+                 [ -2.6690620, -2.3883417,  0.0000000]],
+    'positions 0.9':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.664243938 ,  1.036879148 ,  0.000000000 ],
+                     [  -0.108663437 ,  2.286389518 ,  0.000000000 ],
+                     [  -0.864691937 ,  3.427521953 ,  0.000000000 ],
+                     [  -2.214231597 ,  3.403909532 ,  0.000000000 ],
+                     [  -2.909869859 ,  2.131803891 ,  0.000000000 ],
+                     [  -2.034924624 ,  1.029301194 ,  0.000000000 ],
+                     [  -4.115521524 ,  1.958733959 ,  0.000000000 ],
+                     [  -2.793840332 ,  4.310799346 ,  0.000000000 ],
+                     [  0.917908194 ,  2.334329905 ,  0.000000000 ],
+                     [  -2.469325804 ,  0.116551326 ,  0.000000000 ],
+                     [  -0.300037631 ,  4.348024043 ,  0.000000000 ],
+                     [  2.515009084 ,  2.334329905 ,  0.000000000 ],
+                     [  3.179253022 ,  1.297450757 ,  0.000000000 ],
+                     [  2.623672521 ,  0.047940387 ,  0.000000000 ],
+                     [  3.379701020 ,  -1.093192048 ,  0.000000000 ],
+                     [  4.729240680 ,  -1.069579627 ,  0.000000000 ],
+                     [  5.424878943 ,  0.202526014 ,  0.000000000 ],
+                     [  4.549933708 ,  1.305028711 ,  0.000000000 ],
+                     [  6.630530608 ,  0.375595946 ,  0.000000000 ],
+                     [  5.308849416 ,  -1.976469441 ,  0.000000000 ],
+                     [  1.597100890 ,  0.000000000 ,  0.000000000 ],
+                     [  4.984334888 ,  2.217778579 ,  0.000000000 ],
+                     [  2.815046715 ,  -2.013694138 ,  0.000000000 ]],
+    'positions 1.0':[[  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  -0.664243938000000 ,  1.036879148000000 ,  0.000000000000000 ],
+                     [  -0.108663437000000 ,  2.286389518000000 ,  0.000000000000000 ],
+                     [  -0.864691937000000 ,  3.427521953000000 ,  0.000000000000000 ],
+                     [  -2.214231597000000 ,  3.403909532000000 ,  0.000000000000000 ],
+                     [  -2.909869859000000 ,  2.131803891000000 ,  0.000000000000000 ],
+                     [  -2.034924624000000 ,  1.029301194000000 ,  0.000000000000000 ],
+                     [  -4.115521524000000 ,  1.958733959000000 ,  0.000000000000000 ],
+                     [  -2.793840332000000 ,  4.310799346000000 ,  0.000000000000000 ],
+                     [  0.917908194000000 ,  2.334329905000000 ,  0.000000000000000 ],
+                     [  -2.469325804000000 ,  0.116551326000000 ,  0.000000000000000 ],
+                     [  -0.300037631000000 ,  4.348024043000000 ,  0.000000000000000 ],
+                     [  2.692464738545454 ,  2.334329905000000 ,  0.000000000000000 ],
+                     [  3.356708676545455 ,  1.297450757000000 ,  0.000000000000000 ],
+                     [  2.801128175545454 ,  0.047940387000000 ,  0.000000000000000 ],
+                     [  3.557156674545455 ,  -1.093192048000000 ,  0.000000000000000 ],
+                     [  4.906696334545455 ,  -1.069579627000000 ,  0.000000000000000 ],
+                     [  5.602334597545455 ,  0.202526014000000 ,  0.000000000000000 ],
+                     [  4.727389362545455 ,  1.305028711000000 ,  0.000000000000000 ],
+                     [  6.807986262545454 ,  0.375595946000000 ,  0.000000000000000 ],
+                     [  5.486305070545455 ,  -1.976469441000000 ,  0.000000000000000 ],
+                     [  1.774556544545455 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  5.161790542545455 ,  2.217778579000000 ,  0.000000000000000 ],
+                     [  2.992502369545454 ,  -2.013694138000000 ,  0.000000000000000 ]],
+    'positions 1.2':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.664243938 ,  1.036879148 ,  0.000000000 ],
+                     [  -0.108663437 ,  2.286389518 ,  0.000000000 ],
+                     [  -0.864691937 ,  3.427521953 ,  0.000000000 ],
+                     [  -2.214231597 ,  3.403909532 ,  0.000000000 ],
+                     [  -2.909869859 ,  2.131803891 ,  0.000000000 ],
+                     [  -2.034924624 ,  1.029301194 ,  0.000000000 ],
+                     [  -4.115521524 ,  1.958733959 ,  0.000000000 ],
+                     [  -2.793840332 ,  4.310799346 ,  0.000000000 ],
+                     [  0.917908194 ,  2.334329905 ,  0.000000000 ],
+                     [  -2.469325804 ,  0.116551326 ,  0.000000000 ],
+                     [  -0.300037631 ,  4.348024043 ,  0.000000000 ],
+                     [  3.047376048 ,  2.334329905 ,  0.000000000 ],
+                     [  3.711619986 ,  1.297450757 ,  0.000000000 ],
+                     [  3.156039485 ,  0.047940387 ,  0.000000000 ],
+                     [  3.912067984 ,  -1.093192048 ,  0.000000000 ],
+                     [  5.261607644 ,  -1.069579627 ,  0.000000000 ],
+                     [  5.957245907 ,  0.202526014 ,  0.000000000 ],
+                     [  5.082300672 ,  1.305028711 ,  0.000000000 ],
+                     [  7.162897572 ,  0.375595946 ,  0.000000000 ],
+                     [  5.841216380 ,  -1.976469441 ,  0.000000000 ],
+                     [  2.129467854 ,  0.000000000 ,  0.000000000 ],
+                     [  5.516701852 ,  2.217778579 ,  0.000000000 ],
+                     [  3.347413679 ,  -2.013694138 ,  0.000000000 ]],
+    'positions 1.5':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.664243938 ,  1.036879148 ,  0.000000000 ],
+                     [  -0.108663437 ,  2.286389518 ,  0.000000000 ],
+                     [  -0.864691937 ,  3.427521953 ,  0.000000000 ],
+                     [  -2.214231597 ,  3.403909532 ,  0.000000000 ],
+                     [  -2.909869859 ,  2.131803891 ,  0.000000000 ],
+                     [  -2.034924624 ,  1.029301194 ,  0.000000000 ],
+                     [  -4.115521524 ,  1.958733959 ,  0.000000000 ],
+                     [  -2.793840332 ,  4.310799346 ,  0.000000000 ],
+                     [  0.917908194 ,  2.334329905 ,  0.000000000 ],
+                     [  -2.469325804 ,  0.116551326 ,  0.000000000 ],
+                     [  -0.300037631 ,  4.348024043 ,  0.000000000 ],
+                     [  3.579743012 ,  2.334329905 ,  0.000000000 ],
+                     [  4.243986950 ,  1.297450757 ,  0.000000000 ],
+                     [  3.688406449 ,  0.047940387 ,  0.000000000 ],
+                     [  4.444434948 ,  -1.093192048 ,  0.000000000 ],
+                     [  5.793974608 ,  -1.069579627 ,  0.000000000 ],
+                     [  6.489612871 ,  0.202526014 ,  0.000000000 ],
+                     [  5.614667636 ,  1.305028711 ,  0.000000000 ],
+                     [  7.695264536 ,  0.375595946 ,  0.000000000 ],
+                     [  6.373583344 ,  -1.976469441 ,  0.000000000 ],
+                     [  2.661834818 ,  0.000000000 ,  0.000000000 ],
+                     [  6.049068816 ,  2.217778579 ,  0.000000000 ],
+                     [  3.879780643 ,  -2.013694138 ,  0.000000000 ]],
+    'positions 2.0':[[  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  -0.664243938 ,  1.036879148 ,  0.000000000 ],
+                     [  -0.108663437 ,  2.286389518 ,  0.000000000 ],
+                     [  -0.864691937 ,  3.427521953 ,  0.000000000 ],
+                     [  -2.214231597 ,  3.403909532 ,  0.000000000 ],
+                     [  -2.909869859 ,  2.131803891 ,  0.000000000 ],
+                     [  -2.034924624 ,  1.029301194 ,  0.000000000 ],
+                     [  -4.115521524 ,  1.958733959 ,  0.000000000 ],
+                     [  -2.793840332 ,  4.310799346 ,  0.000000000 ],
+                     [  0.917908194 ,  2.334329905 ,  0.000000000 ],
+                     [  -2.469325804 ,  0.116551326 ,  0.000000000 ],
+                     [  -0.300037631 ,  4.348024043 ,  0.000000000 ],
+                     [  4.467021284 ,  2.334329905 ,  0.000000000 ],
+                     [  5.131265222 ,  1.297450757 ,  0.000000000 ],
+                     [  4.575684721 ,  0.047940387 ,  0.000000000 ],
+                     [  5.331713220 ,  -1.093192048 ,  0.000000000 ],
+                     [  6.681252880 ,  -1.069579627 ,  0.000000000 ],
+                     [  7.376891143 ,  0.202526014 ,  0.000000000 ],
+                     [  6.501945908 ,  1.305028711 ,  0.000000000 ],
+                     [  8.582542808 ,  0.375595946 ,  0.000000000 ],
+                     [  7.260861616 ,  -1.976469441 ,  0.000000000 ],
+                     [  3.549113090 ,  0.000000000 ,  0.000000000 ],
+                     [  6.936347088 ,  2.217778579 ,  0.000000000 ],
+                     [  4.767058915 ,  -2.013694138 ,  0.000000000 ]]},
+
+'Uracil_dimer_stack': {
+    'description': "Complex, S22, S26, stack, dispersion bonded, nucleic base",
+    'name': "Uracil_dimer_stack",
+    's26_number': "13",
+    'interaction energy CC':-0.4224,
+    'interaction energies s22x5':[-0.2931,-0.4280,-0.2715,-0.1049,-0.0299],
+    'offset': -0.0056,
+    'symbols': 'NCHCHCONHCOHNCHCHCONHCOH',
+    'magmoms': None,
+    'dimer atoms': [12,12],
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[  2.0113587, -1.2132073, -0.0980673],
+                 [  2.0257076, -0.6971797, -1.3644029],
+                 [  2.2975208, -1.3910592, -2.1456459],
+                 [  1.7145226,  0.5919651, -1.6124892],
+                 [  1.7272873,  0.9908466, -2.6120050],
+                 [  1.3089605,  1.4575340, -0.5205890],
+                 [  0.9205926,  2.6110864, -0.6260457],
+                 [  1.3768885,  0.8397454,  0.7346356],
+                 [  1.0518040,  1.3862229,  1.5233710],
+                 [  1.6459909, -0.4852113,  1.0187267],
+                 [  1.5611090, -0.9718061,  2.1298059],
+                 [  2.1294635, -2.2015046,  0.0568134],
+                 [ -2.0113587,  1.2132073, -0.0980673],
+                 [ -2.0257076,  0.6971797, -1.3644029],
+                 [ -2.2975208,  1.3910592, -2.1456459],
+                 [ -1.7145226, -0.5919651, -1.6124892],
+                 [ -1.7272873, -0.9908466, -2.6120050],
+                 [ -1.3089605, -1.4575340, -0.5205890],
+                 [ -0.9205926, -2.6110864, -0.6260457],
+                 [ -1.3768885, -0.8397454,  0.7346356],
+                 [ -1.0518040, -1.3862229,  1.5233710],
+                 [ -1.6459909,  0.4852113,  1.0187267],
+                 [ -1.5611090,  0.9718061,  2.1298059],
+                 [ -2.1294635,  2.2015046,  0.0568134]],
+    'positions 0.9':[[  -0.277905006 ,  1.293679543 ,  0.176141970 ],
+                     [  -0.313143400 ,  0.778657200 ,  -1.090194030 ],
+                     [  -0.556628453 ,  1.482976305 ,  -1.871437030 ],
+                     [  -0.054429325 ,  -0.522034140 ,  -1.338280030 ],
+                     [  -0.083339176 ,  -0.920071815 ,  -2.337796030 ],
+                     [  0.315741834 ,  -1.403319766 ,  -0.246380030 ],
+                     [  0.657066634 ,  -2.571655559 ,  -0.351837030 ],
+                     [  0.272892517 ,  -0.783286382 ,  1.008844970 ],
+                     [  0.575575188 ,  -1.342483138 ,  1.797579970 ],
+                     [  0.057676398 ,  0.551482081 ,  1.292935970 ],
+                     [  0.162197796 ,  1.034239706 ,  2.404014970 ],
+                     [  -0.355882042 ,  2.285950208 ,  0.331021970 ],
+                     [  3.306699593 ,  -1.293679543 ,  0.176141970 ],
+                     [  3.341937987 ,  -0.778657200 ,  -1.090194030 ],
+                     [  3.585423040 ,  -1.482976305 ,  -1.871437030 ],
+                     [  3.083223911 ,  0.522034140 ,  -1.338280030 ],
+                     [  3.112133763 ,  0.920071815 ,  -2.337796030 ],
+                     [  2.713052753 ,  1.403319766 ,  -0.246380030 ],
+                     [  2.371727953 ,  2.571655559 ,  -0.351837030 ],
+                     [  2.755902070 ,  0.783286382 ,  1.008844970 ],
+                     [  2.453219399 ,  1.342483138 ,  1.797579970 ],
+                     [  2.971118189 ,  -0.551482081 ,  1.292935970 ],
+                     [  2.866596791 ,  -1.034239706 ,  2.404014970 ],
+                     [  3.384676629 ,  -2.285950208 ,  0.331021970 ]],
+    'positions 1.0':[[  -0.277905006000000 ,  1.293679543000000 ,  0.176141970000000 ],
+                     [  -0.313143400000000 ,  0.778657200000000 ,  -1.090194030000000 ],
+                     [  -0.556628453000000 ,  1.482976305000000 ,  -1.871437030000000 ],
+                     [  -0.054429325000000 ,  -0.522034140000000 ,  -1.338280030000000 ],
+                     [  -0.083339176000000 ,  -0.920071815000000 ,  -2.337796030000000 ],
+                     [  0.315741834000000 ,  -1.403319766000000 ,  -0.246380030000000 ],
+                     [  0.657066634000000 ,  -2.571655559000000 ,  -0.351837030000000 ],
+                     [  0.272892517000000 ,  -0.783286382000000 ,  1.008844970000000 ],
+                     [  0.575575188000000 ,  -1.342483138000000 ,  1.797579970000000 ],
+                     [  0.057676398000000 ,  0.551482081000000 ,  1.292935970000000 ],
+                     [  0.162197796000000 ,  1.034239706000000 ,  2.404014970000000 ],
+                     [  -0.355882042000000 ,  2.285950208000000 ,  0.331021970000000 ],
+                     [  3.643232324909091 ,  -1.293679543000000 ,  0.176141970000000 ],
+                     [  3.678470718909091 ,  -0.778657200000000 ,  -1.090194030000000 ],
+                     [  3.921955771909091 ,  -1.482976305000000 ,  -1.871437030000000 ],
+                     [  3.419756642909091 ,  0.522034140000000 ,  -1.338280030000000 ],
+                     [  3.448666494909091 ,  0.920071815000000 ,  -2.337796030000000 ],
+                     [  3.049585484909091 ,  1.403319766000000 ,  -0.246380030000000 ],
+                     [  2.708260684909091 ,  2.571655559000000 ,  -0.351837030000000 ],
+                     [  3.092434801909091 ,  0.783286382000000 ,  1.008844970000000 ],
+                     [  2.789752130909091 ,  1.342483138000000 ,  1.797579970000000 ],
+                     [  3.307650920909091 ,  -0.551482081000000 ,  1.292935970000000 ],
+                     [  3.203129522909091 ,  -1.034239706000000 ,  2.404014970000000 ],
+                     [  3.721209360909091 ,  -2.285950208000000 ,  0.331021970000000 ]],
+    'positions 1.2':[[  -0.277905006 ,  1.293679543 ,  0.176141970 ],
+                     [  -0.313143400 ,  0.778657200 ,  -1.090194030 ],
+                     [  -0.556628453 ,  1.482976305 ,  -1.871437030 ],
+                     [  -0.054429325 ,  -0.522034140 ,  -1.338280030 ],
+                     [  -0.083339176 ,  -0.920071815 ,  -2.337796030 ],
+                     [  0.315741834 ,  -1.403319766 ,  -0.246380030 ],
+                     [  0.657066634 ,  -2.571655559 ,  -0.351837030 ],
+                     [  0.272892517 ,  -0.783286382 ,  1.008844970 ],
+                     [  0.575575188 ,  -1.342483138 ,  1.797579970 ],
+                     [  0.057676398 ,  0.551482081 ,  1.292935970 ],
+                     [  0.162197796 ,  1.034239706 ,  2.404014970 ],
+                     [  -0.355882042 ,  2.285950208 ,  0.331021970 ],
+                     [  4.316297789 ,  -1.293679543 ,  0.176141970 ],
+                     [  4.351536183 ,  -0.778657200 ,  -1.090194030 ],
+                     [  4.595021236 ,  -1.482976305 ,  -1.871437030 ],
+                     [  4.092822107 ,  0.522034140 ,  -1.338280030 ],
+                     [  4.121731959 ,  0.920071815 ,  -2.337796030 ],
+                     [  3.722650949 ,  1.403319766 ,  -0.246380030 ],
+                     [  3.381326149 ,  2.571655559 ,  -0.351837030 ],
+                     [  3.765500266 ,  0.783286382 ,  1.008844970 ],
+                     [  3.462817595 ,  1.342483138 ,  1.797579970 ],
+                     [  3.980716385 ,  -0.551482081 ,  1.292935970 ],
+                     [  3.876194987 ,  -1.034239706 ,  2.404014970 ],
+                     [  4.394274825 ,  -2.285950208 ,  0.331021970 ]],
+    'positions 1.5':[[  -0.277905006 ,  1.293679543 ,  0.176141970 ],
+                     [  -0.313143400 ,  0.778657200 ,  -1.090194030 ],
+                     [  -0.556628453 ,  1.482976305 ,  -1.871437030 ],
+                     [  -0.054429325 ,  -0.522034140 ,  -1.338280030 ],
+                     [  -0.083339176 ,  -0.920071815 ,  -2.337796030 ],
+                     [  0.315741834 ,  -1.403319766 ,  -0.246380030 ],
+                     [  0.657066634 ,  -2.571655559 ,  -0.351837030 ],
+                     [  0.272892517 ,  -0.783286382 ,  1.008844970 ],
+                     [  0.575575188 ,  -1.342483138 ,  1.797579970 ],
+                     [  0.057676398 ,  0.551482081 ,  1.292935970 ],
+                     [  0.162197796 ,  1.034239706 ,  2.404014970 ],
+                     [  -0.355882042 ,  2.285950208 ,  0.331021970 ],
+                     [  5.325895984 ,  -1.293679543 ,  0.176141970 ],
+                     [  5.361134378 ,  -0.778657200 ,  -1.090194030 ],
+                     [  5.604619431 ,  -1.482976305 ,  -1.871437030 ],
+                     [  5.102420302 ,  0.522034140 ,  -1.338280030 ],
+                     [  5.131330154 ,  0.920071815 ,  -2.337796030 ],
+                     [  4.732249144 ,  1.403319766 ,  -0.246380030 ],
+                     [  4.390924344 ,  2.571655559 ,  -0.351837030 ],
+                     [  4.775098461 ,  0.783286382 ,  1.008844970 ],
+                     [  4.472415790 ,  1.342483138 ,  1.797579970 ],
+                     [  4.990314580 ,  -0.551482081 ,  1.292935970 ],
+                     [  4.885793182 ,  -1.034239706 ,  2.404014970 ],
+                     [  5.403873020 ,  -2.285950208 ,  0.331021970 ]],
+    'positions 2.0':[[  -0.277905006 ,  1.293679543 ,  0.176141970 ],
+                     [  -0.313143400 ,  0.778657200 ,  -1.090194030 ],
+                     [  -0.556628453 ,  1.482976305 ,  -1.871437030 ],
+                     [  -0.054429325 ,  -0.522034140 ,  -1.338280030 ],
+                     [  -0.083339176 ,  -0.920071815 ,  -2.337796030 ],
+                     [  0.315741834 ,  -1.403319766 ,  -0.246380030 ],
+                     [  0.657066634 ,  -2.571655559 ,  -0.351837030 ],
+                     [  0.272892517 ,  -0.783286382 ,  1.008844970 ],
+                     [  0.575575188 ,  -1.342483138 ,  1.797579970 ],
+                     [  0.057676398 ,  0.551482081 ,  1.292935970 ],
+                     [  0.162197796 ,  1.034239706 ,  2.404014970 ],
+                     [  -0.355882042 ,  2.285950208 ,  0.331021970 ],
+                     [  7.008559644 ,  -1.293679543 ,  0.176141970 ],
+                     [  7.043798038 ,  -0.778657200 ,  -1.090194030 ],
+                     [  7.287283091 ,  -1.482976305 ,  -1.871437030 ],
+                     [  6.785083962 ,  0.522034140 ,  -1.338280030 ],
+                     [  6.813993814 ,  0.920071815 ,  -2.337796030 ],
+                     [  6.414912804 ,  1.403319766 ,  -0.246380030 ],
+                     [  6.073588004 ,  2.571655559 ,  -0.351837030 ],
+                     [  6.457762121 ,  0.783286382 ,  1.008844970 ],
+                     [  6.155079450 ,  1.342483138 ,  1.797579970 ],
+                     [  6.672978240 ,  -0.551482081 ,  1.292935970 ],
+                     [  6.568456842 ,  -1.034239706 ,  2.404014970 ],
+                     [  7.086536680 ,  -2.285950208 ,  0.331021970 ]]},
+
+'Water_dimer': {
+    'description': "Complex, S22, S26, 1 h-bond, OH-O",
+    'name': "Water_dimer",
+    's26_number': "02",
+    'interaction energy CC':-0.2177,
+    'interaction energies s22x5':[-0.1873,-0.2155,-0.1752,-0.0993,-0.0416],
+    'offset': 0.0022,
+    'symbols': 'OHHOHH',
+    'magmoms': None,
+    'dimer atoms': [3,3],
+    # Optimisation level: CCSD(T)/cc-pVQZ
+    'positions':[[ -1.551007, -0.114520,  0.000000],
+                 [ -1.934259,  0.762503,  0.000000],
+                 [ -0.599677,  0.040712,  0.000000],
+                 [  1.350625,  0.111469,  0.000000],
+                 [  1.680398, -0.373741, -0.758561],
+                 [  1.680398, -0.373741,  0.758561]],
+    'positions 0.9':[[  -0.956332646 ,  -0.120638358 ,  0.000000000 ],
+                     [  -1.307535174 ,  0.769703274 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  1.756426600 ,  0.000000000 ,  0.000000000 ],
+                     [  2.068390928 ,  -0.496847294 ,  -0.758561000 ],
+                     [  2.068390928 ,  -0.496847294 ,  0.758561000 ]],
+    'positions 1.0':[[  -0.956332646000000 ,  -0.120638358000000 ,  0.000000000000000 ],
+                     [  -1.307535174000000 ,  0.769703274000000 ,  0.000000000000000 ],
+                     [  0.000000000000000 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  1.951585111090909 ,  0.000000000000000 ,  0.000000000000000 ],
+                     [  2.263549439090909 ,  -0.496847294000000 ,  -0.758561000000000 ],
+                     [  2.263549439090909 ,  -0.496847294000000 ,  0.758561000000000 ]],
+    'positions 1.2':[[  -0.956332646 ,  -0.120638358 ,  0.000000000 ],
+                     [  -1.307535174 ,  0.769703274 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.341902133 ,  0.000000000 ,  0.000000000 ],
+                     [  2.653866461 ,  -0.496847294 ,  -0.758561000 ],
+                     [  2.653866461 ,  -0.496847294 ,  0.758561000 ]],
+    'positions 1.5':[[  -0.956332646 ,  -0.120638358 ,  0.000000000 ],
+                     [  -1.307535174 ,  0.769703274 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  2.927377666 ,  0.000000000 ,  0.000000000 ],
+                     [  3.239341994 ,  -0.496847294 ,  -0.758561000 ],
+                     [  3.239341994 ,  -0.496847294 ,  0.758561000 ]],
+    'positions 2.0':[[  -0.956332646 ,  -0.120638358 ,  0.000000000 ],
+                     [  -1.307535174 ,  0.769703274 ,  0.000000000 ],
+                     [  0.000000000 ,  0.000000000 ,  0.000000000 ],
+                     [  3.903170222 ,  0.000000000 ,  0.000000000 ],
+                     [  4.215134550 ,  -0.496847294 ,  -0.758561000 ],
+                     [  4.215134550 ,  -0.496847294 ,  0.758561000 ]]},
+
+# --- s26 ---#
+'Methanol_dimer': {
+    'description': "1 h-bond, OH-O, S26",
+    'name': "Methanol_dimer",
+    's26_number': "23",
+    'interaction energy MP2':-0.1947,
+    'interaction energy CC':-0.2472,
+    'symbols': 'COHHHHCOHHHH',
+    'magmoms': None,
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[ -2.114335, -0.445120,  0.221169],
+     		 [ -1.298032,  0.687432, -0.091609],
+     		 [ -1.514720, -1.087407,  0.858397],
+     		 [ -2.389026, -0.999598, -0.675819],
+     		 [ -3.014036, -0.146131,  0.758353],
+     		 [ -1.779011,  1.249219, -0.706289],
+     		 [  2.245711,  0.159561,  0.329180],
+     		 [  1.285289, -0.472004, -0.501635],
+     		 [  3.156806, -0.431037,  0.275178],
+     		 [  1.921474,  0.200114,  1.371809],
+     		 [  2.472512,  1.174527, -0.005695],
+     		 [  0.459691,  0.030236, -0.432082]]},
+'Methanol-formaldehyde_complex': {
+    'description': "1 h-bond, OH-O, S26",
+    's26_number': "24",
+    'name': "Methanol-formaldehyde_complex",
+    'interaction energy MP2':-0.1375,
+    'interaction energy CC':-0.2303,
+    'symbols': 'COHHHHCOHH',
+    'magmoms': None,
+    # Optimisation level: MP2/cc-pVTZ
+    'positions':[[  1.4073776162,  1.0401758064,  2.0396751091],
+      		 [  0.9349167370,  0.2900025037,  0.9338944612],
+      		 [  2.1022348002,  0.4092302046,  2.5857336738],
+      		 [  0.6031517696,  1.3305232490,  2.7201012084],
+      		 [  1.9382206717,  1.9424443037,  1.7274684180],
+      		 [  0.2386426835,  0.8096239461,  0.5150020113],
+      		 [ -2.0809868810, -0.1309834084,  0.2601720974],
+      		 [ -1.6206107677,  0.9480216819, -0.1003790153],
+      		 [ -3.1316901290, -0.3840062180,  0.0820343467],
+      		 [ -1.4275985002, -0.8637260692,  0.7543476894]]},
+'Methyl_amide_dimer_alpha': {
+    'description': "1 h-bond, NH-O, S26",
+    's26_number': "25",
+    'name': "Methyl_amide_dimer_alpha",
+    'interaction energy MP2':-0.2068,
+    'interaction energy CC':-0.2901,
+    'symbols': 'CCOHHHNHHCCOHHHNHH',
+    'magmoms': None,
+    # Optimisation level: DFT TPSS/TZVP (hydrogen positions optimized)
+    'positions':[[  5.575000,  7.306000, -12.014000],
+  		 [  4.318000,  8.065000, -12.345000],
+  		 [  4.212000,  9.236000, -11.986000],
+  		 [  6.072000,  7.809000, -11.186000],
+  		 [  6.246000,  7.323000, -12.882000],
+  		 [  5.392000,  6.256000, -11.755000],
+  		 [  3.378000,  7.446000, -13.058000],
+  		 [  3.468000,  6.488000, -13.367000],
+  		 [  2.561000,  7.968000, -13.350000],
+  		 [  0.768000,  8.395000, -9.9890000],
+  		 [  1.666000,  9.133000, -8.9870000],
+  		 [  1.355000,  9.267000, -7.8060000],
+  		 [ -0.014000,  9.085000, -10.326000],
+  		 [  0.289000,  7.561000, -9.4730000],
+  		 [  1.315000,  8.032000, -10.865000],
+  		 [  2.798000,  9.666000, -9.4430000],
+  		 [  3.139000,  9.599000, -10.401000],
+  		 [  3.350000,  10.195000, -8.779000]]},
+'Methyl_amide_dimer_beta': {
+    'description': "1 h-bond, NH-O, S26",
+    'name': "Methyl_amide_dimer_beta",
+    's26_number': "26",
+    'interaction energy MP2':-0.2342,
+    'interaction energy CC':-0.3317,
+    'symbols': 'CCOHHHNHHCCOHHHNHH',
+    'magmoms': None,
+    # Optimisation level: DFT TPSS/TZVP (hydrogen positions optimized)
+    'positions':[[  0.300000, -7.945000, -4.8440000],
+  		 [ -1.133000, -7.581000, -4.4840000],
+  		 [ -1.612000, -7.787000, -3.3770000],
+  		 [  0.650000, -7.434000, -5.7440000],
+ 		 [  0.351000, -9.028000, -5.0100000],
+  		 [  0.952000, -7.712000, -3.9990000],
+  		 [ -1.811000, -7.075000, -5.4730000],
+  		 [ -2.781000, -6.832000, -5.3080000],
+ 		 [ -1.403000, -6.863000, -6.3820000],
+  		 [ -0.931000, -6.425000, -10.105000],
+  		 [  0.041000, -6.447000, -8.9820000],
+  		 [ -0.356000, -6.488000, -7.8210000],
+  		 [ -0.492000, -6.635000, -11.086000],
+  		 [ -1.398000, -5.434000, -10.143000],
+  		 [ -1.724000, -7.150000, -9.9060000],
+  		 [  1.318000, -6.364000, -9.3020000],
+  		 [  1.636000, -6.336000, -10.260000],
+  		 [  2.015000, -6.339000, -8.5670000]]},
+}
+
+
+def create_s22_system(name, dist=None, **kwargs):
+    """Create S22/S26/s22x5 system.
+    """
+    s22_,s22x5_,s22_name,dist = identify_s22_sys(name,dist)
+    if s22_ is True:
+        d = data[s22_name]
+        return Atoms(d['symbols'], d['positions'], **kwargs)
+    elif s22x5_ is True:
+        d = data[s22_name]
+        pos = 'positions '+dist
+        return Atoms(d['symbols'], d[pos], **kwargs)
+    else:
+        raise NotImplementedError('s22/s26/s22x5 creation failed')
+
+
+def identify_s22_sys(name,dist=None):
+    s22_ = False
+    s22x5_ = False
+    if (name in s22 or name in s26) and dist == None:
+        s22_name = name
+        s22_ = True
+    elif name in s22x5 and dist == None:
+        s22_name, dist = get_s22x5_id(name)
+        s22x5_ = True
+    elif name in s22 and dist != None:
+        dist_ = str(dist)
+        if dist_ not in ['0.9','1.0','1.2','1.5','2.0']:
+            raise KeyError('Bad s22x5 distance specified: %s' % dist_) 
+        else:
+            s22_name = name
+            dist = dist_
+            s22x5_ = True
+    if s22_ is False and s22x5_ is False:
+        raise KeyError('s22 combination %s %s not in database' %(name,str(dist)))
+    return s22_, s22x5_, s22_name, dist
+
+
+def get_s22x5_id(name):
+    """Get main name and relative separation distance of an S22x5 system.
+    """
+    s22_name = name[:-4]
+    dist = name[-3:]
+    return s22_name, dist
+
+
+def get_s22_number(name,dist=None):
+    """Returns the S22/S26 database number of a system as a string.
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    return data[s22_name]['s26_number']
+
+
+def get_interaction_energy_cc(name,dist=None):
+    """Returns the S22/S26 CCSD(T)/CBS CP interaction energy in eV.
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    return data[s22_name]['interaction energy CC']
+
+
+def get_interaction_energy_s22(name,dist=None):
+    """Returns the S22/S26 CCSD(T)/CBS CP interaction energy in eV.
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    e = get_interaction_energy_cc(s22_name)
+    return e
+
+
+def get_interaction_energy_s22x5(name, dist=None, correct_offset=True):
+    """Returns the S22x5 CCSD(T)/CBS CP interaction energy in eV.
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    if dist_ == '0.9':
+        i = 0
+    elif dist_ == '1.0':
+        i = 1
+    elif dist_ == '1.2':
+        i = 2
+    elif dist_ == '1.5':
+        i = 3
+    elif dist_ == '2.0':
+        i = 4
+    else:
+        raise KeyError('error, mate!')
+    e = data[s22_name]['interaction energies s22x5'][i]
+    if correct_offset == True:
+        e *= data[s22_name]['interaction energy CC']/data[s22_name]['interaction energies s22x5'][1]
+    return e
+
+
+def get_name(name,dist=None):
+    """Returns the database name of an s22 system
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    if s22x5_ is True:
+        raise KeyError('System may not be in s22x5')    
+    return data[name]['name']
+
+
+def get_number_of_dimer_atoms(name,dist=None):
+    """Returns the number of atoms in each s22 dimer as a list; [x,y].
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    return data[s22_name]['dimer atoms']
+
+
+def get_s22x5_distance(name, dist=None):
+    """Returns the relative intermolecular distance in angstroms.
+       Values are in Angstrom and are relative to the original s22 distance.
+    """
+    s22_,s22x5_,s22_name,dist_ = identify_s22_sys(name,dist)
+    if s22_ is True:
+        raise KeyError('System must be in s22x5')    
+    else:
+        x00 = data[s22_name]['positions 1.0'][0][0]
+        x01 = data[s22_name]['positions 1.0'][-1][0]
+        x10 = data[s22_name]['positions '+dist_][0][0]
+        x11 = data[s22_name]['positions '+dist_][-1][0]
+        d0 = x01 - x00
+        d1 = x11 - x10
+        return d1-d0
diff --git a/ase/data/tmfp06d.py b/ase/data/tmfp06d.py
new file mode 100644
index 0000000..729be71
--- /dev/null
+++ b/ase/data/tmfp06d.py
@@ -0,0 +1,506 @@
+# Generated: 2011-12-19
+# doi:10.1063/1.2162161, experimental geometries if available, dimers only
+from numpy import array
+data = {
+    'H': {'charges': None,
+          'database': 'TMFP06D',
+          'magmoms': [1.0],
+          'name': 'H',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'H'},
+    'N': {'charges': None,
+          'database': 'TMFP06D',
+          'magmoms': [3.0],
+          'name': 'N',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'N'},
+    'O': {'charges': None,
+          'database': 'TMFP06D',
+          'magmoms': [2.0],
+          'name': 'O',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'O'},
+    'F': {'charges': None,
+          'database': 'TMFP06D',
+          'magmoms': [1.0],
+          'name': 'F',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'F'},
+    #
+    'K': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0],
+           'name': 'K',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'K'},
+    'Ca': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0],
+           'name': 'Ca',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Ca'},
+    'Sc': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0],
+           'name': 'Sc',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Sc'},
+    'Ti': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.0],
+           'name': 'Ti',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Ti'},
+    'V': {'charges': None,
+          'database': 'TMFP06D',
+          'magmoms': [3.0],
+          'name': 'V',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'V'},
+    'Cr': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [6.0],
+           'name': 'Cr',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Cr'},
+    'Mn': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [5.0],
+           'name': 'Mn',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Mn'},
+    'Fe': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [4.0],
+           'name': 'Fe',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Fe'},
+    'Co': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [3.0],
+           'name': 'Co',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Co'},
+    'Ni': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.0],
+           'name': 'Ni',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Ni'},
+    'Cu': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0],
+           'name': 'Cu',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Cu'},
+    'Zn': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0],
+           'name': 'Zn',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Zn'},
+    #
+    'K2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'K2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  3.905],
+                              ]),
+           'symbols': 'KK'},
+    'Ca2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'Ca2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  4.277],
+                              ]),
+           'symbols': 'CaCa'},
+    'Sc2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.0, 2.0],
+           'name': 'Sc2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.74], # from 10.1063/1.480546 (MRSDQCI)
+                              ]),
+           'symbols': 'ScSc'},
+    'Ti2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0, 1.0],
+           'name': 'Ti2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.943],
+                              ]),
+           'symbols': 'TiTi'},
+    'V2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0, 1.0],
+           'name': 'V2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.77], # ref. a
+                              ]),
+           'symbols': 'VV'},
+    'Cr2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'Cr2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.679],
+                              ]),
+           'symbols': 'CrCr'},
+    'Mn2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [5.0, 5.0],
+           'name': 'Mn2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  3.40],
+                              ]),
+           'symbols': 'MnMn'},
+    'Fe2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [3.0, 3.0],
+           'name': 'Fe2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.02],
+                              ]),
+           'symbols': 'FeFe'},
+    # exp. geometry not given in ref.
+    'Co2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.0, 2.0],
+           'name': 'Co2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.98], # calculated doi:10.1063/1.1788656
+                              ]),
+           'symbols': 'CoCo'},
+    'Ni2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0, 1.0],
+           'name': 'Ni2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.155],
+                              ]),
+           'symbols': 'NiNi'},
+    'Cu2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'Cu2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.219],
+                              ]),
+           'symbols': 'CuCu'},
+    'Zn2': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'Zn2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.35], # ref. j
+                              ]),
+           'symbols': 'ZnZn'},
+    #
+    'KH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'KH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.243],
+                              ]),
+           'symbols': 'KH'},
+    'CaH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.7, 0.3],
+           'name': 'CaH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.003],
+                              ]),
+           'symbols': 'CaH'},
+    # exp. geometry not given in ref.
+    'ScH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'ScH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.775], # doi: 10.1063/1.3489110
+                              ]),
+           'symbols': 'ScH'},
+    'TiH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.9, 0.1],
+           'name': 'TiH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.779],
+                              ]),
+           'symbols': 'TiH'},
+    'CrH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [4.9, 0.1],
+           'name': 'CrH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.656],
+                              ]),
+           'symbols': 'CrH'},
+    'MnH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [5.9, 0.1],
+           'name': 'MnH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.731],
+                              ]),
+           'symbols': 'MnH'},
+    'FeH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.9, 0.1],
+           'name': 'FeH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.589],
+                              ]),
+           'symbols': 'FeH'},
+    'CoH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.9, 0.1],
+           'name': 'CoH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.52],
+                              ]),
+           'symbols': 'CoH'},
+    'NiH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.9, 0.1],
+           'name': 'NiH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.475],
+                              ]),
+           'symbols': 'NiH'},
+    'CuH': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'CuH',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.463],
+                              ]),
+           'symbols': 'CuH'},
+    #
+    'ScN': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'ScN',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.687],
+                              ]),
+           'symbols': 'ScN'},
+    'TiN': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.9, 0.1],
+           'name': 'TiN',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.57],
+                              ]),
+           'symbols': 'TiN'},
+    'VN': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.9, 0.1],
+           'name': 'VN',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.57],
+                              ]),
+           'symbols': 'VN'},
+    'CrN': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.9, 0.1],
+           'name': 'CrN',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.563],
+                              ]),
+           'symbols': 'CrN'},
+    #
+    'CaO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'CaO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.822],
+                              ]),
+            'symbols': 'CaO'},
+    'ScO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.9, 0.1],
+           'name': 'ScO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.668],
+                              ]),
+            'symbols': 'ScO'},
+    'TiO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.9, 0.1],
+           'name': 'TiO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.620],
+                              ]),
+            'symbols': 'TiO'},
+    'VO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.9, 0.1],
+           'name': 'VO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.589],
+                              ]),
+            'symbols': 'VO'},
+    'CrO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [3.9, 0.1],
+           'name': 'CrO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.615],
+                              ]),
+            'symbols': 'CrO'},
+    'MnO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [4.9, 0.1],
+           'name': 'MnO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.646],
+                              ]),
+            'symbols': 'MnO'},
+    'FeO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [3.9, 0.1],
+           'name': 'FeO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.616],
+                              ]),
+            'symbols': 'FeO'},
+    'CoO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.5, 0.5],
+           'name': 'CoO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.629],
+                              ]),
+            'symbols': 'CoO'},
+    'NiO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [1.0, 1.0],
+           'name': 'NiO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.627],
+                              ]),
+            'symbols': 'NiO'},
+    'CuO': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.5, 0.5],
+           'name': 'CuO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.724],
+                              ]),
+            'symbols': 'CuO'},
+    # ZnO not in ref. Taken for completeness from doi: 10.1063/1.3489110
+    'ZnO': {'charges': None,
+           'database': 'TMKM11',
+           'magmoms': [0.0, 0.0],
+           'name': 'ZnO',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.724],
+                              ]),
+           'symbols': 'ZnO'},
+    #
+    'KF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'KF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.171],
+                              ]),
+            'symbols': 'KF'},
+    'CaF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.9, 0.1],
+           'name': 'CaF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.967],
+                              ]),
+            'symbols': 'CaF'},
+    'ScF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'ScF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.788],
+                              ]),
+            'symbols': 'ScF'},
+    'TiF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [2.9, 0.1],
+           'name': 'TiF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.834],
+                              ]),
+            'symbols': 'TiF'},
+    'CrF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [4.9, 0.1],
+           'name': 'CrF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.784],
+                              ]),
+            'symbols': 'CrF'},
+    'FeF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [4.9, 0.1],
+           'name': 'FeF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.780],
+                              ]),
+            'symbols': 'FeF'},
+    'CuF': {'charges': None,
+           'database': 'TMFP06D',
+           'magmoms': [0.0, 0.0],
+           'name': 'CuF',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.745],
+                              ]),
+            'symbols': 'CuF'},
+    }
diff --git a/ase/data/tmgmjbp04n.py b/ase/data/tmgmjbp04n.py
new file mode 100644
index 0000000..6ba0f49
--- /dev/null
+++ b/ase/data/tmgmjbp04n.py
@@ -0,0 +1,570 @@
+# Generated: 2012-01-11
+# doi:10.1063/1.1788656 neutral dimers only
+from numpy import array
+data = {
+    'Sc': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0],
+           'name': 'Sc',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Sc'},
+    'Ti': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.0],
+           'name': 'Ti',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Ti'},
+    'V': {'charges': None,
+          'database': 'TMGMJBP04N',
+          'magmoms': [3.0],
+          'name': 'V',
+          'positions': array([[ 0.,  0.,  0.]]),
+          'symbols': 'V'},
+    'Cr': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [6.0],
+           'name': 'Cr',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Cr'},
+    'Mn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [5.0],
+           'name': 'Mn',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Mn'},
+    'Fe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.0],
+           'name': 'Fe',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Fe'},
+    'Co': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.0],
+           'name': 'Co',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Co'},
+    'Ni': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.0],
+           'name': 'Ni',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Ni'},
+    'Cu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0],
+           'name': 'Cu',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Cu'},
+    'Zn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0],
+           'name': 'Zn',
+           'positions': array([[ 0.,  0.,  0.]]),
+           'symbols': 'Zn'},
+    #
+    'Sc2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.0, 2.0],
+           'name': 'Sc2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.63],
+                              ]),
+            'symbols': 'ScSc'},
+    'ScTi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.5, 3.5],
+           'name': 'ScTi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.29],
+                              ]),
+             'symbols': 'ScTi'},
+    'ScV': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0, 5.0],
+           'name': 'ScV',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.51],
+                              ]),
+            'symbols': 'ScV'},
+    'ScCr': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 2.9],
+           'name': 'ScCr',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.99],
+                              ]),
+             'symbols': 'ScCr'},
+    'ScMn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 1.9],
+           'name': 'ScMn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.94],
+                              ]),
+            'symbols': 'ScMn'},
+    'ScFe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 0.9],
+           'name': 'ScFe',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.85],
+                              ]),
+            'symbols': 'ScFe'},
+    'ScCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'ScCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.80],
+                              ]),
+            'symbols': 'ScCo'},
+    'ScNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.5, 0.5],
+           'name': 'ScNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.05],
+                              ]),
+            'symbols': 'ScNi'},
+    'ScCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.9, 0.1],
+           'name': 'ScCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.54],
+                              ]),
+            'symbols': 'ScCu'},
+    'ScZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'ScZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.71],
+                              ]),
+            'symbols': 'ScZn'},
+    #
+    'Ti2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0, 1.0],
+           'name': 'Ti2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.89],
+                              ]),
+           'symbols': 'TiTi'},
+    'TiV': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0, 2.0],
+           'name': 'TiV',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.78],
+                              ]),
+           'symbols': 'TiV'},
+    'TiCr': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 1.9],
+           'name': 'TiCr',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.79],
+                              ]),
+           'symbols': 'TiCr'},
+    'TiMn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 0.9],
+           'name': 'TiMn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.76],
+                              ]),
+           'symbols': 'TiMn'},
+    'TiFe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'TiFe',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.67],
+                              ]),
+           'symbols': 'TiFe'},
+    'TiCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.9, 0.1],
+           'name': 'TiCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.88],
+                              ]),
+           'symbols': 'TiCo'},
+    'TiNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.9, 0.1],
+           'name': 'TiNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.06],
+                              ]),
+           'symbols': 'TiNi'},
+    'TiCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'TiCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.45],
+                              ]),
+           'symbols': 'TiCu'},
+    'TiZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.9, 0.1],
+           'name': 'TiZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.74],
+                              ]),
+           'symbols': 'TiZn'},
+    #
+    'V2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0, 1.0],
+           'name': 'V2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.74],
+                              ]),
+           'symbols': 'VV'},
+    'VCr': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 0.9],
+           'name': 'VCr',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.72],
+                              ]),
+           'symbols': 'VCr'},
+    'VMn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'VMn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.69],
+                              ]),
+           'symbols': 'VMn'},
+    'VFe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.9, 0.1],
+           'name': 'VFe',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.74],
+                              ]),
+           'symbols': 'VFe'},
+    'VCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.9, 0.1],
+           'name': 'VCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.81],
+                              ]),
+           'symbols': 'VCo'},
+    'VNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'VNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.11],
+                              ]),
+           'symbols': 'VNi'},
+    'VCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.9, 0.1],
+           'name': 'VCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.42],
+                              ]),
+           'symbols': 'VCu'},
+    'VZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.9, 0.1],
+           'name': 'VZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.71],
+                              ]),
+           'symbols': 'VZn'},
+    #
+    'Cr2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'Cr2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.75],
+                              ]),
+           'symbols': 'CrCr'},
+    'CrMn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 0.9],
+           'name': 'CrMn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.46],
+                              ]),
+           'symbols': 'CrMn'},
+    'CrFe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.1, 1.9],
+           'name': 'CrFe',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.37],
+                              ]),
+           'symbols': 'CrFe'},
+    'CrCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'CrCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.34],
+                              ]),
+           'symbols': 'CrCo'},
+    'CrNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.9, 0.1],
+           'name': 'CrNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.25],
+                              ]),
+           'symbols': 'CrNi'},
+    'CrCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.9, 0.1],
+           'name': 'CrCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.42],
+                              ]),
+           'symbols': 'CrCu'},
+    'CrZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [5.9, 0.1],
+           'name': 'CrZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.75],
+                              ]),
+           'symbols': 'CrZn'},
+    #
+    'Mn2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [5.0, 5.0],
+           'name': 'Mn2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.62],
+                              ]),
+           'symbols': 'MnMn'},
+    'MnFe': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [5.0, 4.0],
+           'name': 'MnFe',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.42],
+                              ]),
+           'symbols': 'MnFe'},
+    'MnCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.0, 2.0],
+           'name': 'MnCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.09],
+                              ]),
+           'symbols': 'MnCo'},
+    'MnNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.0, 1.0],
+           'name': 'MnNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.09],
+                              ]),
+           'symbols': 'MnNi'},
+    'MnCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [5.0, 1.0],
+           'name': 'MnCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.37],
+                              ]),
+           'symbols': 'MnCu'},
+    'MnZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.9, 0.1],
+           'name': 'MnZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.99],
+                              ]),
+           'symbols': 'MnZn'},
+    #
+    'Fe2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.0, 3.0],
+           'name': 'Fe2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.01],
+                              ]),
+           'symbols': 'FeFe'},
+    'FeCo': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [4.0, 1.0],
+           'name': 'FeCo',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.96],
+                              ]),
+           'symbols': 'FeCo'},
+    'FeNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.0, 1.0],
+           'name': 'FeNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.07],
+                              ]),
+           'symbols': 'FeNi'},
+    'FeCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'FeCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.31],
+                              ]),
+           'symbols': 'FeCu'},
+    'FeZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [3.9, 0.1],
+           'name': 'FeZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.53],
+                              ]),
+           'symbols': 'FeZn'},
+    #
+    'Co2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.0, 2.0],
+           'name': 'Co2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  1.98],
+                              ]),
+           'symbols': 'CoCo'},
+    'CoNi': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.0, 1.0],
+           'name': 'CoNi',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.10],
+                              ]),
+           'symbols': 'CoNi'},
+    'CoCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.9, 0.1],
+           'name': 'CoCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.26],
+                              ]),
+           'symbols': 'CoCu'},
+    'CoZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [2.9, 0.1],
+           'name': 'CoZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.44],
+                              ]),
+           'symbols': 'CoZn'},
+    #
+    'Ni2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.0, 1.0],
+           'name': 'Ni2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.11],
+                              ]),
+           'symbols': 'NiNi'},
+    'NiCu': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.9, 0.1],
+           'name': 'NiCu',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.25],
+                              ]),
+           'symbols': 'NiCu'},
+    'NiZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [1.9, 0.1],
+           'name': 'NiZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.39],
+                              ]),
+           'symbols': 'NiZn'},
+    #
+    'Cu2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'Cu2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.25],
+                              ]),
+           'symbols': 'CuCu'},
+    'CuZn': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.5, 0.5],
+           'name': 'CuZn',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  2.40],
+                              ]),
+           'symbols': 'CuZn'},
+    #
+    'Zn2': {'charges': None,
+           'database': 'TMGMJBP04N',
+           'magmoms': [0.0, 0.0],
+           'name': 'Zn2',
+           'positions': array([
+                              [ 0.,  0.,  0.],
+                              [ 0.,  0.,  3.27],
+                              ]),
+           'symbols': 'ZnZn'},
+    }
diff --git a/ase/data/tmxr200x.py b/ase/data/tmxr200x.py
new file mode 100644
index 0000000..073604f
--- /dev/null
+++ b/ase/data/tmxr200x.py
@@ -0,0 +1,264 @@
+import os
+import pprint
+import re
+from urllib import urlretrieve
+import zipfile
+
+import datetime
+
+try:
+    from subprocess import Popen, PIPE
+except ImportError:
+    from os import popen3
+else:
+    def popen3(cmd):
+        p = Popen(cmd, shell=True, close_fds=True,
+                  stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        return p.stdin, p.stdout, p.stderr
+
+import numpy as np
+
+import ase.io
+from ase.atom import Atom
+from ase.atoms import Atoms
+from ase.data import atomic_numbers, chemical_symbols
+from ase.data import ground_state_magnetic_moments
+
+# Transition Metals First-row (TM1R): 10.1021/ct6001187 # 32 compounds
+# Transition Metals Second-row (TM2R): 10.1021/ct700178y # 19 compounds
+# Transition Metals Third-row (TM3R): 10.1021/ct800172j # 25 compounds
+
+#http://pubs.acs.org/doi/suppl/10.1021/ct6001187/suppl_file/ct6001187-file002.pdf
+#http://pubs.acs.org/doi/suppl/10.1021/ct700178y/suppl_file/ct700178y-file002.pdf
+#http://pubs.acs.org/doi/suppl/10.1021/ct800172j/suppl_file/ct800172j_si_001.pdf
+
+url_root = 'http://pubs.acs.org/doi/suppl/'
+journal = '10.1021'
+database_files = {
+    'TM1R2006': {'doi': journal + '/ct6001187', 'module': 'TMXR200X_TM1R2006'},
+    'TM2R2007': {'doi': journal + '/ct700178y', 'module': 'TMXR200X_TM2R2007'},
+    'TM3R2008': {'doi': journal + '/ct800172j', 'module': 'TMXR200X_TM3R2008'},
+    }
+
+database_files['TM1R2006']['pdf'] = database_files['TM1R2006']['doi'] + '/suppl_file/ct6001187-file002.pdf'
+
+database_files['TM2R2007']['pdf'] = database_files['TM2R2007']['doi'] + '/suppl_file/ct700178y-file002.pdf'
+
+database_files['TM3R2008']['pdf'] = database_files['TM3R2008']['doi'] + '/suppl_file/ct800172j_si_001.pdf'
+
+def download_file(url, filename, dir='.'):
+    # do not mirror subdirectory structure of url
+    outfile = os.path.join(dir, os.path.basename(filename))
+    if 0: # fails, use files from disk
+        urlretrieve(os.path.join(url, filename), outfile)
+    return outfile
+
+def read_geometries(filename, dir='.'):
+    txt = os.path.join(dir, filename)
+    fh = open(txt, 'rb')
+    table = fh.read()
+    firstsplit = '(in xyz format):' # TM1R2006 and TM2R2007
+    dataformat = 'xyz'
+    if table.find('(Gaussian archive entries):') != -1:
+        firstsplit = '(Gaussian archive entries):' # TM3R2008
+        dataformat = 'gaussian'
+    table = table.split(firstsplit)
+    table = table[1]
+    # remove one or two digit numbers (page numbers/numbers of atoms in xyz format)
+    table = re.sub('\n\d\d\n', '\n', table)
+    table = re.sub('\n\d\n', '\n', table)
+    # remove S + two digit numbers (page numbers)
+    table = re.sub('\nS\d\d\n', '\n', table)
+    # remove S + one digit (page numbers)
+    table = re.sub('\nS\d\n', '\n', table)
+    # remove empty lines
+    # http://stackoverflow.com/questions/1140958/whats-a-quick-one-liner-to-remove-empty-lines-from-a-python-string
+    table = os.linesep.join([s for s in table.splitlines() if s])
+    geometries = []
+    if dataformat == 'xyz':
+        # split on new lines
+        table = table.split('\n')
+        # mark compound names with ':' tags
+        for n, line in enumerate(table):
+            if not (line.find('.') != -1):
+                # remove method/basis set information
+                table[n] = table[n].replace(' BP86/qzvp', '')
+                table[n] = ':' + table[n] + ':'
+        table = '\n'.join([s for s in table])
+        # split into compounds
+        # http://simonwillison.net/2003/Oct/26/reSplit/
+        # http://stackoverflow.com/questions/647655/python-regex-split-and-special-character
+        table = re.compile('(:.*:)').split(table)
+        # remove empty elements
+        table = [l.strip() for l in table]
+        table = [l for l in table if len(l) > 1]
+        # extract compounds
+        for n in range(0, len(table), 2):
+            compound = table[n].replace(':', '').replace(' ', '_')
+            geometry = []
+            for atom in table[n+1].split('\n'):
+                geometry.append(Atom(symbol=atom.split()[0], position=atom.split()[1:]))
+            atoms = Atoms(geometry)
+            # set the charge and magnetic moment on the heaviest atom (better ideas?)
+            heaviest = max([a.get_atomic_number() for a in atoms])
+            heaviest_index = [a.get_atomic_number() for a in atoms].index(heaviest)
+            charge = 0.0
+            if abs(charge) > 0.0:
+                charges = [0.0 for a in atoms]
+                charges[heaviest_index] = charge
+                atoms.set_charges(charges)
+            if compound in [ # see corresponding articles
+                'Ti(BH4)3',  # TM1R2006
+                'V(NMe2)4',  # TM1R2006
+                'Cu(acac)2',  # TM1R2006
+                'Nb(Cp)(C7H7)_Cs', # TM2R2007
+                'CdMe_C3v', # TM2R2007
+                ]:
+                multiplicity = 2.0
+            else:
+                multiplicity = 1.0
+            if multiplicity > 1.0:
+                magmoms = [0.0 for a in atoms]
+                magmoms[heaviest_index] = multiplicity - 1
+                atoms.set_initial_magnetic_moments(magmoms)
+            geometries.append((compound, atoms))
+    elif dataformat == 'gaussian':
+        # remove new lines
+        table = table.replace('\n', '')
+        # fix: MeHg(Cl) written as MeHg(CN)
+        table = table.replace(
+            'MeHg(CN), qzvp (SDD/def-qzvp for metal)\\\\0,1\\Hg,0.,0.,0.1975732257',
+            'MeHg(Cl), qzvp (SDD/def-qzvp for metal)\\\\0,1\\Hg,0.,0.,0.1975732257')
+        # split on compound end marks
+        table = table.split('\\\@')
+        # remove empty elements
+        table = [l.strip() for l in table]
+        table = [l for l in table if len(l) > 1]
+        # extract compounds
+        for n, line in enumerate(table):
+            # split on gaussian separator '\\'
+            entries = line.split('\\\\')
+            compound = entries[2].split(',')[0].split(' ')[0]
+            # charge and multiplicity from gaussian archive
+            charge, multiplicity = entries[3].split('\\')[0].split(',')
+            charge = float(charge)
+            multiplicity = float(multiplicity)
+            if compound in ['Au(Me)PMe3']: # in gzmat format!
+                # check openbabel version (babel >= 2.2 needed)
+                cmd = popen3('babel -V')[1]
+                output = cmd.read().strip()
+                cmd.close()
+                v1, v2, v3 = output.split()[2].split('.')
+                v1, v2, v3 = int(v1), int(v2), int(v3)
+                if not (v1 > 2 or ((v1 == 2) and (v2 >= 2))):
+                    print compound + ': skipped - version of babel does not support gzmat format'
+                    continue # this one is given in z-matrix format
+                finame = compound.replace('(', '').replace(')', '') + '.orig'
+                foname = finame.split('.')[0] + '.xyz'
+                fi = open(finame, 'w')
+                fo = open(foname, 'w')
+                if 1: # how to extract zmat by hand
+                    zmat = ['#'] # must start with gaussian input start
+                    zmat.extend('@') # separated by newline
+                    zmat.extend([compound])
+                    zmat.extend('@') # separated by newline
+                    zmat.extend([str(int(charge)) + ' ' + str(int(multiplicity))])
+                    zmat.extend(entries[3].replace(',', ' ').split('\\')[1:])
+                    zmat.extend('@') # atom and variable definitions separated by newline
+                    zmat.extend(entries[4].split('\\'))
+                    zmat.extend('@') # end with newline
+                    for l in zmat:
+                        fi.write(l.replace('@', '').replace('=', ' ') + '\n')
+                    fi.close()
+                if 0:
+                    # or use the whole gausian archive entry
+                    entries = ''.join(entries)
+                    fi.write(entries)
+                # convert gzmat into xyz using openbabel (babel >= 2.2 needed)
+                cmd = popen3('babel -i gzmat ' + finame + ' -o xyz ' + foname)[2]
+                error = cmd.read().strip()
+                cmd.close()
+                fo.close()
+                if not (error.find('0 molecules') != -1):
+                    atoms = ase.io.read(foname)
+                else:
+                    print compound + ': babel conversion failed'
+                    continue # conversion failed
+            else:
+                positions = entries[3].replace(',', ' ').split('\\')[1:]
+                geometry = []
+                for k, atom in enumerate(positions):
+                    geometry.append(Atom(symbol=atom.split()[0],
+                                         position=[float(p) for p in atom.split()[1:]]))
+                atoms = Atoms(geometry)
+            #
+            # set the charge and magnetic moment on the heaviest atom (better ideas?)
+            heaviest = max([a.get_atomic_number() for a in atoms])
+            heaviest_index = [a.get_atomic_number() for a in atoms].index(heaviest)
+            if abs(charge) > 0.0:
+                charges = [0.0 for a in atoms]
+                charges[heaviest_index] = charge
+                atoms.set_charges(charges)
+            if multiplicity > 1.0:
+                magmoms = [0.0 for a in atoms]
+                magmoms[heaviest_index] = multiplicity - 1
+                atoms.set_initial_magnetic_moments(magmoms)
+            geometries.append((compound, atoms))
+    return geometries
+
+def pdftotext(filename):
+    os.system('pdftotext -raw -nopgbrk '+ filename)
+    return os.path.splitext(filename)[0] + '.txt'
+
+from ase.data.gmtkn30 import format_data
+
+def main():
+    if not os.path.isdir('TMXR200X'):
+        os.makedirs('TMXR200X')
+    #for database in ['TM1R2006']:
+    for database in database_files.keys():
+        fh = open(database_files[database]['module'].lower() + '.py', 'w')
+        fh.write('# Computer generated code! Hands off!\n')
+        fh.write('# Generated: ' + str(datetime.date.today()) + '\n')
+        fh.write('from numpy import array\n')
+        fh.write('data = ')
+        data = {} # specification of molecules
+        info = {} # reference/calculation info
+        # download structures
+        file = database_files[database]['pdf']
+        f = os.path.abspath(download_file(url_root, file, dir='TMXR200X'))
+        f = pdftotext(f)
+        geometries = read_geometries(f)
+        # set number of unpaired electrons and charges
+        no_unpaired_electrons = []
+        charges = []
+        for a in geometries:
+            magmom = sum(a[1].get_initial_magnetic_moments())
+            if magmom > 0.0:
+                no_unpaired_electrons.append((a[0], magmom))
+            charge = sum(a[1].get_charges())
+            if abs(charge) > 0.0:
+                charges.append((a[0], charge))
+        data = format_data(database, geometries, no_unpaired_electrons, charges)
+        # all constituent atoms
+        atoms = []
+        for formula, geometry in geometries:
+            atoms.extend(list(set(geometry.get_chemical_symbols())))
+        atoms=list(set(atoms))
+        atoms.sort()
+        for atom in atoms:
+            magmom=ground_state_magnetic_moments[atomic_numbers[atom]]
+            data[atom] = {
+                'database': database,
+                'name': atom,
+                'symbols': atom,
+                'magmoms': [magmom], # None or list
+                'charges': None, # None or list
+                'positions': np.array([[0.0]*3]),
+                }
+            Atom(atom, magmom=magmom)
+        pprint.pprint(data, stream=fh)
+        fh.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/ase/data/tmxr200x_tm1r2006.py b/ase/data/tmxr200x_tm1r2006.py
new file mode 100644
index 0000000..e1dbf86
--- /dev/null
+++ b/ase/data/tmxr200x_tm1r2006.py
@@ -0,0 +1,736 @@
+# Computer generated code! Hands off!
+# Generated: 2011-08-30
+from numpy import array
+data = {'B': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [1.0],
+       'name': 'B',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'B'},
+ 'C': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [2.0],
+       'name': 'C',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'C'},
+ 'Cl': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [1.0],
+        'name': 'Cl',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cl'},
+ 'Co': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [3.0],
+        'name': 'Co',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Co'},
+ 'Co(CO)3(NO)': {'charges': None,
+                 'database': 'TM1R2006',
+                 'magmoms': None,
+                 'name': 'C3CoO4N',
+                 'positions': array([[ 0.      ,  0.      ,  0.122333],
+       [ 0.      ,  0.      ,  1.78519 ],
+       [ 0.      ,  1.629018, -0.666179],
+       [ 1.410771, -0.814509, -0.666179],
+       [-1.410771, -0.814509, -0.666179],
+       [ 0.      ,  0.      ,  2.94736 ],
+       [ 0.      ,  2.677027, -1.141125],
+       [ 2.318373, -1.338513, -1.141125],
+       [-2.318373, -1.338513, -1.141125]]),
+                 'symbols': 'CoNCCCOOOO'},
+ 'CoH(CO)4': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': None,
+              'name': 'HC4CoO4',
+              'positions': array([[ 0.169633, -0.068726,  0.      ],
+       [ 1.547021, -0.624911,  0.      ],
+       [ 1.098898,  1.466351,  0.      ],
+       [ 0.100109, -0.996887,  1.534435],
+       [ 0.100109, -0.996887, -1.534435],
+       [-1.49914 ,  0.607866,  0.      ],
+       [ 1.738941,  2.42264 ,  0.      ],
+       [ 0.104467, -1.606465,  2.510398],
+       [ 0.104467, -1.606465, -2.510398],
+       [-2.563745,  1.040021,  0.      ]]),
+              'symbols': 'CoHCCCCOOOO'},
+ 'Cr': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [6.0],
+        'name': 'Cr',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cr'},
+ 'Cr(C6H6)(CO)3': {'charges': None,
+                   'database': 'TM1R2006',
+                   'magmoms': None,
+                   'name': 'CrC9O3H6',
+                   'positions': array([[ 0.      ,  0.      ,  0.079343],
+       [ 0.669399,  1.249046, -1.636118],
+       [ 1.41734 ,  0.054603, -1.638444],
+       [ 0.747006, -1.20424 , -1.636118],
+       [-0.661382, -1.254754, -1.638444],
+       [-1.416405, -0.044806, -1.636118],
+       [-0.755958,  1.200151, -1.638444],
+       [ 1.179092,  2.209631, -1.595473],
+       [ 2.504054,  0.091803, -1.599339],
+       [ 1.324051, -2.125939, -1.595473],
+       [-1.172524, -2.214476, -1.599339],
+       [-2.503143, -0.083692, -1.595473],
+       [-1.33153 ,  2.122673, -1.599339],
+       [ 0.012121,  1.481385,  1.178868],
+       [-1.288978, -0.730195,  1.178868],
+       [ 1.276857, -0.75119 ,  1.178868],
+       [ 0.020703,  2.420708,  1.866466],
+       [-2.106746, -1.192425,  1.866466],
+       [ 2.086043, -1.228283,  1.866466]]),
+                   'symbols': 'CrCCCCCCHHHHHHCCCOOO'},
+ 'Cr(C6H6)2': {'charges': None,
+               'database': 'TM1R2006',
+               'magmoms': None,
+               'name': 'CrC12H12',
+               'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  1.421155,  1.619206],
+       [-1.230757,  0.710578,  1.619206],
+       [-1.230757, -0.710578,  1.619206],
+       [ 0.      , -1.421155,  1.619206],
+       [ 1.230757, -0.710578,  1.619206],
+       [ 1.230757,  0.710578,  1.619206],
+       [ 0.      ,  1.421155, -1.619206],
+       [ 1.230757,  0.710578, -1.619206],
+       [ 1.230757, -0.710578, -1.619206],
+       [ 0.      , -1.421155, -1.619206],
+       [-1.230757, -0.710578, -1.619206],
+       [-1.230757,  0.710578, -1.619206],
+       [ 0.      ,  2.511301,  1.57524 ],
+       [-2.17485 ,  1.25565 ,  1.57524 ],
+       [-2.17485 , -1.25565 ,  1.57524 ],
+       [ 0.      , -2.511301,  1.57524 ],
+       [ 2.17485 , -1.25565 ,  1.57524 ],
+       [ 2.17485 ,  1.25565 ,  1.57524 ],
+       [ 0.      ,  2.511301, -1.57524 ],
+       [ 2.17485 ,  1.25565 , -1.57524 ],
+       [ 2.17485 , -1.25565 , -1.57524 ],
+       [ 0.      , -2.511301, -1.57524 ],
+       [-2.17485 , -1.25565 , -1.57524 ],
+       [-2.17485 ,  1.25565 , -1.57524 ]]),
+               'symbols': 'CrCCCCCCCCCCCCHHHHHHHHHHHH'},
+ 'Cr(NO)4': {'charges': None,
+             'database': 'TM1R2006',
+             'magmoms': None,
+             'name': 'CrO4N4',
+             'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 1.006462,  1.006462,  1.006462],
+       [-1.006462, -1.006462,  1.006462],
+       [-1.006462,  1.006462, -1.006462],
+       [ 1.006462, -1.006462, -1.006462],
+       [ 1.681869,  1.681869,  1.681869],
+       [-1.681869, -1.681869,  1.681869],
+       [-1.681869,  1.681869, -1.681869],
+       [ 1.681869, -1.681869, -1.681869]]),
+             'symbols': 'CrNNNNOOOO'},
+ 'CrO2(NO3)2': {'charges': None,
+                'database': 'TM1R2006',
+                'magmoms': None,
+                'name': 'CrO8N2',
+                'positions': array([[ 0.      ,  0.      ,  0.831366],
+       [-1.274141,  0.115328,  1.750816],
+       [ 1.274141, -0.115328,  1.750816],
+       [-0.012757,  1.823831,  0.190162],
+       [ 0.012757, -1.823831,  0.190162],
+       [ 0.864925,  1.702663, -0.832149],
+       [-0.864925, -1.702663, -0.832149],
+       [ 1.276969,  0.51347 , -0.956167],
+       [-1.276969, -0.51347 , -0.956167],
+       [ 1.162006,  2.65268 , -1.497278],
+       [-1.162006, -2.65268 , -1.497278]]),
+                'symbols': 'CrOOOONNOOOO'},
+ 'CrO2Cl2': {'charges': None,
+             'database': 'TM1R2006',
+             'magmoms': None,
+             'name': 'CrO2Cl2',
+             'positions': array([[ 0.      ,  0.      ,  0.357011],
+       [ 0.      ,  1.282258,  1.269789],
+       [ 0.      , -1.282258,  1.269789],
+       [ 1.75172 ,  0.      , -0.849556],
+       [-1.75172 ,  0.      , -0.849556]]),
+             'symbols': 'CrOOClCl'},
+ 'CrO2F2': {'charges': None,
+            'database': 'TM1R2006',
+            'magmoms': None,
+            'name': 'CrO2F2',
+            'positions': array([[ 0.      ,  0.      ,  0.051762],
+       [ 0.      ,  1.274503,  0.97249 ],
+       [ 0.      , -1.274503,  0.97249 ],
+       [ 1.413706,  0.      , -0.933452],
+       [-1.413706,  0.      , -0.933452]]),
+            'symbols': 'CrOOFF'},
+ 'Cu': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [1.0],
+        'name': 'Cu',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cu'},
+ 'Cu(acac)2': {'charges': None,
+               'database': 'TM1R2006',
+               'magmoms': array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.]),
+               'name': 'H14C10CuO4',
+               'positions': array([[-4.354988,  0.      ,  0.      ],
+       [-3.26715 ,  0.      ,  0.      ],
+       [-2.607363,  1.242531,  0.      ],
+       [-1.338538,  1.410816,  0.      ],
+       [-3.420342,  2.520011,  0.      ],
+       [-3.157321,  3.118553,  0.883267],
+       [-4.498751,  2.327325,  0.      ],
+       [ 0.      ,  0.      ,  0.      ],
+       [ 4.354988,  0.      ,  0.      ],
+       [ 3.26715 ,  0.      ,  0.      ],
+       [ 2.607363, -1.242531,  0.      ],
+       [-2.607363, -1.242531,  0.      ],
+       [ 2.607363,  1.242531,  0.      ],
+       [ 1.338538, -1.410816,  0.      ],
+       [-1.338538, -1.410816,  0.      ],
+       [ 1.338538,  1.410816,  0.      ],
+       [ 3.420342, -2.520011,  0.      ],
+       [-3.420342, -2.520011,  0.      ],
+       [ 3.420342,  2.520011,  0.      ],
+       [ 3.157321, -3.118553,  0.883267],
+       [-3.157321, -3.118553, -0.883267],
+       [ 3.157321,  3.118553, -0.883267],
+       [-3.157321,  3.118553, -0.883267],
+       [ 3.157321, -3.118553, -0.883267],
+       [-3.157321, -3.118553,  0.883267],
+       [ 3.157321,  3.118553,  0.883267],
+       [ 4.498751, -2.327325,  0.      ],
+       [-4.498751, -2.327325,  0.      ],
+       [ 4.498751,  2.327325,  0.      ]]),
+               'symbols': 'HCCOCHHCuHCCCCOOOCCCHHHHHHHHHH'},
+ 'CuCN': {'charges': None,
+          'database': 'TM1R2006',
+          'magmoms': None,
+          'name': 'CCuN',
+          'positions': array([[ 0.      ,  0.      ,  0.758411],
+       [ 0.      ,  0.      , -1.061968],
+       [ 0.      ,  0.      , -2.231729]]),
+          'symbols': 'CuCN'},
+ 'CuMe': {'charges': None,
+          'database': 'TM1R2006',
+          'magmoms': None,
+          'name': 'H3CCu',
+          'positions': array([[ 0.      ,  0.      ,  0.475856],
+       [ 0.      ,  0.      , -1.418944],
+       [ 0.      ,  1.042163, -1.762057],
+       [ 0.90254 , -0.521081, -1.762057],
+       [-0.90254 , -0.521081, -1.762057]]),
+          'symbols': 'CuCHHH'},
+ 'F': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [1.0],
+       'name': 'F',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'F'},
+ 'Fe': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [4.0],
+        'name': 'Fe',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Fe'},
+ 'Fe(C5Me5)(P5)': {'charges': None,
+                   'database': 'TM1R2006',
+                   'magmoms': None,
+                   'name': 'P5C10FeH15',
+                   'positions': array([[  0.00000000e+00,   0.00000000e+00,   8.94940000e-02],
+       [  3.78124000e-01,   1.16439800e+00,  -1.63229800e+00],
+       [  1.22425500e+00,   2.01000000e-04,  -1.63229800e+00],
+       [  3.78507000e-01,  -1.16427400e+00,  -1.63229800e+00],
+       [ -9.90325000e-01,  -7.19762000e-01,  -1.63229800e+00],
+       [ -9.90561000e-01,   7.19436000e-01,  -1.63229800e+00],
+       [ -4.13627000e-01,   1.76865100e+00,   1.62799200e+00],
+       [ -1.80990500e+00,   1.53161000e-01,   1.62799200e+00],
+       [ -7.04956000e-01,  -1.67399300e+00,   1.62799200e+00],
+       [  1.37421900e+00,  -1.18774500e+00,   1.62799200e+00],
+       [  1.55427000e+00,   9.39926000e-01,   1.62799200e+00],
+       [  8.40577000e-01,   2.58578700e+00,  -1.71656500e+00],
+       [  2.71898200e+00,  -3.84000000e-04,  -1.71656500e+00],
+       [  8.39847000e-01,  -2.58602400e+00,  -1.71656500e+00],
+       [ -2.19992800e+00,  -1.59786700e+00,  -1.71656500e+00],
+       [ -2.19947700e+00,   1.59848800e+00,  -1.71656500e+00],
+       [  1.42717000e-01,   3.26911600e+00,  -1.21693800e+00],
+       [  3.15321600e+00,   8.74481000e-01,  -1.21693800e+00],
+       [  1.80607800e+00,  -2.72865800e+00,  -1.21693800e+00],
+       [ -2.03699900e+00,  -2.56088400e+00,  -1.21693800e+00],
+       [ -3.06501200e+00,   1.14594400e+00,  -1.21693800e+00],
+       [  1.82549400e+00,   2.71840500e+00,  -1.25219600e+00],
+       [  3.14946500e+00,  -8.96115000e-01,  -1.25219600e+00],
+       [  1.20982000e-01,  -3.27223400e+00,  -1.25219600e+00],
+       [ -3.07469400e+00,  -1.12623700e+00,  -1.25219600e+00],
+       [ -2.02124800e+00,   2.57618200e+00,  -1.25219600e+00],
+       [  9.22453000e-01,   2.89959200e+00,  -2.76981700e+00],
+       [  3.04273000e+00,   1.87180000e-02,  -2.76981700e+00],
+       [  9.58057000e-01,  -2.88802400e+00,  -2.76981700e+00],
+       [ -2.45061800e+00,  -1.80361500e+00,  -2.76981700e+00],
+       [ -2.47262300e+00,   1.77332800e+00,  -2.76981700e+00]]),
+                   'symbols': 'FeCCCCCPPPPPCCCCCHHHHHHHHHHHHHHH'},
+ 'Fe(CO)2(NO)2': {'charges': None,
+                  'database': 'TM1R2006',
+                  'magmoms': None,
+                  'name': 'C2FeO4N2',
+                  'positions': array([[  0.00000000e+00,   0.00000000e+00,   1.30071000e-01],
+       [ -1.38784900e+00,  -8.20000000e-05,  -1.04825300e+00],
+       [  1.38784900e+00,   8.20000000e-05,  -1.04825300e+00],
+       [ -8.00000000e-06,   1.46363300e+00,   9.39560000e-01],
+       [  8.00000000e-06,  -1.46363300e+00,   9.39560000e-01],
+       [ -2.28772400e+00,  -1.13000000e-04,  -1.76703100e+00],
+       [  2.28772400e+00,   1.13000000e-04,  -1.76703100e+00],
+       [  1.52000000e-04,   2.47483300e+00,   1.51974100e+00],
+       [ -1.52000000e-04,  -2.47483300e+00,   1.51974100e+00]]),
+                  'symbols': 'FeCCNNOOOO'},
+ 'Fe(CO)3(tmm)': {'charges': None,
+                  'database': 'TM1R2006',
+                  'magmoms': None,
+                  'name': 'H6C7FeO3',
+                  'positions': array([[ 0.      ,  0.      ,  0.015198],
+       [ 0.      ,  0.      , -1.938396],
+       [ 0.      , -1.394127, -1.614155],
+       [-1.207349,  0.697064, -1.614155],
+       [ 1.207349,  0.697064, -1.614155],
+       [-0.922915, -1.965174, -1.708739],
+       [ 0.922915, -1.965174, -1.708739],
+       [-1.240433,  1.781855, -1.708739],
+       [-2.163348,  0.183319, -1.708739],
+       [ 2.163348,  0.183319, -1.708739],
+       [ 1.240433,  1.781855, -1.708739],
+       [ 0.      ,  1.558543,  0.88711 ],
+       [ 1.349738, -0.779272,  0.88711 ],
+       [-1.349738, -0.779272,  0.88711 ],
+       [ 0.      ,  2.572496,  1.441607],
+       [ 2.227847, -1.286248,  1.441607],
+       [-2.227847, -1.286248,  1.441607]]),
+                  'symbols': 'FeCCCCHHHHHHCCCOOO'},
+ 'Fe(CO)4(C2H4)': {'charges': None,
+                   'database': 'TM1R2006',
+                   'magmoms': None,
+                   'name': 'H4C6FeO4',
+                   'positions': array([[ 0.      ,  0.      ,  0.035592],
+       [ 0.      ,  0.705223,  2.059376],
+       [ 0.      , -0.705223,  2.059376],
+       [ 1.804574,  0.      ,  0.077246],
+       [-1.804574,  0.      ,  0.077246],
+       [ 0.      ,  1.494469, -0.957308],
+       [ 0.      , -1.494469, -0.957308],
+       [ 2.956769,  0.      ,  0.103947],
+       [-2.956769,  0.      ,  0.103947],
+       [ 0.      ,  2.448186, -1.609889],
+       [ 0.      , -2.448186, -1.609889],
+       [-0.914881,  1.25654 ,  2.273583],
+       [ 0.914881,  1.25654 ,  2.273583],
+       [ 0.914881, -1.25654 ,  2.273583],
+       [-0.914881, -1.25654 ,  2.273583]]),
+                   'symbols': 'FeCCCCCCOOOOHHHH'},
+ 'Fe(CO)5': {'charges': None,
+             'database': 'TM1R2006',
+             'magmoms': None,
+             'name': 'C5FeO5',
+             'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  1.807856,  0.      ],
+       [ 1.565649, -0.903928,  0.      ],
+       [-1.565649, -0.903928,  0.      ],
+       [ 0.      ,  0.      ,  1.808029],
+       [ 0.      ,  0.      , -1.808029],
+       [ 0.      ,  2.96131 ,  0.      ],
+       [ 2.56457 , -1.480655,  0.      ],
+       [-2.56457 , -1.480655,  0.      ],
+       [ 0.      ,  0.      ,  2.95866 ],
+       [ 0.      ,  0.      , -2.95866 ]]),
+             'symbols': 'FeCCCCCOOOOO'},
+ 'FeCp2': {'charges': None,
+           'database': 'TM1R2006',
+           'magmoms': None,
+           'name': 'H10C10Fe',
+           'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  1.22008 ,  1.650626],
+       [-1.160365,  0.377025,  1.650626],
+       [-0.717145, -0.987065,  1.650626],
+       [ 0.717145, -0.987065,  1.650626],
+       [ 1.160365,  0.377025,  1.650626],
+       [ 0.      ,  1.22008 , -1.650626],
+       [ 1.160365,  0.377025, -1.650626],
+       [ 0.717145, -0.987065, -1.650626],
+       [-0.717145, -0.987065, -1.650626],
+       [-1.160365,  0.377025, -1.650626],
+       [ 0.      ,  2.306051,  1.635648],
+       [-2.193184,  0.712609,  1.635648],
+       [-1.355463, -1.865634,  1.635648],
+       [ 1.355463, -1.865634,  1.635648],
+       [ 2.193184,  0.712609,  1.635648],
+       [ 0.      ,  2.306051, -1.635648],
+       [ 2.193184,  0.712609, -1.635648],
+       [ 1.355463, -1.865634, -1.635648],
+       [-1.355463, -1.865634, -1.635648],
+       [-2.193184,  0.712609, -1.635648]]),
+           'symbols': 'FeCCCCCCCCCCHHHHHHHHHH'},
+ 'H': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [1.0],
+       'name': 'H',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'H'},
+ 'Mn': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [5.0],
+        'name': 'Mn',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Mn'},
+ 'MnCp(CO)3': {'charges': None,
+               'database': 'TM1R2006',
+               'magmoms': None,
+               'name': 'H5C8MnO3',
+               'positions': array([[ -5.23270000e-02,   8.86000000e-04,  -4.80000000e-05],
+       [  1.75041400e+00,   8.90032000e-01,  -8.02365000e-01],
+       [  1.74465200e+00,   1.03578300e+00,   6.25351000e-01],
+       [  1.73117000e+00,  -2.70325000e-01,   1.19405900e+00],
+       [  1.72839700e+00,  -1.22675500e+00,   1.32675000e-01],
+       [  1.74030300e+00,  -5.00655000e-01,  -1.10190300e+00],
+       [  1.75833800e+00,   1.69831100e+00,  -1.52642300e+00],
+       [  1.75113500e+00,   1.97194400e+00,   1.17377600e+00],
+       [  1.71587100e+00,  -4.99916000e-01,   2.25501400e+00],
+       [  1.72387700e+00,  -2.30606000e+00,   2.41941000e-01],
+       [  1.73502700e+00,  -9.35920000e-01,  -2.09624000e+00],
+       [ -1.05202300e+00,  -9.73104000e-01,  -1.11998200e+00],
+       [ -1.06758900e+00,  -4.72167000e-01,   1.39570900e+00],
+       [ -1.02799600e+00,   1.47198300e+00,  -2.93177000e-01],
+       [ -1.67586400e+00,  -1.61793400e+00,  -1.85760700e+00],
+       [ -1.70361600e+00,  -7.86574000e-01,   2.31524100e+00],
+       [ -1.63381900e+00,   2.44414300e+00,  -4.85783000e-01]]),
+               'symbols': 'MnCCCCCHHHHHCCCOOO'},
+ 'MnO3F': {'charges': None,
+           'database': 'TM1R2006',
+           'magmoms': None,
+           'name': 'MnO3F',
+           'positions': array([[ 0.      ,  0.      ,  0.053348],
+       [ 0.      ,  0.      , -1.666807],
+       [ 0.      ,  1.494372,  0.562137],
+       [-1.294164, -0.747186,  0.562137],
+       [ 1.294164, -0.747186,  0.562137]]),
+           'symbols': 'MnFOOO'},
+ 'N': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [3.0],
+       'name': 'N',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'N'},
+ 'Ni': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [2.0],
+        'name': 'Ni',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Ni'},
+ 'Ni(CO)4': {'charges': None,
+             'database': 'TM1R2006',
+             'magmoms': None,
+             'name': 'NiC4O4',
+             'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 1.054577,  1.054577,  1.054577],
+       [-1.054577, -1.054577,  1.054577],
+       [-1.054577,  1.054577, -1.054577],
+       [ 1.054577, -1.054577, -1.054577],
+       [ 1.717244,  1.717244,  1.717244],
+       [-1.717244, -1.717244,  1.717244],
+       [-1.717244,  1.717244, -1.717244],
+       [ 1.717244, -1.717244, -1.717244]]),
+             'symbols': 'NiCCCCOOOO'},
+ 'Ni(PF3)4': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': None,
+              'name': 'NiF12P4',
+              'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 1.222139,  1.222139,  1.222139],
+       [-1.222139, -1.222139,  1.222139],
+       [-1.222139,  1.222139, -1.222139],
+       [ 1.222139, -1.222139, -1.222139],
+       [ 2.233162,  2.233162,  0.542885],
+       [ 0.542885,  2.233162,  2.233162],
+       [ 2.233162,  0.542885,  2.233162],
+       [-0.542885, -2.233162,  2.233162],
+       [-2.233162, -0.542885,  2.233162],
+       [-2.233162, -2.233162,  0.542885],
+       [-2.233162,  2.233162, -0.542885],
+       [-0.542885,  2.233162, -2.233162],
+       [-2.233162,  0.542885, -2.233162],
+       [ 0.542885, -2.233162, -2.233162],
+       [ 2.233162, -0.542885, -2.233162],
+       [ 2.233162, -2.233162, -0.542885]]),
+              'symbols': 'NiPPPPFFFFFFFFFFFF'},
+ 'Ni(acac)2': {'charges': None,
+               'database': 'TM1R2006',
+               'magmoms': None,
+               'name': 'NiC10O4H14',
+               'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [-1.248785,  1.372676,  0.      ],
+       [ 1.248785, -1.372676,  0.      ],
+       [-1.248785, -1.372676,  0.      ],
+       [ 1.248785,  1.372676,  0.      ],
+       [ 3.198214,  0.      ,  0.      ],
+       [-3.198214,  0.      ,  0.      ],
+       [-2.524578,  1.231009,  0.      ],
+       [ 2.524578, -1.231009,  0.      ],
+       [-2.524578, -1.231009,  0.      ],
+       [ 2.524578,  1.231009,  0.      ],
+       [-3.300189,  2.528058,  0.      ],
+       [ 3.300189, -2.528058,  0.      ],
+       [-3.300189, -2.528058,  0.      ],
+       [ 3.300189,  2.528058,  0.      ],
+       [-4.285791,  0.      ,  0.      ],
+       [ 4.285791,  0.      ,  0.      ],
+       [-3.025745,  3.120807,  0.883699],
+       [ 3.025745, -3.120807,  0.883699],
+       [-3.025745, -3.120807, -0.883699],
+       [ 3.025745,  3.120807, -0.883699],
+       [-3.025745,  3.120807, -0.883699],
+       [ 3.025745, -3.120807, -0.883699],
+       [-3.025745, -3.120807,  0.883699],
+       [ 3.025745,  3.120807,  0.883699],
+       [-4.382325,  2.358532,  0.      ],
+       [ 4.382325, -2.358532,  0.      ],
+       [-4.382325, -2.358532,  0.      ],
+       [ 4.382325,  2.358532,  0.      ]]),
+               'symbols': 'NiOOOOCCCCCCCCCCHHHHHHHHHHHHHH'},
+ 'O': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [2.0],
+       'name': 'O',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'O'},
+ 'P': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [3.0],
+       'name': 'P',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'P'},
+ 'Sc': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [1.0],
+        'name': 'Sc',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Sc'},
+ 'Sc(acac)3': {'charges': None,
+               'database': 'TM1R2006',
+               'magmoms': None,
+               'name': 'ScH21C15O6',
+               'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  3.540777,  0.      ],
+       [ 3.066403, -1.770388,  0.      ],
+       [-3.066403, -1.770388,  0.      ],
+       [ 0.620701,  2.875529,  1.075115],
+       [ 2.179931, -1.975308,  1.075115],
+       [-2.800632, -0.900222,  1.075115],
+       [-0.620701,  2.875529, -1.075115],
+       [-2.179931, -1.975308, -1.075115],
+       [ 2.800632, -0.900222, -1.075115],
+       [ 0.692832,  1.602531,  1.184875],
+       [ 1.041417, -1.401275,  1.184875],
+       [-1.734249, -0.201256,  1.184875],
+       [-0.692832,  1.602531, -1.184875],
+       [-1.041417, -1.401275, -1.184875],
+       [ 1.734249, -0.201256, -1.184875],
+       [ 0.      ,  4.628781,  0.      ],
+       [ 4.008642, -2.314391,  0.      ],
+       [-4.008642, -2.314391,  0.      ],
+       [ 1.255264,  3.675911,  2.189555],
+       [ 2.5558  , -2.925046,  2.189555],
+       [-3.811064, -0.750865,  2.189555],
+       [-1.255264,  3.675911, -2.189555],
+       [-2.5558  , -2.925046, -2.189555],
+       [ 3.811064, -0.750865, -2.189555],
+       [ 1.167445,  4.757208,  2.035793],
+       [ 3.536141, -3.389641,  2.035793],
+       [-4.703586, -1.367567,  2.035793],
+       [-1.167445,  4.757208, -2.035793],
+       [-3.536141, -3.389641, -2.035793],
+       [ 4.703586, -1.367567, -2.035793],
+       [ 0.782067,  3.404276,  3.143979],
+       [ 2.557156, -2.379428,  3.143979],
+       [-3.339224, -1.024848,  3.143979],
+       [-0.782067,  3.404276, -3.143979],
+       [-2.557156, -2.379428, -3.143979],
+       [ 3.339224, -1.024848, -3.143979],
+       [ 2.317429,  3.404876,  2.272149],
+       [ 1.789995, -3.70939 ,  2.272149],
+       [-4.107423,  0.304514,  2.272149],
+       [-2.317429,  3.404876, -2.272149],
+       [-1.789995, -3.70939 , -2.272149],
+       [ 4.107423,  0.304514, -2.272149]]),
+               'symbols': 'ScCCCCCCCCCOOOOOOHHHCCCCCCHHHHHHHHHHHHHHHHHH'},
+ 'Ti': {'charges': None,
+        'database': 'TM1R2006',
+        'magmoms': [2.0],
+        'name': 'Ti',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Ti'},
+ 'Ti(BH4)3': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': array([ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.]),
+              'name': 'H12B3Ti',
+              'positions': array([[  0.00000000e+00,   0.00000000e+00,  -4.10000000e-05],
+       [ -6.08500000e-03,   2.15249500e+00,  -1.05000000e-04],
+       [  1.86715700e+00,  -1.07097800e+00,  -1.05000000e-04],
+       [ -1.86107300e+00,  -1.08151700e+00,  -1.05000000e-04],
+       [  1.15865300e+00,   1.72757300e+00,  -2.00000000e-05],
+       [  9.16796000e-01,  -1.86721000e+00,  -2.00000000e-05],
+       [ -2.07544900e+00,   1.39636000e-01,  -2.00000000e-05],
+       [ -5.39671000e-01,   1.57415900e+00,  -9.93950000e-01],
+       [  1.63309700e+00,  -3.19711000e-01,  -9.93950000e-01],
+       [ -1.09342600e+00,  -1.25444800e+00,  -9.93950000e-01],
+       [ -5.39811000e-01,   1.57429900e+00,   9.93727000e-01],
+       [  1.63328800e+00,  -3.19659000e-01,   9.93727000e-01],
+       [ -1.09347700e+00,  -1.25463900e+00,   9.93727000e-01],
+       [ -1.35742000e-01,   3.33766400e+00,  -3.73000000e-04],
+       [  2.95837300e+00,  -1.55127600e+00,  -3.73000000e-04],
+       [ -2.82263100e+00,  -1.78638800e+00,  -3.73000000e-04]]),
+              'symbols': 'TiBBBHHHHHHHHHHHH'},
+ 'TiCl2Me2': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': None,
+              'name': 'H6C2Cl2Ti',
+              'positions': array([[ 0.      ,  0.      ,  0.209742],
+       [-1.881657,  0.      , -0.945866],
+       [ 1.881657,  0.      , -0.945866],
+       [ 0.      ,  1.639389,  1.457372],
+       [ 0.      , -1.639389,  1.457372],
+       [-0.901274, -1.610233,  2.089371],
+       [ 0.901274, -1.610233,  2.089371],
+       [ 0.      , -2.564277,  0.861137],
+       [ 0.      ,  2.564277,  0.861137],
+       [ 0.901274,  1.610233,  2.089371],
+       [-0.901274,  1.610233,  2.089371]]),
+              'symbols': 'TiClClCCHHHHHH'},
+ 'TiCl3Me': {'charges': None,
+             'database': 'TM1R2006',
+             'magmoms': None,
+             'name': 'H3CCl3Ti',
+             'positions': array([[ 0.      ,  0.      ,  0.116538],
+       [ 0.      ,  2.115907, -0.472447],
+       [-1.832429, -1.057954, -0.472447],
+       [ 1.832429, -1.057954, -0.472447],
+       [ 0.      ,  0.      ,  2.167769],
+       [ 0.902139,  0.52085 ,  2.521547],
+       [ 0.      , -1.0417  ,  2.521547],
+       [-0.902139,  0.52085 ,  2.521547]]),
+             'symbols': 'TiClClClCHHH'},
+ 'TiCl4': {'charges': None,
+           'database': 'TM1R2006',
+           'magmoms': None,
+           'name': 'Cl4Ti',
+           'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 1.262798,  1.262798,  1.262798],
+       [-1.262798, -1.262798,  1.262798],
+       [-1.262798,  1.262798, -1.262798],
+       [ 1.262798, -1.262798, -1.262798]]),
+           'symbols': 'TiClClClCl'},
+ 'V': {'charges': None,
+       'database': 'TM1R2006',
+       'magmoms': [3.0],
+       'name': 'V',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'V'},
+ 'V(NMe2)4': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': array([ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]),
+              'name': 'H24C8N4V',
+              'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [-0.009654,  1.561722,  1.062138],
+       [ 0.009654, -1.561722,  1.062138],
+       [-1.561722, -0.009654, -1.062138],
+       [ 1.561722,  0.009654, -1.062138],
+       [-1.219978,  2.113945,  1.655979],
+       [ 1.219978, -2.113945,  1.655979],
+       [-2.113945, -1.219978, -1.655979],
+       [ 2.113945,  1.219978, -1.655979],
+       [ 1.188278,  2.080402,  1.709075],
+       [-1.188278, -2.080402,  1.709075],
+       [-2.080402,  1.188278, -1.709075],
+       [ 2.080402, -1.188278, -1.709075],
+       [-2.112996,  1.691813,  1.177356],
+       [ 2.112996, -1.691813,  1.177356],
+       [-1.691813, -2.112996, -1.177356],
+       [ 1.691813,  2.112996, -1.177356],
+       [ 2.089441,  1.627028,  1.275983],
+       [-2.089441, -1.627028,  1.275983],
+       [-1.627028,  2.089441, -1.275983],
+       [ 1.627028, -2.089441, -1.275983],
+       [-1.254235,  3.214541,  1.536136],
+       [ 1.254235, -3.214541,  1.536136],
+       [-1.905317, -1.281383, -2.742242],
+       [ 1.905317,  1.281383, -2.742242],
+       [ 1.192045,  1.877111,  2.798018],
+       [-1.192045, -1.877111,  2.798018],
+       [-3.178469,  1.263762, -1.584921],
+       [ 3.178469, -1.263762, -1.584921],
+       [-1.281383,  1.905317,  2.742242],
+       [ 1.281383, -1.905317,  2.742242],
+       [-3.214541, -1.254235, -1.536136],
+       [ 3.214541,  1.254235, -1.536136],
+       [ 1.263762,  3.178469,  1.584921],
+       [-1.263762, -3.178469,  1.584921],
+       [-1.877111,  1.192045, -2.798018],
+       [ 1.877111, -1.192045, -2.798018]]),
+              'symbols': 'VNNNNCCCCCCCCHHHHHHHHHHHHHHHHHHHHHHHH'},
+ 'VCp(CO)4': {'charges': None,
+              'database': 'TM1R2006',
+              'magmoms': None,
+              'name': 'H5C9O4V',
+              'positions': array([[ 0.003658,  0.04101 ,  0.      ],
+       [-0.988688,  2.128308,  0.      ],
+       [-0.161173,  2.021062,  1.151741],
+       [ 1.184667,  1.847267,  0.714111],
+       [ 1.184667,  1.847267, -0.714111],
+       [-0.161173,  2.021062, -1.151741],
+       [-2.065227,  2.26891 ,  0.      ],
+       [-0.495968,  2.070335,  2.183207],
+       [ 2.056257,  1.749228,  1.353427],
+       [ 2.056257,  1.749228, -1.353427],
+       [-0.495968,  2.070335, -2.183207],
+       [-1.765418, -0.746026,  0.      ],
+       [-0.098849, -0.926375, -1.671988],
+       [-0.098849, -0.926375,  1.671988],
+       [ 1.560317, -1.104426,  0.      ],
+       [-2.828096, -1.211475,  0.      ],
+       [-0.156132, -1.498615, -2.679799],
+       [-0.156132, -1.498615,  2.679799],
+       [ 2.502981, -1.781344,  0.      ]]),
+              'symbols': 'VCCCCCHHHHHCCCCOOOO'},
+ 'VF5': {'charges': None,
+         'database': 'TM1R2006',
+         'magmoms': None,
+         'name': 'F5V',
+         'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  0.      ,  1.764166],
+       [ 0.      ,  1.726781,  0.      ],
+       [-1.495436, -0.86339 ,  0.      ],
+       [ 1.495436, -0.86339 ,  0.      ],
+       [ 0.      ,  0.      , -1.764166]]),
+         'symbols': 'VFFFFF'},
+ 'VOCl3': {'charges': None,
+           'database': 'TM1R2006',
+           'magmoms': None,
+           'name': 'Cl3OV',
+           'positions': array([[ 0.      ,  0.      ,  0.263274],
+       [ 0.      ,  0.      ,  1.837702],
+       [ 0.      ,  2.044546, -0.40922 ],
+       [ 1.770629, -1.022273, -0.40922 ],
+       [-1.770629, -1.022273, -0.40922 ]]),
+           'symbols': 'VOClClCl'},
+ 'VOF3': {'charges': None,
+          'database': 'TM1R2006',
+          'magmoms': None,
+          'name': 'F3OV',
+          'positions': array([[ 0.      ,  0.      ,  0.037484],
+       [ 0.      ,  0.      ,  1.614068],
+       [ 0.      ,  1.647059, -0.512097],
+       [ 1.426395, -0.823529, -0.512097],
+       [-1.426395, -0.823529, -0.512097]]),
+          'symbols': 'VOFFF'}}
diff --git a/ase/data/tmxr200x_tm2r2007.py b/ase/data/tmxr200x_tm2r2007.py
new file mode 100644
index 0000000..fa8ff99
--- /dev/null
+++ b/ase/data/tmxr200x_tm2r2007.py
@@ -0,0 +1,431 @@
+# Computer generated code! Hands off!
+# Generated: 2011-08-30
+from numpy import array
+data = {'B': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [1.0],
+       'name': 'B',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'B'},
+ 'C': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [2.0],
+       'name': 'C',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'C'},
+ 'Cd': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [0.0],
+        'name': 'Cd',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cd'},
+ 'CdMe2_D3': {'charges': None,
+              'database': 'TM2R2007',
+              'magmoms': None,
+              'name': 'H6C2Cd',
+              'positions': array([[ 0.      ,  0.      ,  2.127012],
+       [-0.396568,  0.954659,  2.516168],
+       [-0.628475, -0.820767,  2.516168],
+       [ 1.025043, -0.133892,  2.516168],
+       [ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  0.      , -2.127012],
+       [ 0.396568,  0.954659, -2.516168],
+       [-1.025043, -0.133892, -2.516168],
+       [ 0.628475, -0.820767, -2.516168]]),
+              'symbols': 'CHHHCdCHHH'},
+ 'CdMe_C3v': {'charges': None,
+              'database': 'TM2R2007',
+              'magmoms': array([ 0.,  0.,  0.,  0.,  1.]),
+              'name': 'H3CCd',
+              'positions': array([[ 0.      ,  0.      , -1.893946],
+       [ 0.      ,  1.05344 , -2.212298],
+       [ 0.912306, -0.52672 , -2.212298],
+       [-0.912306, -0.52672 , -2.212298],
+       [ 0.      ,  0.      ,  0.375012]]),
+              'symbols': 'CHHHCd'},
+ 'Cl': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [1.0],
+        'name': 'Cl',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cl'},
+ 'F': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [1.0],
+       'name': 'F',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'F'},
+ 'H': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [1.0],
+       'name': 'H',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'H'},
+ 'Mo': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [6.0],
+        'name': 'Mo',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Mo'},
+ 'Mo(CO)6_Oh': {'charges': None,
+                'database': 'TM2R2007',
+                'magmoms': None,
+                'name': 'MoC6O6',
+                'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  0.      ,  2.061499],
+       [ 0.      ,  2.061499,  0.      ],
+       [ 2.061499,  0.      ,  0.      ],
+       [ 0.      , -2.061499,  0.      ],
+       [-2.061499,  0.      ,  0.      ],
+       [ 0.      ,  0.      , -2.061499],
+       [ 0.      ,  0.      ,  3.225318],
+       [ 0.      ,  3.225318,  0.      ],
+       [ 3.225318,  0.      ,  0.      ],
+       [ 0.      , -3.225318,  0.      ],
+       [-3.225318,  0.      ,  0.      ],
+       [ 0.      ,  0.      , -3.225318]]),
+                'symbols': 'MoCCCCCCOOOOOO'},
+ 'Mo2(OAc)4_C4': {'charges': None,
+                  'database': 'TM2R2007',
+                  'magmoms': None,
+                  'name': 'H12Mo2C8O8',
+                  'positions': array([[  0.00000000e+00,   0.00000000e+00,  -1.06938500e+00],
+       [ -2.73808600e+00,   5.22400000e-03,   0.00000000e+00],
+       [ -5.22400000e-03,  -2.73808600e+00,   0.00000000e+00],
+       [  2.73808600e+00,  -5.22400000e-03,   0.00000000e+00],
+       [  5.22400000e-03,   2.73808600e+00,   0.00000000e+00],
+       [  0.00000000e+00,   0.00000000e+00,   1.06938600e+00],
+       [ -4.24697900e+00,   5.34640000e-02,   0.00000000e+00],
+       [ -5.34640000e-02,  -4.24697900e+00,   0.00000000e+00],
+       [  4.24697900e+00,  -5.34640000e-02,   0.00000000e+00],
+       [  5.34640000e-02,   4.24697900e+00,   0.00000000e+00],
+       [ -2.10910100e+00,   0.00000000e+00,  -1.12919500e+00],
+       [  0.00000000e+00,   2.10910100e+00,  -1.12919500e+00],
+       [  2.10910100e+00,   0.00000000e+00,  -1.12919500e+00],
+       [  0.00000000e+00,  -2.10910100e+00,  -1.12919500e+00],
+       [ -2.10910100e+00,   0.00000000e+00,   1.12919500e+00],
+       [  0.00000000e+00,  -2.10910100e+00,   1.12919500e+00],
+       [  2.10910100e+00,   0.00000000e+00,   1.12919500e+00],
+       [  0.00000000e+00,   2.10910100e+00,   1.12919500e+00],
+       [ -4.64728200e+00,  -4.30214000e-01,  -9.04380000e-01],
+       [ -4.64728200e+00,  -4.30197000e-01,   9.04389000e-01],
+       [ -4.58277600e+00,   1.10719800e+00,  -1.10000000e-05],
+       [  4.30214000e-01,  -4.64728200e+00,  -9.04380000e-01],
+       [  4.30197000e-01,  -4.64728200e+00,   9.04389000e-01],
+       [ -1.10719800e+00,  -4.58277600e+00,  -1.10000000e-05],
+       [  4.64728200e+00,   4.30214000e-01,  -9.04380000e-01],
+       [  4.64728200e+00,   4.30197000e-01,   9.04389000e-01],
+       [  4.58277600e+00,  -1.10719800e+00,  -1.10000000e-05],
+       [ -4.30214000e-01,   4.64728200e+00,  -9.04380000e-01],
+       [ -4.30197000e-01,   4.64728200e+00,   9.04389000e-01],
+       [  1.10719800e+00,   4.58277600e+00,  -1.10000000e-05]]),
+                  'symbols': 'MoCCCCMoCCCCOOOOOOOOHHHHHHHHHHHH'},
+ 'MoF6_Oh': {'charges': None,
+             'database': 'TM2R2007',
+             'magmoms': None,
+             'name': 'MoF6',
+             'positions': array([[ 0.     ,  0.     ,  0.     ],
+       [ 0.     ,  0.     ,  1.86242],
+       [ 0.     ,  1.86242,  0.     ],
+       [ 1.86242,  0.     ,  0.     ],
+       [ 0.     , -1.86242,  0.     ],
+       [-1.86242,  0.     ,  0.     ],
+       [ 0.     ,  0.     , -1.86242]]),
+             'symbols': 'MoFFFFFF'},
+ 'MoO2Cl2_C2v': {'charges': None,
+                 'database': 'TM2R2007',
+                 'magmoms': None,
+                 'name': 'MoO2Cl2',
+                 'positions': array([[ -5.30000000e-05,   2.89511000e-01,  -8.90000000e-05],
+       [ -6.79000000e-04,   1.30748200e+00,   1.37187100e+00],
+       [ -6.60000000e-05,   1.31175100e+00,  -1.36905300e+00],
+       [ -1.89022600e+00,  -9.74177000e-01,  -6.59000000e-04],
+       [  1.89070800e+00,  -9.73666000e-01,  -4.49000000e-04]]),
+                 'symbols': 'MoOOClCl'},
+ 'MoOCl4_C4v': {'charges': None,
+                'database': 'TM2R2007',
+                'magmoms': None,
+                'name': 'MoOCl4',
+                'positions': array([[ 0.      ,  0.      ,  0.201479],
+       [ 0.      ,  2.257343, -0.346653],
+       [ 2.257343,  0.      , -0.346653],
+       [ 0.      , -2.257343, -0.346653],
+       [-2.257343,  0.      , -0.346653],
+       [ 0.      ,  0.      ,  1.888784]]),
+                'symbols': 'MoClClClClO'},
+ 'MoOF4_C4v': {'charges': None,
+               'database': 'TM2R2007',
+               'magmoms': None,
+               'name': 'MoOF4',
+               'positions': array([[ 0.      ,  0.      ,  0.051628],
+       [ 0.      ,  1.801608, -0.446746],
+       [ 1.801608,  0.      , -0.446746],
+       [ 0.      , -1.801608, -0.446746],
+       [-1.801608,  0.      , -0.446746],
+       [ 0.      ,  0.      ,  1.739313]]),
+               'symbols': 'MoFFFFO'},
+ 'N': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [3.0],
+       'name': 'N',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'N'},
+ 'Nb': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [5.0],
+        'name': 'Nb',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Nb'},
+ 'Nb(Cp)(C7H7)_Cs': {'charges': None,
+                     'database': 'TM2R2007',
+                     'magmoms': array([ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
+        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]),
+                     'name': 'H12NbC12',
+                     'positions': array([[ -7.61000000e-04,   4.97160000e-02,   0.00000000e+00],
+       [ -1.21916500e+00,   2.16211100e+00,   0.00000000e+00],
+       [ -3.76513000e-01,   2.15983200e+00,   1.15998800e+00],
+       [  9.87289000e-01,   2.15732800e+00,   7.16911000e-01],
+       [  9.87289000e-01,   2.15732800e+00,  -7.16911000e-01],
+       [ -3.76513000e-01,   2.15983200e+00,  -1.15998800e+00],
+       [ -2.31099500e+00,   2.17709700e+00,   0.00000000e+00],
+       [ -7.14132000e-01,   2.17169600e+00,   2.19826200e+00],
+       [  1.87022000e+00,   2.16881900e+00,   1.35918500e+00],
+       [  1.87022000e+00,   2.16881900e+00,  -1.35918500e+00],
+       [ -7.14132000e-01,   2.17169600e+00,  -2.19826200e+00],
+       [ -1.65137500e+00,  -1.60347200e+00,   0.00000000e+00],
+       [ -1.02908300e+00,  -1.60486200e+00,  -1.29113300e+00],
+       [ -1.02908300e+00,  -1.60486200e+00,   1.29113300e+00],
+       [  3.67735000e-01,  -1.60588600e+00,  -1.60975700e+00],
+       [  3.67735000e-01,  -1.60588600e+00,   1.60975700e+00],
+       [  1.48818100e+00,  -1.60167500e+00,  -7.16533000e-01],
+       [  1.48818100e+00,  -1.60167500e+00,   7.16533000e-01],
+       [ -2.74042700e+00,  -1.47333400e+00,   0.00000000e+00],
+       [ -1.70776800e+00,  -1.47447400e+00,  -2.14284200e+00],
+       [ -1.70776800e+00,  -1.47447400e+00,   2.14284200e+00],
+       [  6.09993000e-01,  -1.47178300e+00,  -2.67114300e+00],
+       [  6.09993000e-01,  -1.47178300e+00,   2.67114300e+00],
+       [  2.46895600e+00,  -1.46965700e+00,  -1.18939800e+00],
+       [  2.46895600e+00,  -1.46965700e+00,   1.18939800e+00]]),
+                     'symbols': 'NbCCCCCHHHHHCCCCCCCHHHHHHH'},
+ 'NbCl3Me2_C2v': {'charges': None,
+                  'database': 'TM2R2007',
+                  'magmoms': None,
+                  'name': 'H6NbC2Cl3',
+                  'positions': array([[ 0.      ,  0.      ,  0.070449],
+       [-2.327624,  0.      ,  0.377464],
+       [ 2.327624,  0.      ,  0.377464],
+       [ 0.      ,  0.      , -2.237446],
+       [ 0.      ,  1.873567,  1.168362],
+       [ 0.      ,  1.599605,  2.244212],
+       [-0.904905,  2.465311,  0.951411],
+       [ 0.904905,  2.465311,  0.951411],
+       [ 0.      , -1.873567,  1.168362],
+       [ 0.      , -1.599605,  2.244212],
+       [ 0.904905, -2.465311,  0.951411],
+       [-0.904905, -2.465311,  0.951411]]),
+                  'symbols': 'NbClClClCHHHCHHH'},
+ 'NbCl5_D3h': {'charges': None,
+               'database': 'TM2R2007',
+               'magmoms': None,
+               'name': 'NbCl5',
+               'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  0.      ,  2.3581  ],
+       [ 0.      ,  2.302976,  0.      ],
+       [ 1.994435, -1.151488,  0.      ],
+       [-1.994435, -1.151488,  0.      ],
+       [ 0.      ,  0.      , -2.3581  ]]),
+               'symbols': 'NbClClClClCl'},
+ 'O': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [2.0],
+       'name': 'O',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'O'},
+ 'P': {'charges': None,
+       'database': 'TM2R2007',
+       'magmoms': [3.0],
+       'name': 'P',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'P'},
+ 'Rh': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [3.0],
+        'name': 'Rh',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Rh'},
+ 'Rh(NO)(PF3)3_C3': {'charges': None,
+                     'database': 'TM2R2007',
+                     'magmoms': None,
+                     'name': 'P3F9RhON',
+                     'positions': array([[ 0.      ,  0.      ,  0.570603],
+       [ 0.      ,  0.      ,  2.39318 ],
+       [ 0.      ,  0.      ,  3.567225],
+       [ 0.      ,  2.037087, -0.382037],
+       [ 0.296998,  3.259576,  0.615924],
+       [ 1.034182,  2.430035, -1.545391],
+       [-1.332043,  2.620518, -1.062219],
+       [ 1.764169, -1.018543, -0.382037],
+       [ 2.674377, -1.886996,  0.615924],
+       [ 1.587381, -2.110645, -1.545391],
+       [ 2.935457, -0.156676, -1.062219],
+       [-1.764169, -1.018543, -0.382037],
+       [-2.971375, -1.37258 ,  0.615924],
+       [-2.621563, -0.31939 , -1.545391],
+       [-1.603414, -2.463843, -1.062219]]),
+                     'symbols': 'RhNOPFFFPFFFPFFF'},
+ 'RhCp(C2H4)2_Cs': {'charges': None,
+                    'database': 'TM2R2007',
+                    'magmoms': None,
+                    'name': 'H13C9Rh',
+                    'positions': array([[ 0.002608,  0.208562,  0.      ],
+       [ 1.250238, -1.701265,  0.      ],
+       [ 0.392552, -1.692719,  1.153616],
+       [-0.984381, -1.780687,  0.70967 ],
+       [-0.984381, -1.780687, -0.70967 ],
+       [ 0.392552, -1.692719, -1.153616],
+       [ 2.340492, -1.697608,  0.      ],
+       [ 0.721247, -1.689625,  2.195084],
+       [-1.863227, -1.812641,  1.355483],
+       [-1.863227, -1.812641, -1.355483],
+       [ 0.721247, -1.689625, -2.195084],
+       [-0.732584,  1.553011,  1.485399],
+       [ 0.697572,  1.569767,  1.48282 ],
+       [ 0.697572,  1.569767, -1.48282 ],
+       [-0.732584,  1.553011, -1.485399],
+       [-1.274769,  0.997535,  2.262296],
+       [-1.292117,  2.402834,  1.075787],
+       [ 1.2334  ,  2.432639,  1.068455],
+       [ 1.256866,  1.03298 ,  2.260459],
+       [ 1.256866,  1.03298 , -2.260459],
+       [ 1.2334  ,  2.432639, -1.068455],
+       [-1.292117,  2.402834, -1.075787],
+       [-1.274769,  0.997535, -2.262296]]),
+                    'symbols': 'RhCCCCCHHHHHCCCCHHHHHHHH'},
+ 'Ru': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [4.0],
+        'name': 'Ru',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Ru'},
+ 'Ru(CO)5_D3h': {'charges': None,
+                 'database': 'TM2R2007',
+                 'magmoms': None,
+                 'name': 'RuC5O5',
+                 'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  1.94982 ,  0.      ],
+       [ 1.688593, -0.97491 ,  0.      ],
+       [-1.688593, -0.97491 ,  0.      ],
+       [ 0.      ,  0.      ,  1.95205 ],
+       [ 0.      ,  0.      , -1.95205 ],
+       [ 0.      ,  3.115623,  0.      ],
+       [ 2.698209, -1.557811,  0.      ],
+       [-2.698209, -1.557811,  0.      ],
+       [ 0.      ,  0.      ,  3.113168],
+       [ 0.      ,  0.      , -3.113168]]),
+                 'symbols': 'RuCCCCCOOOOO'},
+ 'RuCp2_D5h': {'charges': None,
+               'database': 'TM2R2007',
+               'magmoms': None,
+               'name': 'RuC10H10',
+               'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.      ,  1.22817 ,  1.826008],
+       [-1.168059,  0.379525,  1.826008],
+       [-0.7219  , -0.993611,  1.826008],
+       [ 0.7219  , -0.993611,  1.826008],
+       [ 1.168059,  0.379525,  1.826008],
+       [ 0.      ,  2.31942 ,  1.830821],
+       [-2.2059  ,  0.71674 ,  1.830821],
+       [-1.363321, -1.87645 ,  1.830821],
+       [ 1.363321, -1.87645 ,  1.830821],
+       [ 2.2059  ,  0.71674 ,  1.830821],
+       [ 0.      ,  1.22817 , -1.826008],
+       [ 1.168059,  0.379525, -1.826008],
+       [ 0.7219  , -0.993611, -1.826008],
+       [-0.7219  , -0.993611, -1.826008],
+       [-1.168059,  0.379525, -1.826008],
+       [ 0.      ,  2.31942 , -1.830821],
+       [ 2.2059  ,  0.71674 , -1.830821],
+       [ 1.363321, -1.87645 , -1.830821],
+       [-1.363321, -1.87645 , -1.830821],
+       [-2.2059  ,  0.71674 , -1.830821]]),
+               'symbols': 'RuCCCCCHHHHHCCCCCHHHHH'},
+ 'RuO4_Td': {'charges': None,
+             'database': 'TM2R2007',
+             'magmoms': None,
+             'name': 'RuO4',
+             'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.990035,  0.990035,  0.990035],
+       [-0.990035, -0.990035,  0.990035],
+       [-0.990035,  0.990035, -0.990035],
+       [ 0.990035, -0.990035, -0.990035]]),
+             'symbols': 'RuOOOO'},
+ 'Zr': {'charges': None,
+        'database': 'TM2R2007',
+        'magmoms': [2.0],
+        'name': 'Zr',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Zr'},
+ 'Zr(BH4)4_T': {'charges': None,
+                'database': 'TM2R2007',
+                'magmoms': None,
+                'name': 'H16B4Zr',
+                'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [-1.340193,  1.340193,  1.340193],
+       [-0.139639,  1.316088,  1.709335],
+       [-1.316088,  1.709335,  0.139639],
+       [-1.709335,  0.139639,  1.316088],
+       [-2.032757,  2.032757,  2.032757],
+       [ 1.340193, -1.340193,  1.340193],
+       [ 0.139639, -1.316088,  1.709335],
+       [ 1.316088, -1.709335,  0.139639],
+       [ 1.709335, -0.139639,  1.316088],
+       [ 2.032757, -2.032757,  2.032757],
+       [ 1.340193,  1.340193, -1.340193],
+       [ 0.139639,  1.316088, -1.709335],
+       [ 1.316088,  1.709335, -0.139639],
+       [ 1.709335,  0.139639, -1.316088],
+       [ 2.032757,  2.032757, -2.032757],
+       [-1.340193, -1.340193, -1.340193],
+       [-0.139639, -1.316088, -1.709335],
+       [-1.316088, -1.709335, -0.139639],
+       [-1.709335, -0.139639, -1.316088],
+       [-2.032757, -2.032757, -2.032757]]),
+                'symbols': 'ZrBHHHHBHHHHBHHHHBHHHH'},
+ 'ZrCl4_Td': {'charges': None,
+              'database': 'TM2R2007',
+              'magmoms': None,
+              'name': 'ZrCl4',
+              'positions': array([[ 0.      ,  0.      ,  0.      ],
+       [ 1.354596,  1.354596,  1.354596],
+       [-1.354596, -1.354596,  1.354596],
+       [-1.354596,  1.354596, -1.354596],
+       [ 1.354596, -1.354596, -1.354596]]),
+              'symbols': 'ZrClClClCl'},
+ 'ZrCp2Cl2_C2': {'charges': None,
+                 'database': 'TM2R2007',
+                 'magmoms': None,
+                 'name': 'H10C10ZrCl2',
+                 'positions': array([[ 0.      ,  0.      ,  0.091105],
+       [-1.859861,  0.23641 ,  1.670541],
+       [ 1.859861, -0.23641 ,  1.670541],
+       [ 0.      ,  2.571399,  0.194369],
+       [ 0.      , -2.571399,  0.194369],
+       [-0.285737,  1.583932, -1.875009],
+       [ 0.285737, -1.583932, -1.875009],
+       [ 1.114301,  1.566551, -1.560615],
+       [-1.114301, -1.566551, -1.560615],
+       [-0.971753,  2.197102, -0.788523],
+       [ 0.971753, -2.197102, -0.788523],
+       [ 1.284778,  2.202141, -0.293068],
+       [-1.284778, -2.202141, -0.293068],
+       [-0.21432 ,  3.026945,  1.162064],
+       [ 0.21432 , -3.026945,  1.162064],
+       [-0.743028,  1.199455, -2.788105],
+       [ 0.743028, -1.199455, -2.788105],
+       [ 1.913243,  1.17553 , -2.193712],
+       [-1.913243, -1.17553 , -2.193712],
+       [-2.051026,  2.330013, -0.700882],
+       [ 2.051026, -2.330013, -0.700882],
+       [ 2.229437,  2.326098,  0.236424],
+       [-2.229437, -2.326098,  0.236424]]),
+                 'symbols': 'ZrClClCCCCCCCCCCHHHHHHHHHH'}}
diff --git a/ase/data/tmxr200x_tm3r2008.py b/ase/data/tmxr200x_tm3r2008.py
new file mode 100644
index 0000000..f06b095
--- /dev/null
+++ b/ase/data/tmxr200x_tm3r2008.py
@@ -0,0 +1,503 @@
+# Computer generated code! Hands off!
+# Generated: 2011-08-30
+from numpy import array
+data = {'Au': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [1.0],
+        'name': 'Au',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Au'},
+ 'Au(CO)Cl': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'CAuOCl',
+              'positions': array([[ 0.        ,  0.        ,  0.02693626],
+       [ 0.        ,  0.        ,  1.91001845],
+       [ 0.        ,  0.        ,  3.03658711],
+       [ 0.        ,  0.        , -2.19566425]]),
+              'symbols': 'AuCOCl'},
+ 'Au(Me)PMe3': {'charges': None,
+                'database': 'TM3R2008',
+                'magmoms': None,
+                'name': 'PC4AuH12',
+                'positions': array([[ 0.     ,  0.     ,  0.     ],
+       [ 2.06086,  0.     ,  0.     ],
+       [-2.29658,  0.     , -0.     ],
+       [ 2.45325,  0.     , -1.02015],
+       [ 2.45325,  0.88348,  0.51008],
+       [ 2.45325, -0.88348,  0.51008],
+       [-3.07743, -0.43118,  1.58726],
+       [-3.07743,  1.5902 , -0.42022],
+       [-3.07743, -1.15902, -1.16704],
+       [-4.16653, -0.41194,  1.5165 ],
+       [-4.16653,  1.51929, -0.4015 ],
+       [-4.16653, -1.10735, -1.115  ],
+       [-2.75322, -1.42683,  1.88959],
+       [-2.75322,  2.34985,  0.29087],
+       [-2.75322, -0.92302, -2.18046],
+       [-2.75319,  0.27474,  2.35171],
+       [-2.75319,  1.89928, -1.41378],
+       [-2.75319, -2.17401, -0.93793]]),
+                'symbols': 'AuCPHHHCCCHHHHHHHHH'},
+ 'B': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [1.0],
+       'name': 'B',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'B'},
+ 'C': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [2.0],
+       'name': 'C',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'C'},
+ 'Cl': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [1.0],
+        'name': 'Cl',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Cl'},
+ 'F': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [1.0],
+       'name': 'F',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'F'},
+ 'F4Re-ReF4': {'charges': None,
+               'database': 'TM3R2008',
+               'magmoms': None,
+               'name': 'Re2F8',
+               'positions': array([[ 0.        ,  0.        ,  1.11278439],
+       [ 0.        ,  0.        , -1.11278439],
+       [-0.14359931,  1.80883587,  1.43739094],
+       [-1.80883587, -0.14359931,  1.43739094],
+       [ 0.14359931, -1.80883587,  1.43739094],
+       [ 1.80883587,  0.14359931,  1.43739094],
+       [ 0.14359931,  1.80883587, -1.43739094],
+       [ 1.80883587, -0.14359931, -1.43739094],
+       [-0.14359931, -1.80883587, -1.43739094],
+       [-1.80883587,  0.14359931, -1.43739094]]),
+               'symbols': 'ReReFFFFFFFF'},
+ 'H': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [1.0],
+       'name': 'H',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'H'},
+ 'Hf': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [2.0],
+        'name': 'Hf',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Hf'},
+ 'Hf(BH4)4': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'H16B4Hf',
+              'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [-1.32690136,  1.32690136,  1.32690136],
+       [-0.13270994,  1.30489363,  1.68117993],
+       [-1.30489363,  1.68117993,  0.13270994],
+       [-1.68117993,  0.13270994,  1.30489363],
+       [-2.01165285,  2.01165285,  2.01165285],
+       [ 1.32690136, -1.32690136,  1.32690136],
+       [ 0.13270994, -1.30489363,  1.68117993],
+       [ 1.30489363, -1.68117993,  0.13270994],
+       [ 1.68117993, -0.13270994,  1.30489363],
+       [ 2.01165285, -2.01165285,  2.01165285],
+       [ 1.32690136,  1.32690136, -1.32690136],
+       [ 0.13270994,  1.30489363, -1.68117993],
+       [ 1.30489363,  1.68117993, -0.13270994],
+       [ 1.68117993,  0.13270994, -1.30489363],
+       [ 2.01165285,  2.01165285, -2.01165285],
+       [-1.32690136, -1.32690136, -1.32690136],
+       [-0.13270994, -1.30489363, -1.68117993],
+       [-1.30489363, -1.68117993, -0.13270994],
+       [-1.68117993, -0.13270994, -1.30489363],
+       [-2.01165285, -2.01165285, -2.01165285]]),
+              'symbols': 'HfBHHHHBHHHHBHHHHBHHHH'},
+ 'HfCl4': {'charges': None,
+           'database': 'TM3R2008',
+           'magmoms': None,
+           'name': 'HfCl4',
+           'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [ 1.33936969,  1.33936969,  1.33936969],
+       [-1.33936969, -1.33936969,  1.33936969],
+       [-1.33936969,  1.33936969, -1.33936969],
+       [ 1.33936969, -1.33936969, -1.33936969]]),
+           'symbols': 'HfClClClCl'},
+ 'Hg': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [0.0],
+        'name': 'Hg',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Hg'},
+ 'Hg(CF3)2': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'C2HgF6',
+              'positions': array([[  0.00000000e+00,   3.00000000e-10,   0.00000000e+00],
+       [  0.00000000e+00,   3.00000000e-10,   2.12403758e+00],
+       [ -2.64417504e-01,   1.21640215e+00,   2.62889481e+00],
+       [ -9.21226414e-01,  -8.37193352e-01,   2.62889481e+00],
+       [  1.18564392e+00,  -3.79208800e-01,   2.62889481e+00],
+       [  0.00000000e+00,   3.00000000e-10,  -2.12403758e+00],
+       [  9.21226415e-01,  -8.37193352e-01,  -2.62889481e+00],
+       [  2.64417503e-01,   1.21640215e+00,  -2.62889481e+00],
+       [ -1.18564392e+00,  -3.79208802e-01,  -2.62889481e+00]]),
+              'symbols': 'HgCFFFCFFF'},
+ 'Ir': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [3.0],
+        'name': 'Ir',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Ir'},
+ 'IrF6': {'charges': None,
+          'database': 'TM3R2008',
+          'magmoms': array([ 3.,  0.,  0.,  0.,  0.,  0.,  0.]),
+          'name': 'IrF6',
+          'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [ 0.        ,  0.        ,  1.83277076],
+       [ 0.        ,  0.        , -1.83277076],
+       [ 0.        ,  1.83277076,  0.        ],
+       [ 0.        , -1.83277076,  0.        ],
+       [ 1.83277076,  0.        ,  0.        ],
+       [-1.83277076,  0.        ,  0.        ]]),
+          'symbols': 'IrFFFFFF'},
+ 'MeHg(CN)': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'H3C2HgN',
+              'positions': array([[  0.00000000e+00,   0.00000000e+00,   1.54247531e-01],
+       [  0.00000000e+00,   0.00000000e+00,   2.21583263e+00],
+       [ -2.00000000e-10,   1.02349865e+00,   2.58822736e+00],
+       [ -8.86375827e-01,  -5.11749323e-01,   2.58822736e+00],
+       [  8.86375827e-01,  -5.11749322e-01,   2.58822736e+00],
+       [  0.00000000e+00,   0.00000000e+00,  -1.89056634e+00],
+       [  0.00000000e+00,   0.00000000e+00,  -3.04316792e+00]]),
+              'symbols': 'HgCHHHCN'},
+ 'MeHg(Cl)': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'H3CHgCl',
+              'positions': array([[  0.00000000e+00,   0.00000000e+00,   1.97573226e-01],
+       [  0.00000000e+00,   0.00000000e+00,   2.26177241e+00],
+       [ -4.00000000e-10,   1.02674631e+00,   2.62218302e+00],
+       [ -8.89188388e-01,  -5.13373156e-01,   2.62218302e+00],
+       [  8.89188389e-01,  -5.13373155e-01,   2.62218302e+00],
+       [  0.00000000e+00,   0.00000000e+00,  -2.10026991e+00]]),
+              'symbols': 'HgCHHHCl'},
+ 'N': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [3.0],
+       'name': 'N',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'N'},
+ 'O': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [2.0],
+       'name': 'O',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'O'},
+ 'Os': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [4.0],
+        'name': 'Os',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Os'},
+ 'Os(CO)4(C2H4)': {'charges': None,
+                   'database': 'TM3R2008',
+                   'magmoms': None,
+                   'name': 'H4C6OsO4',
+                   'positions': array([[ 0.        ,  0.        ,  0.03274136],
+       [ 0.        ,  0.71587047,  2.13161279],
+       [ 0.        , -0.71587047,  2.13161279],
+       [ 1.94852607,  0.        ,  0.16179664],
+       [-1.94852607,  0.        ,  0.16179664],
+       [ 0.        ,  1.54176948, -1.14066325],
+       [ 0.        , -1.54176948, -1.14066325],
+       [ 3.07625455,  0.        ,  0.28637417],
+       [-3.07625455,  0.        ,  0.28637417],
+       [ 0.        ,  2.45341344, -1.82437728],
+       [ 0.        , -2.45341344, -1.82437728],
+       [-0.90326741,  1.24510061,  2.40864957],
+       [ 0.90326741,  1.24510061,  2.40864957],
+       [ 0.90326741, -1.24510061,  2.40864957],
+       [-0.90326741, -1.24510061,  2.40864957]]),
+                   'symbols': 'OsCCCCCCOOOOHHHH'},
+ 'Os(CO)5': {'charges': None,
+             'database': 'TM3R2008',
+             'magmoms': None,
+             'name': 'C5OsO5',
+             'positions': array([[  0.00000000e+00,   2.00000000e-10,   0.00000000e+00],
+       [ -2.20000000e-09,   1.94754636e+00,   0.00000000e+00],
+       [  1.68662462e+00,  -9.73773176e-01,   0.00000000e+00],
+       [ -1.68662462e+00,  -9.73773180e-01,   0.00000000e+00],
+       [  0.00000000e+00,   2.00000000e-10,   1.96213847e+00],
+       [  0.00000000e+00,   2.00000000e-10,  -1.96213847e+00],
+       [ -3.40000000e-09,   3.08702941e+00,   0.00000000e+00],
+       [  2.67344590e+00,  -1.54351470e+00,   0.00000000e+00],
+       [ -2.67344589e+00,  -1.54351471e+00,   0.00000000e+00],
+       [  0.00000000e+00,   2.00000000e-10,   3.09536014e+00],
+       [  0.00000000e+00,   2.00000000e-10,  -3.09536014e+00]]),
+             'symbols': 'OsCCCCCOOOOO'},
+ 'OsO4': {'charges': None,
+          'database': 'TM3R2008',
+          'magmoms': None,
+          'name': 'OsO4',
+          'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [ 0.97412275,  0.97412275,  0.97412275],
+       [-0.97412275, -0.97412275,  0.97412275],
+       [-0.97412275,  0.97412275, -0.97412275],
+       [ 0.97412275, -0.97412275, -0.97412275]]),
+          'symbols': 'OsOOOO'},
+ 'OsOCl4': {'charges': None,
+            'database': 'TM3R2008',
+            'magmoms': None,
+            'name': 'OsOCl4',
+            'positions': array([[ 0.        ,  0.        ,  0.24014979],
+       [ 0.        ,  0.        ,  1.87351269],
+       [ 0.        ,  2.1415415 , -0.47039842],
+       [ 2.1415415 ,  0.        , -0.47039842],
+       [ 0.        , -2.1415415 , -0.47039842],
+       [-2.1415415 ,  0.        , -0.47039842]]),
+            'symbols': 'OsOClClClCl'},
+ 'P': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [3.0],
+       'name': 'P',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'P'},
+ 'Pt': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [2.0],
+        'name': 'Pt',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Pt'},
+ 'Pt(PF3)4': {'charges': None,
+              'database': 'TM3R2008',
+              'magmoms': None,
+              'name': 'P4PtF12',
+              'positions': array([[  2.00000000e-10,   0.00000000e+00,   1.10000000e-09],
+       [ -1.70000000e-09,  -3.80000000e-09,   2.24208410e+00],
+       [  2.11385716e+00,  -7.00000000e-10,  -7.47361364e-01],
+       [ -1.05692858e+00,  -1.83065400e+00,  -7.47361370e-01],
+       [ -1.05692858e+00,   1.83065401e+00,  -7.47361364e-01],
+       [  1.35617332e+00,  -6.40000000e-09,   3.00249051e+00],
+       [ -6.78086660e-01,   1.17448054e+00,   3.00249051e+00],
+       [ -6.78086663e-01,  -1.17448055e+00,   3.00249051e+00],
+       [  3.28283297e+00,  -3.60000000e-09,   2.77782301e-01],
+       [  2.60474631e+00,  -1.17448055e+00,  -1.64013640e+00],
+       [  2.60474632e+00,   1.17448055e+00,  -1.64013640e+00],
+       [ -2.31950315e+00,  -1.66853620e+00,  -1.64013641e+00],
+       [ -2.85243169e-01,  -2.84301675e+00,  -1.64013641e+00],
+       [ -1.64141649e+00,  -2.84301675e+00,   2.77782292e-01],
+       [ -2.31950314e+00,   1.66853621e+00,  -1.64013640e+00],
+       [ -1.64141648e+00,   2.84301675e+00,   2.77782302e-01],
+       [ -2.85243163e-01,   2.84301675e+00,  -1.64013640e+00]]),
+              'symbols': 'PtPPPPFFFFFFFFFFFF'},
+ 'Re': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [5.0],
+        'name': 'Re',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Re'},
+ 'ReO2Me(acetylene)': {'charges': None,
+                       'database': 'TM3R2008',
+                       'magmoms': None,
+                       'name': 'ReC3O2H5',
+                       'positions': array([[ 0.10832716, -0.11038995,  0.        ],
+       [-1.9345267 , -0.6255945 ,  0.        ],
+       [ 0.61427212, -0.77529932,  1.47696525],
+       [ 0.61427212, -0.77529932, -1.47696525],
+       [-0.6740278 ,  1.79763693,  0.        ],
+       [-2.43249081, -0.23629049, -0.88600926],
+       [-2.43249081, -0.23629049,  0.88600926],
+       [-1.97895971, -1.71693171,  0.        ],
+       [ 0.60922597,  1.85993289,  0.        ],
+       [-1.58166249,  2.38020766,  0.        ],
+       [ 1.48166873,  2.4941834 ,  0.        ]]),
+                       'symbols': 'ReCOOCHHHCHH'},
+ 'ReO3Me': {'charges': None,
+            'database': 'TM3R2008',
+            'magmoms': None,
+            'name': 'ReCO3H3',
+            'positions': array([[  0.00000000e+00,   0.00000000e+00,   6.81648865e-02],
+       [  0.00000000e+00,   0.00000000e+00,  -2.00552344e+00],
+       [ -7.00000000e-10,   1.62640415e+00,   5.34727627e-01],
+       [ -1.40850731e+00,  -8.13202073e-01,   5.34727627e-01],
+       [  1.40850731e+00,  -8.13202072e-01,   5.34727627e-01],
+       [  5.00000000e-10,  -1.03024896e+00,  -2.36255251e+00],
+       [  8.92221775e-01,   5.15124483e-01,  -2.36255251e+00],
+       [ -8.92221776e-01,   5.15124482e-01,  -2.36255251e+00]]),
+            'symbols': 'ReCOOOHHH'},
+ 'ReOCl4': {'charges': None,
+            'database': 'TM3R2008',
+            'magmoms': array([ 1.,  0.,  0.,  0.,  0.,  0.]),
+            'name': 'ReOCl4',
+            'positions': array([[ 0.        ,  0.        ,  0.20420213],
+       [ 0.        ,  0.        ,  1.8494153 ],
+       [ 0.        ,  2.18131112, -0.42456542],
+       [ 2.18131112,  0.        , -0.42456542],
+       [ 0.        , -2.18131112, -0.42456542],
+       [-2.18131112,  0.        , -0.42456542]]),
+            'symbols': 'ReOClClClCl'},
+ 'S': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [2.0],
+       'name': 'S',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'S'},
+ 'Ta': {'charges': None,
+        'database': 'TM3R2008',
+        'magmoms': [3.0],
+        'name': 'Ta',
+        'positions': array([[ 0.,  0.,  0.]]),
+        'symbols': 'Ta'},
+ 'TaCl5': {'charges': None,
+           'database': 'TM3R2008',
+           'magmoms': None,
+           'name': 'Cl5Ta',
+           'positions': array([[  0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
+       [  0.00000000e+00,   0.00000000e+00,   2.32221776e+00],
+       [  2.00000000e-10,   2.27473923e+00,   0.00000000e+00],
+       [  1.96998196e+00,  -1.13736961e+00,   0.00000000e+00],
+       [ -1.96998196e+00,  -1.13736961e+00,   0.00000000e+00],
+       [  0.00000000e+00,   0.00000000e+00,  -2.32221776e+00]]),
+           'symbols': 'TaClClClClCl'},
+ 'W': {'charges': None,
+       'database': 'TM3R2008',
+       'magmoms': [4.0],
+       'name': 'W',
+       'positions': array([[ 0.,  0.,  0.]]),
+       'symbols': 'W'},
+ 'W(CO)6': {'charges': None,
+            'database': 'TM3R2008',
+            'magmoms': None,
+            'name': 'C6O6W',
+            'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [ 0.        ,  0.        ,  2.06220361],
+       [ 0.        ,  2.06220361,  0.        ],
+       [ 0.        ,  0.        , -2.06220361],
+       [ 0.        , -2.06220361,  0.        ],
+       [ 2.06220361,  0.        ,  0.        ],
+       [-2.06220361,  0.        ,  0.        ],
+       [ 0.        ,  0.        ,  3.19881782],
+       [ 0.        ,  3.19881782,  0.        ],
+       [ 0.        ,  0.        , -3.19881782],
+       [ 0.        , -3.19881782,  0.        ],
+       [ 3.19881782,  0.        ,  0.        ],
+       [-3.19881782,  0.        ,  0.        ]]),
+            'symbols': 'WCCCCCCOOOOOO'},
+ 'WCp2H2': {'charges': None,
+            'database': 'TM3R2008',
+            'magmoms': None,
+            'name': 'H12C10W',
+            'positions': array([[  0.00000000e+00,   0.00000000e+00,   2.00584842e-01],
+       [ -2.91900900e-04,   2.29699115e+00,   6.65595016e-01],
+       [  2.91900900e-04,  -2.29699115e+00,   6.65595016e-01],
+       [  4.77456255e-01,   1.45921150e+00,  -1.41346899e+00],
+       [ -4.77456255e-01,  -1.45921150e+00,  -1.41346899e+00],
+       [  1.65479029e+00,   1.33025115e+00,  -6.07252372e-01],
+       [ -1.65479029e+00,  -1.33025115e+00,  -6.07252372e-01],
+       [ -5.58127908e-01,   2.04663917e+00,  -6.16468331e-01],
+       [  5.58127908e-01,  -2.04663917e+00,  -6.16468331e-01],
+       [  1.33889056e+00,   1.86290544e+00,   6.71492660e-01],
+       [ -1.33889056e+00,  -1.86290544e+00,   6.71492660e-01],
+       [ -5.25783914e-01,   2.71829365e+00,   1.50595743e+00],
+       [  5.25783914e-01,  -2.71829365e+00,   1.50595743e+00],
+       [  3.99007679e-01,   1.20429583e+00,  -2.45717096e+00],
+       [ -3.99007679e-01,  -1.20429583e+00,  -2.45717096e+00],
+       [  2.62045961e+00,   9.73610957e-01,  -9.23208679e-01],
+       [ -2.62045961e+00,  -9.73610957e-01,  -9.23208679e-01],
+       [ -1.54767221e+00,   2.32127087e+00,  -9.40603479e-01],
+       [  1.54767221e+00,  -2.32127087e+00,  -9.40603479e-01],
+       [  2.00491669e+00,   1.89866806e+00,   1.51703806e+00],
+       [ -2.00491669e+00,  -1.89866806e+00,   1.51703806e+00],
+       [ -1.01499724e+00,   3.30079122e-01,   1.52525947e+00],
+       [  1.01499724e+00,  -3.30079122e-01,   1.52525947e+00]]),
+            'symbols': 'WCCCCCCCCCCHHHHHHHHHHHH'},
+ 'WF6': {'charges': None,
+         'database': 'TM3R2008',
+         'magmoms': None,
+         'name': 'WF6',
+         'positions': array([[ 0.        ,  0.        ,  0.        ],
+       [ 0.        ,  0.        ,  1.83355384],
+       [ 0.        ,  1.83355384,  0.        ],
+       [ 1.83355384,  0.        ,  0.        ],
+       [ 0.        , -1.83355384,  0.        ],
+       [-1.83355384,  0.        ,  0.        ],
+       [ 0.        ,  0.        , -1.83355384]]),
+         'symbols': 'WFFFFFF'},
+ 'WMe6': {'charges': None,
+          'database': 'TM3R2008',
+          'magmoms': None,
+          'name': 'H18C6W',
+          'positions': array([[  1.00000000e-10,   0.00000000e+00,   7.65893894e-02],
+       [ -5.45968352e-01,   1.72450779e+00,   1.19698965e+00],
+       [  1.76645173e+00,  -3.89431434e-01,   1.19698965e+00],
+       [ -1.22048338e+00,  -1.33507636e+00,   1.19698965e+00],
+       [ -3.94222194e-01,   1.50121675e+00,  -1.45166875e+00],
+       [  1.49720294e+00,  -4.09201939e-01,  -1.45166875e+00],
+       [ -1.10298074e+00,  -1.09201481e+00,  -1.45166875e+00],
+       [ -3.63081775e-01,   1.44094933e+00,   2.24204871e+00],
+       [  1.42943961e+00,  -4.06036621e-01,   2.24204871e+00],
+       [ -1.06635783e+00,  -1.03491270e+00,   2.24204871e+00],
+       [ -3.59966937e-01,   1.16646310e+00,  -2.48466456e+00],
+       [  1.19017014e+00,  -2.71491036e-01,  -2.48466456e+00],
+       [ -8.30203206e-01,  -8.94972061e-01,  -2.48466456e+00],
+       [  1.98748912e-02,   2.63070127e+00,   9.82230750e-01],
+       [  2.26831669e+00,  -1.33256280e+00,   9.82230750e-01],
+       [ -2.28819158e+00,  -1.29813848e+00,   9.82230750e-01],
+       [ -1.36729179e+00,   1.96289567e+00,  -1.26126613e+00],
+       [  2.38356341e+00,   2.02661592e-01,  -1.26126613e+00],
+       [ -1.01627162e+00,  -2.16555726e+00,  -1.26126613e+00],
+       [ -1.61064099e+00,   1.94736732e+00,   1.09219183e+00],
+       [  2.49179007e+00,   4.21172352e-01,   1.09219183e+00],
+       [ -8.81149078e-01,  -2.36853968e+00,   1.09219183e+00],
+       [  3.70369014e-01,   2.27366111e+00,  -1.31090510e+00],
+       [  1.78386377e+00,  -1.45757953e+00,  -1.31090510e+00],
+       [ -2.15423278e+00,  -8.16081579e-01,  -1.31090510e+00]]),
+          'symbols': 'WCCCCCCHHHHHHHHHHHHHHHHHH'},
+ 'WOF4': {'charges': None,
+          'database': 'TM3R2008',
+          'magmoms': None,
+          'name': 'OWF4',
+          'positions': array([[ 0.        ,  0.        ,  0.03766786],
+       [ 0.        ,  0.        ,  1.70513688],
+       [ 0.        ,  1.78103536, -0.44897456],
+       [ 1.78103536,  0.        , -0.44897456],
+       [ 0.        , -1.78103536, -0.44897456],
+       [-1.78103536,  0.        , -0.44897456]]),
+          'symbols': 'WOFFFF'},
+ 'WSCl4': {'charges': None,
+           'database': 'TM3R2008',
+           'magmoms': None,
+           'name': 'SWCl4',
+           'positions': array([[ 0.        ,  0.        ,  0.04250424],
+       [ 0.        ,  0.        ,  2.12606446],
+       [ 0.        ,  2.21369028, -0.53145183],
+       [ 2.21369028,  0.        , -0.53145183],
+       [ 0.        , -2.21369028, -0.53145183],
+       [-2.21369028,  0.        , -0.53145183]]),
+           'symbols': 'WSClClClCl'},
+ 'trans-TaF2Me3': {'charges': None,
+                   'database': 'TM3R2008',
+                   'magmoms': None,
+                   'name': 'H9C3F2Ta',
+                   'positions': array([[  0.00000000e+00,   2.00000000e-10,   0.00000000e+00],
+       [  0.00000000e+00,   2.00000000e-10,   1.88678026e+00],
+       [  1.08476960e-03,   2.14496447e+00,   0.00000000e+00],
+       [  1.85705133e+00,  -1.07342167e+00,   0.00000000e+00],
+       [ -1.85813610e+00,  -1.07154280e+00,   0.00000000e+00],
+       [  0.00000000e+00,   2.00000000e-10,  -1.88678026e+00],
+       [  1.04343657e+00,   2.48691538e+00,   0.00000000e+00],
+       [  1.63201361e+00,  -2.14710027e+00,   0.00000000e+00],
+       [ -2.67545018e+00,  -3.39815115e-01,   0.00000000e+00],
+       [ -4.84880162e-01,   2.54187313e+00,  -8.93158317e-01],
+       [  2.44376678e+00,  -8.51018024e-01,  -8.93158317e-01],
+       [ -1.95888662e+00,  -1.69085510e+00,  -8.93158317e-01],
+       [ -4.84880162e-01,   2.54187313e+00,   8.93158317e-01],
+       [  2.44376678e+00,  -8.51018024e-01,   8.93158317e-01],
+       [ -1.95888662e+00,  -1.69085510e+00,   8.93158317e-01]]),
+                   'symbols': 'TaFCCCFHHHHHHHHH'}}
diff --git a/ase/data/vdw.py b/ase/data/vdw.py
new file mode 100644
index 0000000..6f8b823
--- /dev/null
+++ b/ase/data/vdw.py
@@ -0,0 +1,129 @@
+# encoding: utf-8
+""" Van der Waals radii in [A] taken from
+http://www.webelements.com/periodicity/van_der_waals_radius/
+and the references given there:
+1. A. Bondi, J. Phys. Chem., 1964, 68, 441.
+2. L. Pauling, The Nature of the Chemical Bond, 
+   Cornell University Press, USA, 1945.
+3. J.E. Huheey, E.A. Keiter, and R.L. Keiter in Inorganic Chemistry : 
+   Principles of Structure and Reactivity, 4th edition, HarperCollins, 
+   New York, USA, 1993.W.W. Porterfield in Inorganic chemistry, 
+   a unified approach, Addison Wesley Publishing Co., 
+   Reading Massachusetts, USA, 1984.
+4. A.M. James and M.P. Lord in Macmillan's Chemical and Physical Data, 
+   Macmillan, London, UK, 1992.
+
+additional source from: http://de.wikipedia.org/wiki/Van-der-Waals-Radius
+5. Manjeera Mantina, Adam C. Chamberlin, Rosendo Valero, 
+   Christopher J. Cramer, Donald G. Truhlar: Consistent van der Waals Radii 
+   for the Whole Main Group. In: J. Phys. Chem. A. 2009, 113, 5806–5812, 
+   doi:10.1021/jp8111556
+"""
+
+import numpy as np
+
+vdw_radii = np.array([
+ np.nan, # X
+ 1.20, # H
+ 1.40, # He [1]
+ 1.82, # Li [1]
+ 1.53, # Be [5]
+ 1.92, # B [5]
+ 1.70, # C [1]
+ 1.55, # N [1]
+ 1.52, # O [1]
+ 1.47, # F [1]
+ 1.54, # Ne [1]
+ 2.27, # Na [1]
+ 1.73, # Mg [1]
+ 1.84, # Al [5]
+ 2.10, # Si [1]
+ 1.80, # P [1]
+ 1.80, # S [1]
+ 1.75, # Cl [1]
+ 1.88, # Ar [1]
+ 2.75, # K [1]
+ 2.31, # Ca [5]
+ np.nan, # Sc
+ np.nan, # Ti
+ np.nan, # V
+ np.nan, # Cr
+ np.nan, # Mn
+ np.nan, # Fe
+ np.nan, # Co
+ 1.63, # Ni [1]
+ 1.40, # Cu [1]
+ 1.39, # Zn [1]
+ 1.87, # Ga [1]
+ 2.11, # Ge [5]
+ 1.85, # As [1]
+ 1.90, # Se [1]
+ 1.85, # Br [1]
+ 2.02, # Kr [1]
+ 3.03, # Rb [5]
+ 2.49, # Sr [5]
+ np.nan, # Y
+ np.nan, # Zr
+ np.nan, # Nb
+ np.nan, # Mo
+ np.nan, # Tc
+ np.nan, # Ru
+ np.nan, # Rh
+ 1.63, # Pd [1]
+ 1.72, # Ag [1]
+ 1.58, # Cd [1]
+ 1.93, # In [1]
+ 2.17, # Sn [1]
+ 2.06, # Sb [5]
+ 2.06, # Te [1]
+ 1.98, # I [1]
+ 2.16, # Xe [1]
+ 3.43, # Cs [5]
+ 2.49, # Ba [5]
+ np.nan, # La
+ np.nan, # Ce
+ np.nan, # Pr
+ np.nan, # Nd
+ np.nan, # Pm
+ np.nan, # Sm
+ np.nan, # Eu
+ np.nan, # Gd
+ np.nan, # Tb
+ np.nan, # Dy
+ np.nan, # Ho
+ np.nan, # Er
+ np.nan, # Tm
+ np.nan, # Yb
+ np.nan, # Lu
+ np.nan, # Hf
+ np.nan, # Ta
+ np.nan, # W
+ np.nan, # Re
+ np.nan, # Os
+ np.nan, # Ir
+ 1.75, # Pt [1]
+ 1.66, # Au [1]
+ 1.55, # Hg [1]
+ 1.96, # Tl [1]
+ 2.02, # Pb [1]
+ 2.07, # Bi [5]
+ 1.97, # Po [5]
+ 2.02, # At [5]
+ 2.20, # Rn [5]
+ 3.48, # Fr [5]
+ 2.83, # Ra [5]
+ np.nan, # Ac
+ np.nan, # Th
+ np.nan, # Pa
+ 1.86, # U [1]
+ np.nan, # Np
+ np.nan, # Pu
+ np.nan, # Am
+ np.nan, # Cm
+ np.nan, # Bk
+ np.nan, # Cf
+ np.nan, # Es
+ np.nan, # Fm
+ np.nan, # Md
+ np.nan, # No
+ np.nan]) # Lr
diff --git a/ase/dft/__init__.py b/ase/dft/__init__.py
new file mode 100644
index 0000000..a093f9d
--- /dev/null
+++ b/ase/dft/__init__.py
@@ -0,0 +1,27 @@
+import numpy as np
+from ase.dft.stm import STM
+from ase.dft.dos import DOS
+from ase.dft.wannier import Wannier
+from ase.dft.kpoints import monkhorst_pack
+
+def get_distribution_moment(x, y, order=0):
+    """Return the moment of nth order of distribution.
+    
+    1st and 2nd order moments of a band correspond to the band's
+    center and width respectively.
+    
+    For integration, the trapezoid rule is used.
+    """
+
+    x = np.asarray(x)
+    y = np.asarray(y)
+        
+    if order==0:
+        return np.trapz(y, x)
+    elif isinstance(order, int):
+        return np.trapz(x**order * y, x) / np.trapz(y, x)
+    elif hasattr(order, '__iter__'):
+        return [get_distribution_moment(x, y, n) for n in order]
+    else:
+        raise ValueError('Illegal order: %s' % order)
+
diff --git a/ase/dft/dos.py b/ase/dft/dos.py
new file mode 100644
index 0000000..b812f5e
--- /dev/null
+++ b/ase/dft/dos.py
@@ -0,0 +1,67 @@
+from math import pi, sqrt
+
+import numpy as np
+
+
+class DOS:
+    def __init__(self, calc, width=0.1, window=None, npts=201):
+        """Electronic Density Of States object.
+
+        calc: calculator object
+            Any ASE compliant calculator object.
+        width: float
+            Width of guassian smearing.
+        window: tuple of two float
+            Use ``window=(emin, emax)``.  If not specified, a window
+            big enough to hold all the eigenvalues will be used.
+        npts: int
+            Number of points.
+
+        """
+        
+        self.npts = npts
+        self.width = width
+        self.w_k = calc.get_k_point_weights()
+        self.nspins = calc.get_number_of_spins()
+        self.e_skn = np.array([[calc.get_eigenvalues(kpt=k, spin=s)
+                                for k in range(len(self.w_k))]
+                               for s in range(self.nspins)])
+        self.e_skn -= calc.get_fermi_level()
+
+        if window is None:
+            emin = self.e_skn.min() - 5 * self.width
+            emax = self.e_skn.max() + 5 * self.width
+        else:
+            emin, emax = window
+
+        self.energies = np.linspace(emin, emax, npts)
+
+    def get_energies(self):
+        """Return the array of energies used to sample the DOS. The energies are reported relative to the Fermi level."""
+        return self.energies
+
+    def delta(self, energy):
+        """Return a delta-function centered at 'energy'."""
+        x = -((self.energies - energy) / self.width)**2
+        return np.exp(x) / (sqrt(pi) * self.width)
+
+    def get_dos(self, spin=None):
+        """Get array of DOS values.
+
+        The *spin* argument can be 0 or 1 (spin up or down) - if not
+        specified, the total DOS is returned.
+        """
+        
+        if spin is None:
+            if self.nspins == 2:
+                # Spin-polarized calculation, but no spin specified -
+                # return the total DOS:
+                return self.get_dos(spin=0) + self.get_dos(spin=1)
+            else:
+                spin = 0
+        
+        dos = np.zeros(self.npts)
+        for w, e_n in zip(self.w_k, self.e_skn[spin]):
+            for e in e_n:
+                dos += w * self.delta(e)
+        return dos
diff --git a/ase/dft/kpoints.py b/ase/dft/kpoints.py
new file mode 100644
index 0000000..d10522c
--- /dev/null
+++ b/ase/dft/kpoints.py
@@ -0,0 +1,247 @@
+from __future__ import division
+import warnings
+
+import numpy as np
+
+
+def monkhorst_pack(size):
+    """Construct a uniform sampling of k-space of given size."""
+    if np.less_equal(size, 0).any():
+        raise ValueError('Illegal size: %s' % list(size))
+    kpts = np.indices(size).transpose((1, 2, 3, 0)).reshape((-1, 3))
+    return (kpts + 0.5) / size - 0.5
+
+
+def get_monkhorst_pack_size_and_offset(kpts):
+    """Find Monkhorst-Pack size and offset.
+
+    Returns (size, offset), where::
+
+        kpts = monkhorst_pack(size) + offset.
+    
+    The set of k-points must not have been symmetry reduced."""
+
+    if len(kpts) == 1:
+        return np.ones(3, int), np.array(kpts[0], dtype=float)
+    
+    size = np.zeros(3, int)
+    for c in range(3):
+        # Determine increment between k-points along current axis
+        delta = max(np.diff(np.sort(kpts[:, c])))
+
+        # Determine number of k-points as inverse of distance between kpoints
+        if delta > 1e-8:
+            size[c] = int(round(1.0 / delta))
+        else:
+            size[c] = 1
+
+    kpts0 = monkhorst_pack(size)
+    offsets = kpts - kpts0
+
+    # All offsets must be identical:
+    if (offsets.ptp(axis=0) > 1e-9).any():
+        raise ValueError('Not an ASE-style Monkhorst-Pack grid!')
+
+    return size, offsets[0].copy()
+
+
+def get_monkhorst_shape(kpts):
+    warnings.warn('Use get_monkhorst_pack_size_and_offset()[0] instead.')
+    return get_monkhorst_pack_size_and_offset(kpts)[0]
+
+
+def kpoint_convert(cell_cv, skpts_kc=None, ckpts_kv=None):
+    """Convert k-points between scaled and cartesian coordinates.
+
+    Given the atomic unit cell, and either the scaled or cartesian k-point
+    coordinates, the other is determined.
+
+    The k-point arrays can be either a single point, or a list of points,
+    i.e. the dimension k can be empty or multidimensional.
+    """
+    if ckpts_kv is None:
+        icell_cv = 2 * np.pi * np.linalg.inv(cell_cv).T
+        return np.dot(skpts_kc, icell_cv)
+    elif skpts_kc is None:
+        return np.dot(ckpts_kv, cell_cv.T) / (2 * np.pi)
+    else:
+        raise KeyError('Either scaled or cartesian coordinates must be given.')
+
+
+def get_bandpath(points, cell, npoints=50):
+    """Make a list of kpoints defining the path between the given points.
+
+    points: list
+        List of special IBZ point pairs, e.g. ``points =
+        [W, L, Gamma, X, W, K]``.  These should be given in
+        scaled coordinates.
+    cell: 3x3 ndarray
+        Unit cell of the atoms.
+    npoints: int
+        Length of the output kpts list.
+
+    Return list of k-points, list of x-coordinates and list of
+    x-coordinates of special points."""
+
+    points = np.asarray(points)
+    dists = points[1:] - points[:-1]
+    lengths = [np.linalg.norm(d) for d in kpoint_convert(cell, skpts_kc=dists)]
+    length = sum(lengths)
+    kpts = []
+    x0 = 0
+    x = []
+    X = [0]
+    for P, d, L in zip(points[:-1], dists, lengths):
+        n = int(round(L * (npoints - 1 - len(x)) / (length - x0)))
+        for t in np.linspace(0, 1, n, endpoint=False):
+            kpts.append(P + t * d)
+            x.append(x0 + t * L)
+        x0 += L
+        X.append(x0)
+    kpts.append(points[-1])
+    x.append(x0)
+    return kpts, np.array(x), np.array(X)
+
+
+# The following is a list of the critical points in the 1. Brillouin zone
+# for some typical crystal structures.
+# (In units of the reciprocal basis vectors)
+# See http://en.wikipedia.org/wiki/Brillouin_zone
+ibz_points = {'cubic': {'Gamma': [0,     0,     0    ],
+                        'X':     [0,     0 / 2, 1 / 2],
+                        'R':     [1 / 2, 1 / 2, 1 / 2],
+                        'M':     [0 / 2, 1 / 2, 1 / 2]},
+
+              'fcc':   {'Gamma': [0,     0,     0    ],
+                        'X':     [1 / 2, 0,     1 / 2],
+                        'W':     [1 / 2, 1 / 4, 3 / 4],
+                        'K':     [3 / 8, 3 / 8, 3 / 4],
+                        'U':     [5 / 8, 1 / 4, 5 / 8],
+                        'L':     [1 / 2, 1 / 2, 1 / 2]},
+
+              'bcc':   {'Gamma': [0,      0,     0    ],
+                        'H':     [1 / 2, -1 / 2, 1 / 2],
+                        'N':     [0,      0,     1 / 2],
+                        'P':     [1 / 4,  1 / 4, 1 / 4]},
+              'hexagonal':
+                       {'Gamma': [0,      0,       0   ],
+                        'M':     [0,      1 / 2,   0   ],
+                        'K':     [-1 / 3, 1 / 3,   0   ],
+                        'A':     [0,      0,     1 / 2 ],
+                        'L':     [0,     1 / 2,  1 / 2 ],
+                        'H':     [-1 / 3, 1 / 3, 1 / 2 ]},
+              'tetragonal':
+                       {'Gamma': [0,      0,       0   ],
+                        'X':     [1 / 2,  0,       0   ],
+                        'M':     [1 / 2,  1 / 2,   0   ],
+                        'Z':     [0,      0,     1 / 2 ],
+                        'R':     [1 / 2,  0,     1 / 2 ],
+                        'A':     [1 / 2,  1 / 2, 1 / 2 ]},
+              'orthorhombic':
+                       {'Gamma': [0,      0,       0   ],
+                        'R':     [1 / 2,  1 / 2, 1 / 2 ],
+                        'S':     [1 / 2,  1 / 2,   0   ],
+                        'T':     [0,      1 / 2, 1 / 2 ],
+                        'U':     [1 / 2,  0,     1 / 2 ],
+                        'X':     [1 / 2,  0,       0   ],
+                        'Y':     [0,      1 / 2,   0   ],
+                        'Z':     [0,      0,     1 / 2 ]},              
+              }
+
+# ChadiCohen k point grids. The k point grids are given in units of the
+# reciprocal unit cell. The variables are named after the following
+# convention: cc+'<Nkpoints>'+_+'shape'. For example an 18 k point
+# sq(3)xsq(3) is named 'cc18_sq3xsq3'.
+
+cc6_1x1 = np.array([1, 1, 0, 1, 0, 0, 0, -1, 0, -1, -1, 0, -1, 0, 0,
+    0, 1, 0]).reshape((6, 3)) / 3.0
+
+cc12_2x3 = np.array([3, 4, 0, 3, 10, 0, 6, 8, 0, 3, -2, 0, 6, -4, 0,
+    6, 2, 0, -3, 8, 0, -3, 2, 0, -3, -4, 0, -6, 4, 0, -6, -2, 0, -6,
+    -8, 0]).reshape((12, 3)) / 18.0
+
+cc18_sq3xsq3 = np.array([2, 2, 0, 4, 4, 0, 8, 2, 0, 4, -2, 0, 8, -4,
+    0, 10, -2, 0, 10, -8, 0, 8, -10, 0, 2, -10, 0, 4, -8, 0, -2, -8,
+    0, 2, -4, 0, -4, -4, 0, -2, -2, 0, -4, 2, 0, -2, 4, 0, -8, 4, 0,
+    -4, 8, 0]).reshape((18, 3)) / 18.0
+
+cc18_1x1 = np.array([2, 4, 0, 2, 10, 0, 4, 8, 0, 8, 4, 0, 8, 10, 0,
+    10, 8, 0, 2, -2, 0, 4, -4, 0, 4, 2, 0, -2, 8, 0, -2, 2, 0, -2, -4,
+    0, -4, 4, 0, -4, -2, 0, -4, -8, 0, -8, 2, 0, -8, -4, 0, -10, -2,
+    0]).reshape((18, 3)) / 18.0
+
+cc54_sq3xsq3 = np.array([4, -10, 0, 6, -10, 0, 0, -8, 0, 2, -8, 0, 6,
+    -8, 0, 8, -8, 0, -4, -6, 0, -2, -6, 0, 2, -6, 0, 4, -6, 0, 8, -6,
+    0, 10, -6, 0, -6, -4, 0, -2, -4, 0, 0, -4, 0, 4, -4, 0, 6, -4, 0,
+    10, -4, 0, -6, -2, 0, -4, -2, 0, 0, -2, 0, 2, -2, 0, 6, -2, 0, 8,
+    -2, 0, -8, 0, 0, -4, 0, 0, -2, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0,
+    -8, 2, 0, -6, 2, 0, -2, 2, 0, 0, 2, 0, 4, 2, 0, 6, 2, 0, -10, 4,
+    0, -6, 4, 0, -4, 4, 0, 0, 4, 0, 2, 4, 0, 6, 4, 0, -10, 6, 0, -8,
+    6, 0, -4, 6, 0, -2, 6, 0, 2, 6, 0, 4, 6, 0, -8, 8, 0, -6, 8, 0,
+    -2, 8, 0, 0, 8, 0, -6, 10, 0, -4, 10, 0]).reshape((54, 3)) / 18.0
+
+cc54_1x1 = np.array([2, 2, 0, 4, 4, 0, 8, 8, 0, 6, 8, 0, 4, 6, 0, 6,
+    10, 0, 4, 10, 0, 2, 6, 0, 2, 8, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, -2,
+    6, 0, -2, 4, 0, -4, 6, 0, -6, 4, 0, -4, 2, 0, -6, 2, 0, -2, 0, 0,
+    -4, 0, 0, -8, 0, 0, -8, -2, 0, -6, -2, 0, -10, -4, 0, -10, -6, 0,
+    -6, -4, 0, -8, -6, 0, -2, -2, 0, -4, -4, 0, -8, -8, 0, 4, -2, 0,
+    6, -2, 0, 6, -4, 0, 2, 0, 0, 4, 0, 0, 6, 2, 0, 6, 4, 0, 8, 6, 0,
+    8, 0, 0, 8, 2, 0, 10, 4, 0, 10, 6, 0, 2, -4, 0, 2, -6, 0, 4, -6,
+    0, 0, -2, 0, 0, -4, 0, -2, -6, 0, -4, -6, 0, -6, -8, 0, 0, -8, 0,
+    -2, -8, 0, -4, -10, 0, -6, -10, 0]).reshape((54, 3)) / 18.0
+
+cc162_sq3xsq3 = np.array([-8, 16, 0, -10, 14, 0, -7, 14, 0, -4, 14,
+    0, -11, 13, 0, -8, 13, 0, -5, 13, 0, -2, 13, 0, -13, 11, 0, -10,
+    11, 0, -7, 11, 0, -4, 11, 0, -1, 11, 0, 2, 11, 0, -14, 10, 0, -11,
+    10, 0, -8, 10, 0, -5, 10, 0, -2, 10, 0, 1, 10, 0, 4, 10, 0, -16,
+    8, 0, -13, 8, 0, -10, 8, 0, -7, 8, 0, -4, 8, 0, -1, 8, 0, 2, 8, 0,
+    5, 8, 0, 8, 8, 0, -14, 7, 0, -11, 7, 0, -8, 7, 0, -5, 7, 0, -2, 7,
+    0, 1, 7, 0, 4, 7, 0, 7, 7, 0, 10, 7, 0, -13, 5, 0, -10, 5, 0, -7,
+    5, 0, -4, 5, 0, -1, 5, 0, 2, 5, 0, 5, 5, 0, 8, 5, 0, 11, 5, 0,
+    -14, 4, 0, -11, 4, 0, -8, 4, 0, -5, 4, 0, -2, 4, 0, 1, 4, 0, 4, 4,
+    0, 7, 4, 0, 10, 4, 0, -13, 2, 0, -10, 2, 0, -7, 2, 0, -4, 2, 0,
+    -1, 2, 0, 2, 2, 0, 5, 2, 0, 8, 2, 0, 11, 2, 0, -11, 1, 0, -8, 1,
+    0, -5, 1, 0, -2, 1, 0, 1, 1, 0, 4, 1, 0, 7, 1, 0, 10, 1, 0, 13, 1,
+    0, -10, -1, 0, -7, -1, 0, -4, -1, 0, -1, -1, 0, 2, -1, 0, 5, -1,
+    0, 8, -1, 0, 11, -1, 0, 14, -1, 0, -11, -2, 0, -8, -2, 0, -5, -2,
+    0, -2, -2, 0, 1, -2, 0, 4, -2, 0, 7, -2, 0, 10, -2, 0, 13, -2, 0,
+    -10, -4, 0, -7, -4, 0, -4, -4, 0, -1, -4, 0, 2, -4, 0, 5, -4, 0,
+    8, -4, 0, 11, -4, 0, 14, -4, 0, -8, -5, 0, -5, -5, 0, -2, -5, 0,
+    1, -5, 0, 4, -5, 0, 7, -5, 0, 10, -5, 0, 13, -5, 0, 16, -5, 0, -7,
+    -7, 0, -4, -7, 0, -1, -7, 0, 2, -7, 0, 5, -7, 0, 8, -7, 0, 11, -7,
+    0, 14, -7, 0, 17, -7, 0, -8, -8, 0, -5, -8, 0, -2, -8, 0, 1, -8,
+    0, 4, -8, 0, 7, -8, 0, 10, -8, 0, 13, -8, 0, 16, -8, 0, -7, -10,
+    0, -4, -10, 0, -1, -10, 0, 2, -10, 0, 5, -10, 0, 8, -10, 0, 11,
+    -10, 0, 14, -10, 0, 17, -10, 0, -5, -11, 0, -2, -11, 0, 1, -11, 0,
+    4, -11, 0, 7, -11, 0, 10, -11, 0, 13, -11, 0, 16, -11, 0, -1, -13,
+    0, 2, -13, 0, 5, -13, 0, 8, -13, 0, 11, -13, 0, 14, -13, 0, 1,
+    -14, 0, 4, -14, 0, 7, -14, 0, 10, -14, 0, 13, -14, 0, 5, -16, 0,
+    8, -16, 0, 11, -16, 0, 7, -17, 0, 10, -17, 0]).reshape((162, 3)) / 27.0
+
+cc162_1x1 = np.array([-8, -16, 0, -10, -14, 0, -7, -14, 0, -4, -14,
+    0, -11, -13, 0, -8, -13, 0, -5, -13, 0, -2, -13, 0, -13, -11, 0,
+    -10, -11, 0, -7, -11, 0, -4, -11, 0, -1, -11, 0, 2, -11, 0, -14,
+    -10, 0, -11, -10, 0, -8, -10, 0, -5, -10, 0, -2, -10, 0, 1, -10,
+    0, 4, -10, 0, -16, -8, 0, -13, -8, 0, -10, -8, 0, -7, -8, 0, -4,
+    -8, 0, -1, -8, 0, 2, -8, 0, 5, -8, 0, 8, -8, 0, -14, -7, 0, -11,
+    -7, 0, -8, -7, 0, -5, -7, 0, -2, -7, 0, 1, -7, 0, 4, -7, 0, 7, -7,
+    0, 10, -7, 0, -13, -5, 0, -10, -5, 0, -7, -5, 0, -4, -5, 0, -1,
+    -5, 0, 2, -5, 0, 5, -5, 0, 8, -5, 0, 11, -5, 0, -14, -4, 0, -11,
+    -4, 0, -8, -4, 0, -5, -4, 0, -2, -4, 0, 1, -4, 0, 4, -4, 0, 7, -4,
+    0, 10, -4, 0, -13, -2, 0, -10, -2, 0, -7, -2, 0, -4, -2, 0, -1,
+    -2, 0, 2, -2, 0, 5, -2, 0, 8, -2, 0, 11, -2, 0, -11, -1, 0, -8,
+    -1, 0, -5, -1, 0, -2, -1, 0, 1, -1, 0, 4, -1, 0, 7, -1, 0, 10, -1,
+    0, 13, -1, 0, -10, 1, 0, -7, 1, 0, -4, 1, 0, -1, 1, 0, 2, 1, 0, 5,
+    1, 0, 8, 1, 0, 11, 1, 0, 14, 1, 0, -11, 2, 0, -8, 2, 0, -5, 2, 0,
+    -2, 2, 0, 1, 2, 0, 4, 2, 0, 7, 2, 0, 10, 2, 0, 13, 2, 0, -10, 4,
+    0, -7, 4, 0, -4, 4, 0, -1, 4, 0, 2, 4, 0, 5, 4, 0, 8, 4, 0, 11, 4,
+    0, 14, 4, 0, -8, 5, 0, -5, 5, 0, -2, 5, 0, 1, 5, 0, 4, 5, 0, 7, 5,
+    0, 10, 5, 0, 13, 5, 0, 16, 5, 0, -7, 7, 0, -4, 7, 0, -1, 7, 0, 2,
+    7, 0, 5, 7, 0, 8, 7, 0, 11, 7, 0, 14, 7, 0, 17, 7, 0, -8, 8, 0,
+    -5, 8, 0, -2, 8, 0, 1, 8, 0, 4, 8, 0, 7, 8, 0, 10, 8, 0, 13, 8, 0,
+    16, 8, 0, -7, 10, 0, -4, 10, 0, -1, 10, 0, 2, 10, 0, 5, 10, 0, 8,
+    10, 0, 11, 10, 0, 14, 10, 0, 17, 10, 0, -5, 11, 0, -2, 11, 0, 1,
+    11, 0, 4, 11, 0, 7, 11, 0, 10, 11, 0, 13, 11, 0, 16, 11, 0, -1,
+    13, 0, 2, 13, 0, 5, 13, 0, 8, 13, 0, 11, 13, 0, 14, 13, 0, 1, 14,
+    0, 4, 14, 0, 7, 14, 0, 10, 14, 0, 13, 14, 0, 5, 16, 0, 8, 16, 0,
+    11, 16, 0, 7, 17, 0, 10, 17, 0]).reshape((162, 3)) / 27.0
diff --git a/ase/dft/stm.py b/ase/dft/stm.py
new file mode 100644
index 0000000..764409c
--- /dev/null
+++ b/ase/dft/stm.py
@@ -0,0 +1,167 @@
+from math import exp, sqrt
+
+import numpy as np
+
+from ase.atoms import Atoms
+
+
+class STM:
+    def __init__(self, atoms, symmetries=None):
+        if isinstance(atoms, Atoms):
+            calc = atoms.get_calculator()
+        else:
+            calc = atoms
+            atoms = calc.get_atoms()
+        self.nbands = calc.get_number_of_bands()
+        self.weights = calc.get_k_point_weights()
+        self.nkpts = len(self.weights)
+        self.nspins = calc.get_number_of_spins()
+        self.eigs = np.array([[calc.get_eigenvalues(k, s)
+                               for k in range(self.nkpts)]
+                              for s in range(self.nspins)])
+        self.eigs -= calc.get_fermi_level()
+        self.calc = calc
+        self.cell = atoms.get_cell()
+        assert not self.cell[2, :2].any() and not self.cell[:2, 2].any()
+        self.ldos = None
+        self.symmetries = symmetries or []
+                               
+    def calculate_ldos(self, width=None):
+        if self.ldos is not None and width == self.width:
+            return
+
+        if width is None:
+            width = 0.1
+            
+        ldos = None
+        for s in range(self.nspins):
+            for k in range(self.nkpts):
+                for n in range(self.nbands):
+                    psi = self.calc.get_pseudo_wave_function(n, k, s)
+                    if ldos is None:
+                        ldos = np.zeros(psi.shape)
+                    f = (exp(-(self.eigs[s, k, n] / width)**2) *
+                         self.weights[k])
+                    ldos += f * (psi * np.conj(psi)).real
+
+        if 0 in self.symmetries:
+            # (x,y) -> (-x,y)
+            ldos[1:] += ldos[:0:-1].copy()
+            ldos[1:] *= 0.5
+
+        if 1 in self.symmetries:
+            # (x,y) -> (x,-y)
+            ldos[:, 1:] += ldos[:, :0:-1].copy()
+            ldos[:, 1:] *= 0.5
+            
+        if 2 in self.symmetries:
+            # (x,y) -> (y,x)
+            ldos += ldos.transpose((1, 0, 2)).copy()
+            ldos *= 0.5
+            
+        self.ldos = ldos
+        self.width = width
+
+    #def save_ldos(self, filename='ldos.pckl'):
+        
+
+    def get_averaged_current(self, z, width=None):
+        self.calculate_ldos(width)
+        nz = self.ldos.shape[2]
+
+        # Find grid point:
+        n = z / self.cell[2, 2] * nz
+        dn = n - np.floor(n)
+        n = int(n) % nz
+        print n,dn
+
+        # Average and do linear interpolation:
+        return ((1 - dn) * self.ldos[:, :, n].mean() +
+                dn *       self.ldos[:, :, (n + 1) % nz].mean())
+    
+    def scan(self, current, z=None, width=None):
+        self.calculate_ldos(width)
+
+        L = self.cell[2, 2]
+        if z is None:
+            z = L / 2
+
+        nz = self.ldos.shape[2]
+        n = int(round(z / L * nz)) % nz
+        h = L / nz
+
+        ldos = self.ldos.reshape((-1, nz))
+
+        heights = np.empty(ldos.shape[0])
+        for i, a in enumerate(ldos):
+            heights[i], z, n = find_height(a, current, z, n, nz, h)
+
+        heights.shape = self.ldos.shape[:2]
+        return heights
+    
+    def linescan(self, current, p1, p2, npoints=None, z=None, width=None):
+        self.calculate_ldos(width)
+
+        L = self.cell[2, 2]
+        if z is None:
+            z = L / 2
+
+        nz = self.ldos.shape[2]
+        n = int(round(z / L * nz)) % nz
+        h = L / nz
+        ldos = self.ldos.reshape((-1, nz))
+
+        p1 = np.asarray(p1)
+        p2 = np.asarray(p2)
+        d = p2 - p1
+        s = sqrt(np.dot(d, d))
+        
+        if npints == None:
+            npoints = int(3 * s / h + 2)
+
+        cell = self.cell[:2, :2]
+        shape = np.array(self.ldos.shape[:2], float)
+        M = cell.I
+        heights = np.empty(npoints)
+        for i in range(npoints):
+            p = p1 + i * d / (npoints - 1)
+            q = np.dot(M, p) * shape
+            qi = q.astype(int)
+            n0, n1 = qi
+            f = q - qi
+            g = 1 - f
+            a = (g[0] * g[0] * ldos[n0,     n1    ] +
+                 f[0] * g[0] * ldos[n0 + 1, n1    ] +
+                 g[0] * f[0] * ldos[n0,     n1 + 1] +
+                 f[0] * f[0] * ldos[n0 + 1, n1 + 1])
+            heights[i], z, n = find_height(a, current, z, n, nz, h)
+        return np.linspace(0, s, npoints), heights
+
+    def cube(self, filename, atoms=None):
+        pass
+
+
+def find_height(array, current, z, n, nz, h):
+    c1 = array[n]
+    sign = cmp(c1, current)
+    m = 0
+    while m < nz:
+        n = (n + sign) % nz
+        z += sign * h
+        c2 = array[n]
+        if cmp(c2, current) != sign:
+            break
+        c1 = c2
+        m += 1
+
+    if m == nz:
+        print z, n, nz, h, current, array
+        raise RuntimeError('Tip crash!')
+
+    return z - sign * h * (current - c2) / (c1 - c2), z, n
+
+                
+            
+        
+    
+        
diff --git a/ase/dft/wannier.py b/ase/dft/wannier.py
new file mode 100644
index 0000000..8e68794
--- /dev/null
+++ b/ase/dft/wannier.py
@@ -0,0 +1,799 @@
+""" Maximally localized Wannier Functions
+
+    Find the set of maximally localized Wannier functions
+    using the spread functional of Marzari and Vanderbilt
+    (PRB 56, 1997 page 12847). 
+"""
+from time import time
+from math import sqrt, pi
+from pickle import dump, load
+
+import numpy as np
+
+from ase.parallel import paropen
+from ase.calculators.dacapo import Dacapo
+from ase.dft.kpoints import get_monkhorst_pack_size_and_offset
+from ase.transport.tools import dagger, normalize
+
+dag = dagger
+
+
+def gram_schmidt(U):
+    """Orthonormalize columns of U according to the Gram-Schmidt procedure."""
+    for i, col in enumerate(U.T):
+        for col2 in U.T[:i]:
+            col -= col2 * np.dot(col2.conj(), col)
+        col /= np.linalg.norm(col)
+
+
+def gram_schmidt_single(U, n):
+    """Orthogonalize columns of U to column n"""
+    N = len(U.T)
+    v_n = U.T[n]
+    indices = range(N)
+    del indices[indices.index(n)]
+    for i in indices:
+        v_i = U.T[i]
+        v_i -=  v_n * np.dot(v_n.conj(), v_i)
+        
+
+def lowdin(U, S=None):
+    """Orthonormalize columns of U according to the Lowdin procedure.
+    
+    If the overlap matrix is know, it can be specified in S.
+    """
+    if S is None:
+        S = np.dot(dag(U), U)
+    eig, rot = np.linalg.eigh(S)
+    rot = np.dot(rot / np.sqrt(eig), dag(rot))
+    U[:] = np.dot(U, rot)
+
+
+def neighbor_k_search(k_c, G_c, kpt_kc, tol=1e-4):
+    # search for k1 (in kpt_kc) and k0 (in alldir), such that
+    # k1 - k - G + k0 = 0
+    alldir_dc = np.array([[0,0,0],[1,0,0],[0,1,0],[0,0,1],
+                           [1,1,0],[1,0,1],[0,1,1]], int)
+    for k0_c in alldir_dc:
+        for k1, k1_c in enumerate(kpt_kc):
+            if np.linalg.norm(k1_c - k_c - G_c + k0_c) < tol:
+                return k1, k0_c
+
+    print 'Wannier: Did not find matching kpoint for kpt=', k_c
+    print 'Probably non-uniform k-point grid'
+    raise NotImplementedError
+
+
+def calculate_weights(cell_cc):
+    """ Weights are used for non-cubic cells, see PRB **61**, 10040"""
+    alldirs_dc = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1],
+                           [1, 1, 0], [1, 0, 1], [0, 1, 1]], dtype=int)
+    g = np.dot(cell_cc, cell_cc.T)
+    # NOTE: Only first 3 of following 6 weights are presently used:
+    w = np.zeros(6)              
+    w[0] = g[0, 0] - g[0, 1] - g[0, 2]
+    w[1] = g[1, 1] - g[0, 1] - g[1, 2]
+    w[2] = g[2, 2] - g[0, 2] - g[1, 2]
+    w[3] = g[0, 1]
+    w[4] = g[0, 2]
+    w[5] = g[1, 2]
+    # Make sure that first 3 Gdir vectors are included - 
+    # these are used to calculate Wanniercenters.
+    Gdir_dc = alldirs_dc[:3]
+    weight_d = w[:3]
+    for d in range(3, 6):
+        if abs(w[d]) > 1e-5:
+            Gdir_dc = np.concatenate((Gdir_dc, alldirs_dc[d:d + 1]))
+            weight_d = np.concatenate((weight_d, w[d:d + 1]))
+    weight_d /= max(abs(weight_d))
+    return weight_d, Gdir_dc
+
+
+def random_orthogonal_matrix(dim, seed=None, real=False):
+    """Generate a random orthogonal matrix"""
+    if seed is not None:
+        np.random.seed(seed)
+
+    H = np.random.rand(dim, dim)
+    np.add(dag(H), H, H)
+    np.multiply(.5, H, H)
+
+    if real:
+        gram_schmidt(H)
+        return H
+    else: 
+        val, vec = np.linalg.eig(H)
+        return np.dot(vec * np.exp(1.j * val), dag(vec))
+
+
+def steepest_descent(func, step=.005, tolerance=1e-6, **kwargs):
+    fvalueold = 0.
+    fvalue = fvalueold + 10
+    count=0
+    while abs((fvalue - fvalueold) / fvalue) > tolerance:
+        fvalueold = fvalue
+        dF = func.get_gradients()
+        func.step(dF * step, **kwargs)
+        fvalue = func.get_functional_value()
+        count += 1
+        print 'SteepestDescent: iter=%s, value=%s' % (count, fvalue)
+
+
+def md_min(func, step=.25, tolerance=1e-6, verbose=False, **kwargs):
+    if verbose:
+        print 'Localize with step =', step, 'and tolerance =', tolerance
+        t = -time()
+    fvalueold = 0.
+    fvalue = fvalueold + 10
+    count = 0
+    V = np.zeros(func.get_gradients().shape, dtype=complex)
+    while abs((fvalue - fvalueold) / fvalue) > tolerance:
+        fvalueold = fvalue
+        dF = func.get_gradients()
+        V *= (dF * V.conj()).real > 0
+        V += step * dF
+        func.step(V, **kwargs)
+        fvalue = func.get_functional_value()
+        if fvalue < fvalueold:
+            step *= 0.5
+        count += 1
+        if verbose:
+            print 'MDmin: iter=%s, step=%s, value=%s' % (count, step, fvalue)
+    if verbose:
+        t += time()
+        print '%d iterations in %0.2f seconds (%0.2f ms/iter), endstep = %s' %(
+            count, t, t * 1000. / count, step)
+
+
+def rotation_from_projection2(proj_nw, fixed):
+    V_ni = proj_nw
+    Nb, Nw = proj_nw.shape
+    M = fixed
+    L = Nw - M
+    print 'M=%i, L=%i, Nb=%i, Nw=%i' % (M, L, Nb, Nw) 
+    U_ww = np.zeros((Nw, Nw), dtype=proj_nw.dtype)
+    c_ul = np.zeros((Nb-M, L), dtype=proj_nw.dtype)
+    for V_n in V_ni.T:
+        V_n /= np.linalg.norm(V_n)
+    
+    # Find EDF
+    P_ui = V_ni[M:].copy()
+    la = np.linalg
+    for l in range(L):
+        norm_list = np.array([la.norm(v) for v in P_ui.T])
+        perm_list = np.argsort(-norm_list)
+        P_ui = P_ui[:, perm_list].copy()    # largest norm to the left
+        P_ui[:, 0] /= la.norm(P_ui[:, 0])   # normalize
+        c_ul[:, l] = P_ui[:, 0]             # save normalized EDF
+        gram_schmidt_single(P_ui, 0)        # ortho remain. to this EDF
+        P_ui = P_ui[:, 1:].copy()           # remove this EDF
+
+    U_ww[:M] = V_ni[:M, :]
+    U_ww[M:] = np.dot(c_ul.T.conj(), V_ni[M:])
+    gram_schmidt(U_ww)
+    return U_ww, c_ul
+
+
+def rotation_from_projection(proj_nw, fixed, ortho=True):
+    """Determine rotation and coefficient matrices from projections
+    
+    proj_nw = <psi_n|p_w>
+    psi_n: eigenstates
+    p_w: localized function
+    
+    Nb (n) = Number of bands
+    Nw (w) = Number of wannier functions
+    M  (f) = Number of fixed states
+    L  (l) = Number of extra degrees of freedom
+    U  (u) = Number of non-fixed states
+    """
+
+    Nb, Nw = proj_nw.shape
+    M = fixed
+    L = Nw - M
+
+    U_ww = np.empty((Nw, Nw), dtype=proj_nw.dtype)
+    U_ww[:M] = proj_nw[:M]
+
+    if L > 0:
+        proj_uw = proj_nw[M:]
+        eig_w, C_ww = np.linalg.eigh(np.dot(dag(proj_uw), proj_uw))
+        C_ul = np.dot(proj_uw, C_ww[:, np.argsort(-eig_w.real)[:L]])
+        #eig_u, C_uu = np.linalg.eigh(np.dot(proj_uw, dag(proj_uw)))
+        #C_ul = C_uu[:, np.argsort(-eig_u.real)[:L]]
+
+        U_ww[M:] = np.dot(dag(C_ul), proj_uw)
+    else:
+        C_ul = np.empty((Nb - M, 0))
+
+    normalize(C_ul)
+    if ortho:
+        lowdin(U_ww)
+    else:
+        normalize(U_ww)
+
+    return U_ww, C_ul
+
+
+class Wannier:
+    """Maximally localized Wannier Functions
+
+    Find the set of maximally localized Wannier functions using the
+    spread functional of Marzari and Vanderbilt (PRB 56, 1997 page
+    12847).
+    """
+
+    def __init__(self, nwannier, calc,
+                 file=None,
+                 nbands=None,
+                 fixedenergy=None,
+                 fixedstates=None,
+                 spin=0,
+                 initialwannier='random',
+                 seed=None,
+                 verbose=False):
+        """
+        Required arguments:
+
+          ``nwannier``: The number of Wannier functions you wish to construct.
+            This must be at least half the number of electrons in the system
+            and at most equal to the number of bands in the calculation.
+
+          ``calc``: A converged DFT calculator class.
+            If ``file`` arg. is not provided, the calculator *must* provide the
+            method ``get_wannier_localization_matrix``, and contain the
+            wavefunctions (save files with only the density is not enough).
+            If the localization matrix is read from file, this is not needed,
+            unless ``get_function`` or ``write_cube`` is called.
+          
+        Optional arguments:
+
+          ``nbands``: Bands to include in localization.
+            The number of bands considered by Wannier can be smaller than the
+            number of bands in the calculator. This is useful if the highest
+            bands of the DFT calculation are not well converged.
+
+          ``spin``: The spin channel to be considered.
+            The Wannier code treats each spin channel independently.
+
+          ``fixedenergy`` / ``fixedstates``: Fixed part of Heilbert space.
+            Determine the fixed part of Hilbert space by either a maximal
+            energy *or* a number of bands (possibly a list for multiple
+            k-points).
+            Default is None meaning that the number of fixed states is equated
+            to ``nwannier``.
+
+          ``file``: Read localization and rotation matrices from this file.
+
+          ``initialwannier``: Initial guess for Wannier rotation matrix.
+            Can be 'bloch' to start from the Bloch states, 'random' to be
+            randomized, or a list passed to calc.get_initial_wannier.
+
+          ``seed``: Seed for random ``initialwannier``.
+
+          ``verbose``: True / False level of verbosity.
+          """
+        # Bloch phase sign convention
+        sign = -1
+        classname = calc.__class__.__name__
+        if classname in ['Dacapo', 'Jacapo']:
+            print 'Using ' + classname
+            sign = +1
+            
+        self.nwannier = nwannier
+        self.calc = calc
+        self.spin = spin
+        self.verbose = verbose
+        self.kpt_kc = calc.get_ibz_k_points()
+        assert len(calc.get_bz_k_points()) == len(self.kpt_kc)
+        self.kptgrid = get_monkhorst_pack_size_and_offset(self.kpt_kc)[0]
+        self.kpt_kc *= sign
+
+        self.Nk = len(self.kpt_kc)
+        self.unitcell_cc = calc.get_atoms().get_cell()
+        self.largeunitcell_cc = (self.unitcell_cc.T * self.kptgrid).T
+        self.weight_d, self.Gdir_dc = calculate_weights(self.largeunitcell_cc)
+        self.Ndir = len(self.weight_d) # Number of directions
+
+        if nbands is not None:
+            self.nbands = nbands
+        else:
+            self.nbands = calc.get_number_of_bands()
+        if fixedenergy is None:
+            if fixedstates is None:
+                self.fixedstates_k = np.array([nwannier] * self.Nk, int)
+            else:
+                if type(fixedstates) is int:
+                    fixedstates = [fixedstates] * self.Nk
+                self.fixedstates_k = np.array(fixedstates, int)
+        else:
+            # Setting number of fixed states and EDF from specified energy.
+            # All states below this energy (relative to Fermi level) are fixed.
+            fixedenergy += calc.get_fermi_level()
+            print fixedenergy
+            self.fixedstates_k = np.array(
+                [calc.get_eigenvalues(k, spin).searchsorted(fixedenergy)
+                 for k in range(self.Nk)], int)
+        self.edf_k = self.nwannier - self.fixedstates_k
+        if verbose:
+            print 'Wannier: Fixed states            : %s' % self.fixedstates_k
+            print 'Wannier: Extra degrees of freedom: %s' % self.edf_k
+
+        # Set the list of neighboring k-points k1, and the "wrapping" k0,
+        # such that k1 - k - G + k0 = 0
+        #
+        # Example: kpoints = (-0.375,-0.125,0.125,0.375), dir=0
+        # G = [0.25,0,0]
+        # k=0.375, k1= -0.375 : -0.375-0.375-0.25 => k0=[1,0,0]
+        #
+        # For a gamma point calculation k1 = k = 0,  k0 = [1,0,0] for dir=0
+        if self.Nk == 1:
+            self.kklst_dk = np.zeros((self.Ndir, 1), int)
+            k0_dkc = self.Gdir_dc.reshape(-1, 1, 3)
+        else:
+            self.kklst_dk = np.empty((self.Ndir, self.Nk), int)
+            k0_dkc = np.empty((self.Ndir, self.Nk, 3), int)
+
+            # Distance between kpoints
+            kdist_c = np.empty(3)
+            for c in range(3):
+                # make a sorted list of the kpoint values in this direction
+                slist = np.argsort(self.kpt_kc[:, c], kind='mergesort')
+                skpoints_kc = np.take(self.kpt_kc, slist, axis=0)
+                kdist_c[c] = max([skpoints_kc[n + 1, c] - skpoints_kc[n, c]
+                                  for n in range(self.Nk - 1)])               
+
+            for d, Gdir_c in enumerate(self.Gdir_dc):
+                for k, k_c in enumerate(self.kpt_kc):
+                    # setup dist vector to next kpoint
+                    G_c = np.where(Gdir_c > 0, kdist_c, 0)
+                    if max(G_c) < 1e-4:
+                        self.kklst_dk[d, k] = k
+                        k0_dkc[d, k] = Gdir_c
+                    else:
+                        self.kklst_dk[d, k], k0_dkc[d, k] = \
+                                       neighbor_k_search(k_c, G_c, self.kpt_kc)
+
+        # Set the inverse list of neighboring k-points
+        self.invkklst_dk = np.empty((self.Ndir, self.Nk), int)
+        for d in range(self.Ndir):
+            for k1 in range(self.Nk):
+                self.invkklst_dk[d, k1] = self.kklst_dk[d].tolist().index(k1)
+
+        Nw = self.nwannier
+        Nb = self.nbands
+        self.Z_dkww = np.empty((self.Ndir, self.Nk, Nw, Nw), complex)
+        self.V_knw = np.zeros((self.Nk, Nb, Nw), complex)
+        if file is None:
+            self.Z_dknn = np.empty((self.Ndir, self.Nk, Nb, Nb), complex)
+            for d, dirG in enumerate(self.Gdir_dc):
+                for k in range(self.Nk):
+                    k1 = self.kklst_dk[d, k]
+                    k0_c = k0_dkc[d, k]
+                    self.Z_dknn[d, k] = calc.get_wannier_localization_matrix(
+                        nbands=Nb, dirG=dirG, kpoint=k, nextkpoint=k1,
+                        G_I=k0_c, spin=self.spin)
+        self.initialize(file=file, initialwannier=initialwannier, seed=seed)
+
+    def initialize(self, file=None, initialwannier='random', seed=None):
+        """Re-initialize current rotation matrix.
+
+        Keywords are identical to those of the constructor.
+        """
+        Nw = self.nwannier
+        Nb = self.nbands
+
+        if file is not None:
+            self.Z_dknn, self.U_kww, self.C_kul = load(paropen(file))
+        elif initialwannier == 'bloch':
+            # Set U and C to pick the lowest Bloch states
+            self.U_kww = np.zeros((self.Nk, Nw, Nw), complex)
+            self.C_kul = []
+            for U, M, L in zip(self.U_kww, self.fixedstates_k, self.edf_k):
+                U[:] = np.identity(Nw, complex)
+                if L > 0:
+                    self.C_kul.append(
+                        np.identity(Nb - M, complex)[:, :L])
+                else:
+                    self.C_kul.append([])
+        elif initialwannier == 'random':
+            # Set U and C to random (orthogonal) matrices
+            self.U_kww = np.zeros((self.Nk, Nw, Nw), complex)
+            self.C_kul = []
+            for U, M, L in zip(self.U_kww, self.fixedstates_k, self.edf_k):
+                U[:] = random_orthogonal_matrix(Nw, seed, real=False)
+                if L > 0:
+                    self.C_kul.append(random_orthogonal_matrix(
+                        Nb - M, seed=seed, real=False)[:, :L])
+                else:
+                    self.C_kul.append(np.array([]))        
+        else:
+            # Use initial guess to determine U and C
+            self.C_kul, self.U_kww = self.calc.initial_wannier(
+                initialwannier, self.kptgrid, self.fixedstates_k,
+                self.edf_k, self.spin, self.nbands)
+        self.update()
+
+    def save(self, file):
+        """Save information on localization and rotation matrices to file."""
+        dump((self.Z_dknn, self.U_kww, self.C_kul), paropen(file, 'w'))
+
+    def update(self):
+        # Update large rotation matrix V (from rotation U and coeff C)
+        for k, M in enumerate(self.fixedstates_k):
+            self.V_knw[k, :M] = self.U_kww[k, :M]
+            if M < self.nwannier:
+                self.V_knw[k, M:] = np.dot(self.C_kul[k], self.U_kww[k, M:])
+            # else: self.V_knw[k, M:] = 0.0
+
+        # Calculate the Zk matrix from the large rotation matrix:
+        # Zk = V^d[k] Zbloch V[k1]
+        for d in range(self.Ndir):
+            for k in range(self.Nk):
+                k1 = self.kklst_dk[d, k]
+                self.Z_dkww[d, k] = np.dot(dag(self.V_knw[k]), np.dot(
+                    self.Z_dknn[d, k], self.V_knw[k1]))
+
+        # Update the new Z matrix
+        self.Z_dww = self.Z_dkww.sum(axis=1) / self.Nk
+
+    def get_centers(self, scaled=False):
+        """Calculate the Wannier centers
+
+        ::
+        
+          pos =  L / 2pi * phase(diag(Z))
+        """
+        coord_wc = np.angle(self.Z_dww[:3].diagonal(0, 1, 2)).T / (2 * pi) % 1
+        if not scaled:
+            coord_wc = np.dot(coord_wc, self.largeunitcell_cc)
+        return coord_wc
+
+    def get_radii(self):
+        """Calculate the spread of the Wannier functions.
+
+        ::
+          
+                        --  /  L  \ 2       2
+          radius**2 = - >   | --- |   ln |Z| 
+                        --d \ 2pi /
+        """
+        r2 = -np.dot(self.largeunitcell_cc.diagonal()**2 / (2 * pi)**2,
+                     np.log(abs(self.Z_dww[:3].diagonal(0, 1, 2))**2))
+        return np.sqrt(r2)
+
+    def get_spectral_weight(self, w):
+        return abs(self.V_knw[:, :, w])**2 / self.Nk
+
+    def get_pdos(self, w, energies, width):
+        """Projected density of states (PDOS).
+
+        Returns the (PDOS) for Wannier function ``w``. The calculation
+        is performed over the energy grid specified in energies. The
+        PDOS is produced as a sum of Gaussians centered at the points
+        of the energy grid and with the specified width.
+        """
+        spec_kn = self.get_spectral_weight(w)
+        dos = np.zeros(len(energies))
+        for k, spec_n in enumerate(spec_kn):
+            eig_n = self.calc.get_eigenvalues(k=kpt, s=self.spin)
+            for weight, eig in zip(spec_n, eig):
+                # Add gaussian centered at the eigenvalue
+                x = ((energies - center) / width)**2
+                dos += weight * np.exp(-x.clip(0., 40.)) / (sqrt(pi) * width)
+        return dos
+
+    def max_spread(self, directions=[0, 1, 2]):
+        """Returns the index of the most delocalized Wannier function
+        together with the value of the spread functional"""
+        d = np.zeros(self.nwannier)
+        for dir in directions:
+            d[dir] = np.abs(self.Z_dww[dir].diagonal())**2 *self.weight_d[dir]
+        index = np.argsort(d)[0]
+        print 'Index:', index
+        print 'Spread:', d[index]           
+
+    def translate(self, w, R):
+        """Translate the w'th Wannier function
+
+        The distance vector R = [n1, n2, n3], is in units of the basis
+        vectors of the small cell.
+        """
+        for kpt_c, U_ww in zip(self.kpt_kc, self.U_kww):
+            U_ww[:, w] *= np.exp(2.j * pi * np.dot(np.array(R), kpt_c))
+        self.update()
+
+    def translate_to_cell(self, w, cell):
+        """Translate the w'th Wannier function to specified cell"""
+        scaled_c = np.angle(self.Z_dww[:3, w, w]) * self.kptgrid / (2 * pi)
+        trans = np.array(cell) - np.floor(scaled_c)
+        self.translate(w, trans)
+
+    def translate_all_to_cell(self, cell=[0, 0, 0]):
+        """Translate all Wannier functions to specified cell.
+
+        Move all Wannier orbitals to a specific unit cell.  There
+        exists an arbitrariness in the positions of the Wannier
+        orbitals relative to the unit cell. This method can move all
+        orbitals to the unit cell specified by ``cell``.  For a
+        `\Gamma`-point calculation, this has no effect. For a
+        **k**-point calculation the periodicity of the orbitals are
+        given by the large unit cell defined by repeating the original
+        unitcell by the number of **k**-points in each direction.  In
+        this case it is usefull to move the orbitals away from the
+        boundaries of the large cell before plotting them. For a bulk
+        calculation with, say 10x10x10 **k** points, one could move
+        the orbitals to the cell [2,2,2].  In this way the pbc
+        boundary conditions will not be noticed.
+        """
+        scaled_wc = np.angle(self.Z_dww[:3].diagonal(0, 1, 2)).T  * \
+                    self.kptgrid / (2 * pi)
+        trans_wc =  np.array(cell)[None] - np.floor(scaled_wc)
+        for kpt_c, U_ww in zip(self.kpt_kc, self.U_kww):
+            U_ww *= np.exp(2.j * pi * np.dot(trans_wc, kpt_c))
+        self.update()
+
+    def distances(self, R):
+        Nw = self.nwannier
+        cen = self.get_centers()
+        r1 = cen.repeat(Nw, axis=0).reshape(Nw, Nw, 3)
+        r2 = cen.copy()
+        for i in range(3):
+            r2 += self.unitcell_cc[i] * R[i]
+
+        r2 = np.swapaxes(r2.repeat(Nw, axis=0).reshape(Nw, Nw, 3), 0, 1)
+        return np.sqrt(np.sum((r1 - r2)**2, axis=-1))
+
+    def get_hopping(self, R):
+        """Returns the matrix H(R)_nm=<0,n|H|R,m>.
+
+        ::
+        
+                                1   _   -ik.R 
+          H(R) = <0,n|H|R,m> = --- >_  e      H(k)
+                                Nk  k         
+
+        where R is the cell-distance (in units of the basis vectors of
+        the small cell) and n,m are indices of the Wannier functions.
+        """
+        H_ww = np.zeros([self.nwannier, self.nwannier], complex)
+        for k, kpt_c in enumerate(self.kpt_kc):
+            phase = np.exp(-2.j * pi * np.dot(np.array(R), kpt_c))
+            H_ww += self.get_hamiltonian(k) * phase
+        return H_ww / self.Nk
+
+    def get_hamiltonian(self, k=0):
+        """Get Hamiltonian at existing k-vector of index k
+
+        ::
+        
+                  dag
+          H(k) = V    diag(eps )  V
+                  k           k    k
+        """
+        eps_n = self.calc.get_eigenvalues(kpt=k, spin=self.spin)[:self.nbands]
+        return np.dot(dag(self.V_knw[k]) * eps_n, self.V_knw[k])
+
+    def get_hamiltonian_kpoint(self, kpt_c):
+        """Get Hamiltonian at some new arbitrary k-vector
+
+        ::
+        
+                  _   ik.R 
+          H(k) = >_  e     H(R)
+                  R         
+
+        Warning: This method moves all Wannier functions to cell (0, 0, 0)
+        """
+        if self.verbose:
+            print 'Translating all Wannier functions to cell (0, 0, 0)'
+        self.translate_all_to_cell()
+        max = (self.kptgrid - 1) / 2
+        N1, N2, N3 = max
+        Hk = np.zeros([self.nwannier, self.nwannier], complex)
+        for n1 in xrange(-N1, N1 + 1):
+            for n2 in xrange(-N2, N2 + 1):
+                for n3 in xrange(-N3, N3 + 1):
+                    R = np.array([n1, n2, n3], float)
+                    hop_ww = self.get_hopping(R)
+                    phase = np.exp(+2.j * pi * np.dot(R, kpt_c))
+                    Hk += hop_ww * phase
+        return Hk
+
+    def get_function(self, index, repeat=None):
+        """Get Wannier function on grid.
+
+        Returns an array with the funcion values of the indicated Wannier
+        function on a grid with the size of the *repeated* unit cell.
+       
+        For a calculation using **k**-points the relevant unit cell for
+        eg. visualization of the Wannier orbitals is not the original unit
+        cell, but rather a larger unit cell defined by repeating the
+        original unit cell by the number of **k**-points in each direction.
+        Note that for a `\Gamma`-point calculation the large unit cell
+        coinsides with the original unit cell.
+        The large unitcell also defines the periodicity of the Wannier
+        orbitals.
+
+        ``index`` can be either a single WF or a coordinate vector in terms
+        of the WFs.
+        """
+
+        # Default size of plotting cell is the one corresponding to k-points.
+        if repeat is None:
+            repeat = self.kptgrid
+        N1, N2, N3 = repeat
+
+        dim = self.calc.get_number_of_grid_points()
+        largedim = dim * [N1, N2, N3]
+        
+        wanniergrid = np.zeros(largedim, dtype=complex)
+        for k, kpt_c in enumerate(self.kpt_kc):
+            # The coordinate vector of wannier functions
+            if type(index) == int:
+                vec_n = self.V_knw[k, :, index]
+            else:   
+                vec_n = np.dot(self.V_knw[k], index)
+
+            wan_G = np.zeros(dim, complex)
+            for n, coeff in enumerate(vec_n):
+                wan_G += coeff * self.calc.get_pseudo_wave_function(
+                    n, k, self.spin, pad=True)
+
+            # Distribute the small wavefunction over large cell:
+            for n1 in xrange(N1):
+                for n2 in xrange(N2):
+                    for n3 in xrange(N3): # sign?
+                        e = np.exp(-2.j * pi * np.dot([n1, n2, n3], kpt_c))
+                        wanniergrid[n1 * dim[0]:(n1 + 1) * dim[0],
+                                    n2 * dim[1]:(n2 + 1) * dim[1],
+                                    n3 * dim[2]:(n3 + 1) * dim[2]] += e * wan_G
+
+        # Normalization
+        wanniergrid /= np.sqrt(self.Nk)
+        return wanniergrid
+
+    def write_cube(self, index, fname, repeat=None, real=True):
+        """Dump specified Wannier function to a cube file"""
+        from ase.io.cube import write_cube
+
+        # Default size of plotting cell is the one corresponding to k-points.
+        if repeat is None:
+            repeat = self.kptgrid
+        atoms = self.calc.get_atoms() * repeat
+        func = self.get_function(index, repeat)
+
+        # Handle separation of complex wave into real parts
+        if real:
+            if self.Nk == 1:
+                func *= np.exp(-1.j * np.angle(func.max()))
+                if 0: assert max(abs(func.imag).flat) < 1e-4
+                func = func.real
+            else:
+                func = abs(func)
+        else:
+            phase_fname = fname.split('.')
+            phase_fname.insert(1, 'phase')
+            phase_fname = '.'.join(phase_fname)
+            write_cube(phase_fname, atoms, data=np.angle(func))
+            func = abs(func)
+
+        write_cube(fname, atoms, data=func)
+
+    def localize(self, step=0.25, tolerance=1e-08,
+                 updaterot=True, updatecoeff=True):
+        """Optimize rotation to give maximal localization"""
+        md_min(self, step, tolerance, verbose=self.verbose,
+               updaterot=updaterot, updatecoeff=updatecoeff)
+
+    def get_functional_value(self): 
+        """Calculate the value of the spread functional.
+
+        ::
+
+          Tr[|ZI|^2]=sum(I)sum(n) w_i|Z_(i)_nn|^2,
+
+        where w_i are weights."""
+        a_d = np.sum(np.abs(self.Z_dww.diagonal(0, 1, 2))**2, axis=1)
+        return np.dot(a_d, self.weight_d).real
+
+    def get_gradients(self):
+        # Determine gradient of the spread functional.
+        # 
+        # The gradient for a rotation A_kij is::
+        # 
+        #    dU = dRho/dA_{k,i,j} = sum(I) sum(k')
+        #            + Z_jj Z_kk',ij^* - Z_ii Z_k'k,ij^*
+        #            - Z_ii^* Z_kk',ji + Z_jj^* Z_k'k,ji
+        # 
+        # The gradient for a change of coefficients is::
+        # 
+        #   dRho/da^*_{k,i,j} = sum(I) [[(Z_0)_{k} V_{k'} diag(Z^*) +
+        #                                (Z_0_{k''})^d V_{k''} diag(Z)] *
+        #                                U_k^d]_{N+i,N+j}
+        # 
+        # where diag(Z) is a square,diagonal matrix with Z_nn in the diagonal, 
+        # k' = k + dk and k = k'' + dk.
+        # 
+        # The extra degrees of freedom chould be kept orthonormal to the fixed
+        # space, thus we introduce lagrange multipliers, and minimize instead::
+        # 
+        #     Rho_L=Rho- sum_{k,n,m} lambda_{k,nm} <c_{kn}|c_{km}>
+        # 
+        # for this reason the coefficient gradients should be multiplied
+        # by (1 - c c^d).
+        
+        Nb = self.nbands
+        Nw = self.nwannier
+        
+        dU = []
+        dC = []
+        for k in xrange(self.Nk):
+            M = self.fixedstates_k[k]
+            L = self.edf_k[k]
+            U_ww = self.U_kww[k]
+            C_ul = self.C_kul[k]
+            Utemp_ww = np.zeros((Nw, Nw), complex)
+            Ctemp_nw = np.zeros((Nb, Nw), complex)
+
+            for d, weight in enumerate(self.weight_d):
+                if abs(weight) < 1.0e-6:
+                    continue
+
+                Z_knn = self.Z_dknn[d]
+                diagZ_w = self.Z_dww[d].diagonal()
+                Zii_ww = np.repeat(diagZ_w, Nw).reshape(Nw, Nw)
+                k1 = self.kklst_dk[d, k]
+                k2 = self.invkklst_dk[d, k]
+                V_knw = self.V_knw
+                Z_kww = self.Z_dkww[d]
+                
+                if L > 0:
+                    Ctemp_nw += weight * np.dot(
+                        np.dot(Z_knn[k], V_knw[k1]) * diagZ_w.conj() +
+                        np.dot(dag(Z_knn[k2]), V_knw[k2]) * diagZ_w,
+                        dag(U_ww))
+
+                temp = Zii_ww.T * Z_kww[k].conj() - Zii_ww * Z_kww[k2].conj()
+                Utemp_ww += weight * (temp - dag(temp))
+            dU.append(Utemp_ww.ravel())
+            if L > 0:
+                # Ctemp now has same dimension as V, the gradient is in the
+                # lower-right (Nb-M) x L block
+                Ctemp_ul = Ctemp_nw[M:, M:]
+                G_ul = Ctemp_ul - np.dot(np.dot(C_ul, dag(C_ul)), Ctemp_ul)
+                dC.append(G_ul.ravel())
+
+        return np.concatenate(dU + dC)
+                        
+    def step(self, dX, updaterot=True, updatecoeff=True):
+        # dX is (A, dC) where U->Uexp(-A) and C->C+dC
+        Nw = self.nwannier
+        Nk = self.Nk
+        M_k = self.fixedstates_k
+        L_k = self.edf_k
+        if updaterot:
+            A_kww = dX[:Nk * Nw**2].reshape(Nk, Nw, Nw)
+            for U, A in zip(self.U_kww, A_kww):
+                H = -1.j * A.conj()
+                epsilon, Z = np.linalg.eigh(H)
+                # Z contains the eigenvectors as COLUMNS.
+                # Since H = iA, dU = exp(-A) = exp(iH) = ZDZ^d
+                dU = np.dot(Z * np.exp(1.j * epsilon), dag(Z))
+                if U.dtype == float:
+                    U[:] = np.dot(U, dU).real
+                else:
+                    U[:] = np.dot(U, dU)
+
+        if updatecoeff:
+            start = 0
+            for C, unocc, L in zip(self.C_kul, self.nbands - M_k, L_k):
+                if L == 0 or unocc == 0:
+                    continue
+                Ncoeff = L * unocc
+                deltaC = dX[Nk * Nw**2 + start: Nk * Nw**2 + start + Ncoeff]
+                C += deltaC.reshape(unocc, L)
+                gram_schmidt(C)
+                start += Ncoeff
+
+        self.update()
diff --git a/ase/dimer.py b/ase/dimer.py
new file mode 100644
index 0000000..a5d4cd7
--- /dev/null
+++ b/ase/dimer.py
@@ -0,0 +1,1152 @@
+"""Minimum mode follower for finding saddle points in an unbiased way.
+
+There is, currently, only one implemented method: The Dimer method.
+
+"""
+
+import sys
+import time
+import warnings
+
+import numpy as np
+
+from ase.optimize.optimize import Optimizer
+from math import cos, sin, atan, tan, degrees, pi, sqrt
+from ase.parallel import rank, size, world
+from ase.calculators.singlepoint import SinglePointCalculator
+
+# Handy vector methods
+norm = np.linalg.norm
+def normalize(vector):
+    """Create a unit vector along *vector*"""
+    return vector / norm(vector)
+def parallel_vector(vector, base):
+    """Extract the components of *vector* that are parallel to *base*"""
+    return np.vdot(vector, base) * base
+def perpendicular_vector(vector, base):
+    """Remove the components of *vector* that are parallel to *base*"""
+    return vector - parallel_vector(vector, base)
+def rotate_vectors(v1i, v2i, angle):
+    """Rotate vectors *v1i* and *v2i* by *angle*"""
+    cAng = cos(angle)
+    sAng = sin(angle)
+    v1o = v1i * cAng + v2i * sAng
+    v2o = v2i * cAng - v1i * sAng
+    # Ensure the length of the input and output vectors is equal
+    return normalize(v1o) * norm(v1i), normalize(v2o) * norm(v2i)
+
+class DimerEigenmodeSearch:
+    """An implementation of the Dimer's minimum eigenvalue mode search.
+
+    This class implements the rotational part of the dimer saddle point
+    searching method.
+
+    Parameters:
+
+    atoms : MinModeAtoms object
+        MinModeAtoms is an extension to the Atoms object, which includes
+        information about the lowest eigenvalue mode.
+    control : DimerControl object
+        Contains the parameters necessary for the eigenmode search.
+        If no control object is supplied a default DimerControl
+        will be created and used.
+    basis : list of xyz-values
+        Eigenmode. Must be an ndarray of shape (n, 3).
+        It is possible to constrain the eigenmodes to be orthogonal
+        to this given eigenmode.
+
+    Notes:
+
+    The code is inspired, with permission, by code written by the Henkelman
+    group, which can be found at http://theory.cm.utexas.edu/vtsttools/code/
+
+    References:
+
+    .. [1] Henkelman and Jonsson, JCP 111, 7010 (1999)
+    .. [2] Olsen, Kroes, Henkelman, Arnaldsson, and Jonsson, JCP 121,
+           9776 (2004).
+    .. [3] Heyden, Bell, and Keil, JCP 123, 224101 (2005).
+    .. [4] Kastner and Sherwood, JCP 128, 014106 (2008).
+
+    """
+    def __init__(self, atoms, control=None, eigenmode=None, basis=None, \
+                 **kwargs):
+        if hasattr(atoms, 'get_eigenmode'):
+            self.atoms = atoms
+        else:
+            e = 'The atoms object must be a MinModeAtoms object'
+            raise TypeError(e)
+        self.basis = basis
+
+        if eigenmode is None:
+            self.eigenmode = self.atoms.get_eigenmode()
+        else:
+            self.eigenmode = eigenmode
+
+        if control is None:
+            self.control = DimerControl(**kwargs)
+            w = 'Missing control object in ' + self.__class__.__name__ + \
+                '. Using default: DimerControl()'
+            warnings.warn(w, UserWarning)
+            if self.control.logfile is not None:
+                self.control.logfile.write('DIM:WARN: ' + w + '\n')
+                self.control.logfile.flush()
+        else:
+            self.control = control
+            # kwargs must be empty if a control object is supplied
+            for key in kwargs:
+                e = '__init__() got an unexpected keyword argument \'%s\'' % \
+                    (key)
+                raise TypeError(e)
+
+        self.dR = self.control.get_parameter('dimer_separation')
+        self.logfile = self.control.get_logfile()
+
+    def converge_to_eigenmode(self):
+        """Perform an eigenmode search."""
+        self.set_up_for_eigenmode_search()
+        stoprot = False
+
+        # Load the relevant parameters from control
+        f_rot_min = self.control.get_parameter('f_rot_min')
+        f_rot_max = self.control.get_parameter('f_rot_max')
+        trial_angle = self.control.get_parameter('trial_angle')
+        max_num_rot = self.control.get_parameter('max_num_rot')
+        extrapolate = self.control.get_parameter('extrapolate_forces')
+
+        while not stoprot:
+            if self.forces1E == None:
+                self.update_virtual_forces()
+            else:
+                self.update_virtual_forces(extrapolated_forces = True)
+            self.forces1A = self.forces1
+            self.update_curvature()
+            f_rot_A = self.get_rotational_force()
+
+            # Pre rotation stop criteria
+            if norm(f_rot_A) <= f_rot_min:
+                self.log(f_rot_A, None)
+                stoprot = True
+            else:
+                n_A = self.eigenmode
+                rot_unit_A = normalize(f_rot_A)
+
+                # Get the curvature and its derivative
+                c0 = self.get_curvature()
+                c0d = np.vdot((self.forces2 - self.forces1), rot_unit_A) / \
+                      self.dR
+
+                # Trial rotation (no need to store the curvature)
+                # NYI variable trial angles from [3]
+                n_B, rot_unit_B = rotate_vectors(n_A, rot_unit_A, trial_angle)
+                self.eigenmode = n_B
+                self.update_virtual_forces()
+                self.forces1B = self.forces1
+
+                # Get the curvature's derivative
+                c1d = np.vdot((self.forces2 - self.forces1), rot_unit_B) / \
+                      self.dR
+
+                # Calculate the Fourier coefficients
+                a1 = c0d * cos(2 * trial_angle) - c1d / \
+                     (2 * sin(2 * trial_angle))
+                b1 = 0.5 * c0d
+                a0 = 2 * (c0 - a1)
+
+                # Estimate the rotational angle
+                rotangle = atan(b1 / a1) / 2.0
+
+                # Make sure that you didn't find a maximum
+                cmin = a0 / 2.0 + a1 * cos(2 * rotangle) + \
+                       b1 * sin(2 * rotangle)
+                if c0 < cmin:
+                    rotangle += pi / 2.0
+
+                # Rotate into the (hopefully) lowest eigenmode
+                # NYI Conjugate gradient rotation
+                n_min, dummy = rotate_vectors(n_A, rot_unit_A, rotangle)
+                self.update_eigenmode(n_min)
+
+                # Store the curvature estimate instead of the old curvature
+                self.update_curvature(cmin)
+
+                self.log(f_rot_A, rotangle)
+
+                # Force extrapolation scheme from [4]
+                if extrapolate:
+                    self.forces1E = sin(trial_angle - rotangle) / \
+                        sin(trial_angle) * self.forces1A + sin(rotangle) / \
+                        sin(trial_angle) * self.forces1B + \
+                        (1 - cos(rotangle) - sin(rotangle) * \
+                        tan(trial_angle / 2.0)) * self.forces0
+                else:
+                    self.forces1E = None
+
+            # Post rotation stop criteria
+            if not stoprot:
+                if self.control.get_counter('rotcount') >= max_num_rot:
+                    stoprot = True
+                elif norm(f_rot_A) <= f_rot_max:
+                    stoprot = True
+
+    def log(self, f_rot_A, angle):
+        """Log each rotational step."""
+        # NYI Log for the trial angle
+        if self.logfile is not None:
+            if angle:
+                l = 'DIM:ROT: %7d %9d %9.4f %9.4f %9.4f\n' % \
+                    (self.control.get_counter('optcount'),
+                    self.control.get_counter('rotcount'),
+                    self.get_curvature(), degrees(angle), norm(f_rot_A))
+            else:
+                l = 'DIM:ROT: %7d %9d %9.4f %9s %9.4f\n' % \
+                    (self.control.get_counter('optcount'),
+                    self.control.get_counter('rotcount'),
+                    self.get_curvature(), '---------', norm(f_rot_A))
+            self.logfile.write(l)
+            self.logfile.flush()
+
+    def get_rotational_force(self):
+        """Calculate the rotational force that acts on the dimer."""
+        rot_force = perpendicular_vector((self.forces1 - self.forces2),
+                    self.eigenmode) / (2.0 * self.dR)
+        if self.basis is not None:
+            if len(self.basis) == len(self.atoms) and len(self.basis[0]) == \
+               3 and isinstance(self.basis[0][0], float):
+                rot_force = perpendicular_vector(rot_force, self.basis)
+            else:
+                for base in self.basis:
+                    rot_force = perpendicular_vector(rot_force, base)
+        return rot_force
+
+    def update_curvature(self, curv = None):
+        """Update the curvature in the MinModeAtoms object."""
+        if curv:
+            self.curvature = curv
+        else:
+            self.curvature = np.vdot((self.forces2 - self.forces1),
+                             self.eigenmode) / (2.0 * self.dR)
+
+    def update_eigenmode(self, eigenmode):
+        """Update the eigenmode in the MinModeAtoms object."""
+        self.eigenmode = eigenmode
+        self.update_virtual_positions()
+        self.control.increment_counter('rotcount')
+
+    def get_eigenmode(self):
+        """Returns the current eigenmode."""
+        return self.eigenmode
+
+    def get_curvature(self):
+        """Returns the curvature along the current eigenmode."""
+        return self.curvature
+
+    def get_control(self):
+        """Return the control object."""
+        return self.control
+
+    def update_center_forces(self):
+        """Get the forces at the center of the dimer."""
+        self.atoms.set_positions(self.pos0)
+        self.forces0 = self.atoms.get_forces(real = True)
+        self.energy0 = self.atoms.get_potential_energy()
+
+    def update_virtual_forces(self, extrapolated_forces = False):
+        """Get the forces at the endpoints of the dimer."""
+        self.update_virtual_positions()
+
+        # Estimate / Calculate the forces at pos1
+        if extrapolated_forces:
+            self.forces1 = self.forces1E.copy()
+        else:
+            self.forces1 = self.atoms.get_forces(real = True, pos = self.pos1)
+
+        # Estimate / Calculate the forces at pos2
+        if self.control.get_parameter('use_central_forces'):
+            self.forces2 = 2 * self.forces0 - self.forces1
+        else:
+            self.forces2 = self.atoms.get_forces(real = True, pos = self.pos2)
+
+    def update_virtual_positions(self):
+        """Update the end point positions."""
+        self.pos1 = self.pos0 + self.eigenmode * self.dR
+        self.pos2 = self.pos0 - self.eigenmode * self.dR
+
+    def set_up_for_eigenmode_search(self):
+        """Before eigenmode search, prepare for rotation."""
+        self.pos0 = self.atoms.get_positions()
+        self.update_center_forces()
+        self.update_virtual_positions()
+        self.control.reset_counter('rotcount')
+        self.forces1E = None
+
+    def set_up_for_optimization_step(self):
+        """At the end of rotation, prepare for displacement of the dimer."""
+        self.atoms.set_positions(self.pos0)
+        self.forces1E = None
+
+class MinModeControl:
+    """A parent class for controlling minimum mode saddle point searches.
+
+    Method specific control classes inherit this one. The only thing
+    inheriting classes need to implement are the log() method and
+    the *parameters* class variable with default values for ALL
+    parameters needed by the method in question.
+    When instantiating control classes default parameter values can
+    be overwritten.
+
+    """
+    parameters = {}
+    def __init__(self, logfile = '-', eigenmode_logfile=None, **kwargs):
+        # Overwrite the defaults with the input parameters given
+        for key in kwargs:
+            if not key in self.parameters.keys():
+                e = 'Invalid parameter >>%s<< with value >>%s<< in %s' % \
+                    (key, str(kwargs[key]), self.__class__.__name__)
+                raise ValueError(e)
+            else:
+                self.set_parameter(key, kwargs[key], log = False)
+
+        # Initialize the log files
+        self.initialize_logfiles(logfile, eigenmode_logfile)
+
+        # Initialize the counters
+        self.counters = {'forcecalls': 0, 'rotcount': 0, 'optcount': 0}
+
+        self.log()
+
+    def initialize_logfiles(self, logfile=None, eigenmode_logfile=None):
+        """Set up the log files."""
+        # Set up the regular logfile
+        if rank != 0:
+            logfile = None
+        elif isinstance(logfile, str):
+            if logfile == '-':
+                logfile = sys.stdout
+            else:
+                logfile = open(logfile, 'a')
+        self.logfile = logfile
+
+        # Set up the eigenmode logfile
+        if eigenmode_logfile:
+            if rank != 0:
+                eigenmode_logfile = None
+            elif isinstance(eigenmode_logfile, str):
+                if eigenmode_logfile == '-':
+                    eigenmode_logfile = sys.stdout
+                else:
+                    eigenmode_logfile = open(eigenmode_logfile, 'a')
+        self.eigenmode_logfile = eigenmode_logfile
+
+    def log(self, parameter=None):
+        """Log the parameters of the eigenmode search."""
+        pass
+
+    def set_parameter(self, parameter, value, log=True):
+        """Change a parameter's value."""
+        if not parameter in self.parameters.keys():
+            e = 'Invalid parameter >>%s<< with value >>%s<<' % \
+                (parameter, str(value))
+            raise ValueError(e)
+        self.parameters[parameter] = value
+        if log:
+            self.log(parameter)
+
+    def get_parameter(self, parameter):
+        """Returns the value of a parameter."""
+        if not parameter in self.parameters.keys():
+            e = 'Invalid parameter >>%s<<' % \
+                (parameter)
+            raise ValueError(e)
+        return self.parameters[parameter]
+
+    def get_logfile(self):
+        """Returns the log file."""
+        return self.logfile
+
+    def get_eigenmode_logfile(self):
+        """Returns the eigenmode log file."""
+        return self.eigenmode_logfile
+
+    def get_counter(self, counter):
+        """Returns a given counter."""
+        return self.counters[counter]
+
+    def increment_counter(self, counter):
+        """Increment a given counter."""
+        self.counters[counter] += 1
+
+    def reset_counter(self, counter):
+        """Reset a given counter."""
+        self.counters[counter] = 0
+
+    def reset_all_counters(self):
+        """Reset all counters."""
+        for key in self.counters.keys():
+            self.counters[key] = 0
+
+class DimerControl(MinModeControl):
+    """A class that takes care of the parameters needed for a Dimer search.
+
+    Parameters:
+
+    eigenmode_method: str
+        The name of the eigenmode search method.
+    f_rot_min: float
+        Size of the rotational force under which no rotation will be
+        performed.
+    f_rot_max: float
+        Size of the rotational force under which only one rotation will be
+        performed.
+    max_num_rot: int
+        Maximum number of rotations per optimizer step.
+    trial_angle: float
+        Trial angle for the finite difference estimate of the rotational
+        angle in radians.
+    trial_trans_step: float
+        Trial step size for the MinModeTranslate optimizer.
+    maximum_translation: float
+        Maximum step size and forced step size when the curvature is still
+        positive for the MinModeTranslate optimizer.
+    cg_translation: bool
+        Conjugate Gradient for the MinModeTranslate optimizer.
+    use_central_forces: bool
+        Only calculate the forces at one end of the dimer and extrapolate
+        the forces to the other.
+    dimer_separation: float
+        Separation of the dimer's images.
+    initial_eigenmode_method: str
+        How to construct the initial eigenmode of the dimer. If an eigenmode
+        is given when creating the MinModeAtoms object, this will be ignored.
+        Possible choices are: 'gauss' and 'displacement'
+    extrapolate_forces: bool
+        When more than one rotation is performed, an extrapolation scheme can
+        be used to reduce the number of force evaluations.
+    displacement_method: str
+        How to displace the atoms. Possible choices are 'gauss' and 'vector'.
+    gauss_std: float
+        The standard deviation of the gauss curve used when doing random
+        displacement.
+    order: int
+        How many lowest eigenmodes will be inverted.
+    mask: list of bool
+        Which atoms will be moved during displacement.
+    displacement_center: int or [float, float, float]
+        The center of displacement, nearby atoms will be displaced.
+    displacement_radius: float
+        When choosing which atoms to displace with the *displacement_center*
+        keyword, this decides how many nearby atoms to displace.
+    number_of_displacement_atoms: int
+        The amount of atoms near *displacement_center* to displace.
+
+    """
+    # Default parameters for the Dimer eigenmode search
+    parameters = {'eigenmode_method': 'dimer',
+                  'f_rot_min': 0.1,
+                  'f_rot_max': 1.00,
+                  'max_num_rot': 1,
+                  'trial_angle': pi / 4.0,
+                  'trial_trans_step': 0.001,
+                  'maximum_translation': 0.1,
+                  'cg_translation': True,
+                  'use_central_forces': True,
+                  'dimer_separation': 0.0001,
+                  'initial_eigenmode_method': 'gauss',
+                  'extrapolate_forces': False,
+                  'displacement_method': 'gauss',
+                  'gauss_std': 0.1,
+                  'order': 1,
+                  'mask': None, # NB mask should not be a "parameter"
+                  'displacement_center': None,
+                  'displacement_radius': None,
+                  'number_of_displacement_atoms': None}
+
+    # NB: Can maybe put this in EigenmodeSearch and MinModeControl
+    def log(self, parameter=None):
+        """Log the parameters of the eigenmode search."""
+        if self.logfile is not None:
+            if parameter is not None:
+                l = 'DIM:CONTROL: Updated Parameter: %s = %s\n' % (parameter,
+                     str(self.get_parameter(parameter)))
+            else:
+                l = 'MINMODE:METHOD: Dimer\n'
+                l += 'DIM:CONTROL: Search Parameters:\n'
+                l += 'DIM:CONTROL: ------------------\n'
+                for key in self.parameters:
+                    l += 'DIM:CONTROL: %s = %s\n' % (key,
+                         str(self.get_parameter(key)))
+                l += 'DIM:CONTROL: ------------------\n'
+                l += 'DIM:ROT: OPT-STEP ROT-STEP CURVATURE ROT-ANGLE ' + \
+                     'ROT-FORCE\n'
+            self.logfile.write(l)
+            self.logfile.flush()
+
+class MinModeAtoms:
+    """Wrapper for Atoms with information related to minimum mode searching.
+
+    Contains an Atoms object and pipes all unknown function calls to that
+    object.
+    Other information that is stored in this object are the estimate for
+    the lowest eigenvalue, *curvature*, and its corresponding eigenmode,
+    *eigenmode*. Furthermore, the original configuration of the Atoms
+    object is stored for use in multiple minimum mode searches.
+    The forces on the system are modified by inverting the component
+    along the eigenmode estimate. This eventually brings the system to
+    a saddle point.
+
+    Parameters:
+
+    atoms : Atoms object
+        A regular Atoms object
+    control : MinModeControl object
+        Contains the parameters necessary for the eigenmode search.
+        If no control object is supplied a default DimerControl
+        will be created and used.
+    mask: list of bool
+        Determines which atoms will be moved when calling displace()
+    random_seed: int
+        The seed used for the random number generator. Defaults to
+        modified version the current time.
+
+    References:
+
+    .. [1] Henkelman and Jonsson, JCP 111, 7010 (1999)
+    .. [2] Olsen, Kroes, Henkelman, Arnaldsson, and Jonsson, JCP 121,
+           9776 (2004).
+    .. [3] Heyden, Bell, and Keil, JCP 123, 224101 (2005).
+    .. [4] Kastner and Sherwood, JCP 128, 014106 (2008).
+
+    """
+    def __init__(self, atoms, control=None, eigenmodes=None, random_seed=None, **kwargs):
+        self.minmode_init = True
+        self.atoms = atoms
+
+        # Initialize to None to avoid strange behaviour due to __getattr__
+        self.eigenmodes = eigenmodes
+        self.curvatures = None
+
+        if control is None:
+            self.control = DimerControl(**kwargs)
+            w = 'Missing control object in ' + self.__class__.__name__ + \
+                '. Using default: DimerControl()'
+            warnings.warn(w, UserWarning)
+            if self.control.logfile is not None:
+                self.control.logfile.write('DIM:WARN: ' + w + '\n')
+                self.control.logfile.flush()
+        else:
+            self.control = control
+            logfile = self.control.get_logfile()
+            mlogfile = self.control.get_eigenmode_logfile()
+            for key in kwargs:
+                if key == 'logfile':
+                    logfile = kwargs[key]
+                elif key == 'eigenmode_logfile':
+                    mlogfile = kwargs[key]
+                else:
+                    self.control.set_parameter(key, kwargs[key])
+            self.control.initialize_logfiles(logfile = logfile,
+                                             eigenmode_logfile = mlogfile)
+
+        # Seed the randomness
+        if random_seed is None:
+            t = time.time()
+            if size > 1:
+                t = world.sum(t) / float(size)
+            # Harvest the latter part of the current time
+            random_seed = int(('%30.9f' % t)[-9:])
+        self.random_state = np.random.RandomState(random_seed)
+
+        # Check the order
+        self.order = self.control.get_parameter('order')
+
+        # Construct the curvatures list
+        self.curvatures = [100.0] * self.order
+
+        # Save the original state of the atoms.
+        self.atoms0 = self.atoms.copy()
+        self.save_original_forces()
+
+        # Get a reference to the log files
+        self.logfile = self.control.get_logfile()
+        self.mlogfile = self.control.get_eigenmode_logfile()
+
+    def save_original_forces(self, force_calculation=False):
+        """Store the forces (and energy) of the original state."""
+        # NB: Would be nice if atoms.copy() took care of this.
+        if self.calc is not None:
+            # Hack because some calculators do not have calculation_required
+            if (hasattr(self.calc, 'calculation_required') \
+               and not self.calc.calculation_required(self.atoms,
+               ['energy', 'forces'])) or force_calculation:
+                calc = SinglePointCalculator( \
+                       self.atoms.get_potential_energy(), \
+                       self.atoms.get_forces(), None, None, self.atoms0)
+                self.atoms0.set_calculator(calc)
+
+    def initialize_eigenmodes(self, method=None, eigenmodes=None, \
+                              gauss_std=None):
+        """Make an initial guess for the eigenmode."""
+        if eigenmodes is None:
+            pos = self.get_positions()
+            old_pos = self.get_original_positions()
+            if method == None:
+                method = \
+                     self.control.get_parameter('initial_eigenmode_method')
+            if method.lower() == 'displacement' and (pos - old_pos).any():
+                eigenmode = normalize(pos - old_pos)
+            elif method.lower() == 'gauss':
+                self.displace(log = False, gauss_std = gauss_std,
+                              method = method)
+                new_pos = self.get_positions()
+                eigenmode = normalize(new_pos - pos)
+                self.set_positions(pos)
+            else:
+                e = 'initial_eigenmode must use either \'gauss\' or ' + \
+                    '\'displacement\', if the latter is used the atoms ' + \
+                    'must have moved away from the original positions.' + \
+                    'You have requested \'%s\'.' % method
+                raise NotImplementedError(e) # NYI
+            eigenmodes = [eigenmode]
+
+        # Create random higher order mode guesses
+        if self.order > 1:
+            if len(eigenmodes) == 1:
+                for k in range(1, self.order):
+                    pos = self.get_positions()
+                    self.displace(log = False, gauss_std = gauss_std,
+                                  method = method)
+                    new_pos = self.get_positions()
+                    eigenmode = normalize(new_pos - pos)
+                    self.set_positions(pos)
+                    eigenmodes += [eigenmode]
+
+        self.eigenmodes = eigenmodes
+        # Ensure that the higher order mode guesses are all orthogonal
+        if self.order > 1:
+            for k in range(self.order):
+                self.ensure_eigenmode_orthogonality(k)
+        self.eigenmode_log()
+
+    # NB maybe this name might be confusing in context to
+    # calc.calculation_required()
+    def calculation_required(self):
+        """Check if a calculation is required."""
+        return self.minmode_init or self.check_atoms != self.atoms
+
+    def calculate_real_forces_and_energies(self, **kwargs):
+        """Calculate and store the potential energy and forces."""
+        if self.minmode_init:
+            self.minmode_init = False
+            self.initialize_eigenmodes(eigenmodes = self.eigenmodes)
+        self.rotation_required = True
+        self.forces0 = self.atoms.get_forces(**kwargs)
+        self.energy0 = self.atoms.get_potential_energy()
+        self.control.increment_counter('forcecalls')
+        self.check_atoms = self.atoms.copy()
+
+    def get_potential_energy(self):
+        """Return the potential energy."""
+        if self.calculation_required():
+            self.calculate_real_forces_and_energies()
+        return self.energy0
+
+    def get_forces(self, real=False, pos=None, **kwargs):
+        """Return the forces, projected or real."""
+        if self.calculation_required() and pos is None:
+            self.calculate_real_forces_and_energies(**kwargs)
+        if real and pos is None:
+            return self.forces0
+        elif real and pos != None:
+            old_pos = self.atoms.get_positions()
+            self.atoms.set_positions(pos)
+            forces = self.atoms.get_forces()
+            self.control.increment_counter('forcecalls')
+            self.atoms.set_positions(old_pos)
+            return forces
+        else:
+            if self.rotation_required:
+                self.find_eigenmodes(order = self.order)
+                self.eigenmode_log()
+                self.rotation_required = False
+                self.control.increment_counter('optcount')
+            return self.get_projected_forces()
+
+    def ensure_eigenmode_orthogonality(self, order):
+        mode = self.eigenmodes[order - 1].copy()
+        for k in range(order - 1):
+            mode = perpendicular_vector(mode, self.eigenmodes[k])
+        self.eigenmodes[order - 1] = normalize(mode)
+
+    def find_eigenmodes(self, order=1):
+        """Launch eigenmode searches."""
+        if self.control.get_parameter('eigenmode_method').lower() != 'dimer':
+            e = 'Only the Dimer control object has been implemented.'
+            raise NotImplementedError(e) # NYI
+        for k in range(order):
+            if k > 0:
+                self.ensure_eigenmode_orthogonality(k + 1)
+            search = DimerEigenmodeSearch(self, self.control, \
+                eigenmode = self.eigenmodes[k], basis = self.eigenmodes[:k])
+            search.converge_to_eigenmode()
+            search.set_up_for_optimization_step()
+            self.eigenmodes[k] = search.get_eigenmode()
+            self.curvatures[k] = search.get_curvature()
+
+    def get_projected_forces(self, pos=None):
+        """Return the projected forces."""
+        if pos is not None:
+            forces = self.get_forces(real = True, pos = pos).copy()
+        else:
+            forces = self.forces0.copy()
+
+        # Loop through all the eigenmodes
+        # NB: Can this be done with a linear combination, instead?
+        for k, mode in enumerate(self.eigenmodes):
+            # NYI This If statement needs to be overridable in the control
+            if self.get_curvature(order = k) > 0.0 and self.order == 1:
+                forces = -parallel_vector(forces, mode)
+            else:
+                forces -= 2 * parallel_vector(forces, mode)
+        return forces
+
+    def restore_original_positions(self):
+        """Restore the MinModeAtoms object positions to the original state."""
+        self.atoms.set_positions(self.get_original_positions())
+
+    def get_barrier_energy(self):
+        """The energy difference between the current and original states"""
+        try:
+            original_energy = self.get_original_potential_energy()
+            dimer_energy = self.get_potential_energy()
+            return dimer_energy - original_energy
+        except RuntimeError:
+            w = 'The potential energy is not available, without further ' + \
+                'calculations, most likely at the original state.'
+            warnings.warn(w, UserWarning)
+            return np.nan
+
+    def get_control(self):
+        """Return the control object."""
+        return self.control
+
+    def get_curvature(self, order='max'):
+        """Return the eigenvalue estimate."""
+        if order == 'max':
+            return max(self.curvatures)
+        else:
+            return self.curvatures[order - 1]
+
+    def get_eigenmode(self, order=1):
+        """Return the current eigenmode guess."""
+        return self.eigenmodes[order - 1]
+
+    def get_atoms(self):
+        """Return the unextended Atoms object."""
+        return self.atoms
+
+    def set_atoms(self, atoms):
+        """Set a new Atoms object"""
+        self.atoms = atoms
+
+    def set_eigenmode(self, eigenmode, order=1):
+        """Set the eigenmode guess."""
+        self.eigenmodes[order - 1] = eigenmode
+
+    def set_curvature(self, curvature, order=1):
+        """Set the eigenvalue estimate."""
+        self.curvatures[order - 1] = curvature
+
+    # Pipe all the stuff from Atoms that is not overwritten.
+    # Pipe all requests for get_original_* to self.atoms0.
+    def __getattr__(self, attr):
+        """Return any value of the Atoms object"""
+        if 'original' in attr.split('_'):
+            attr = attr.replace('_original_', '_')
+            return getattr(self.atoms0, attr)
+        else:
+            return getattr(self.atoms, attr)
+
+    def displace(self, displacement_vector=None, mask=None, method=None,
+                 displacement_center=None, radius=None, number_of_atoms=None,
+                 gauss_std=None, mic=True, log=True):
+        """Move the atoms away from their current position.
+
+        This is one of the essential parts of minimum mode searches.
+        The parameters can all be set in the control object and overwritten
+        when this method is run, apart from *displacement_vector*.
+        It is preferred to modify the control values rather than those here
+        in order for the correct ones to show up in the log file.
+
+        *method* can be either 'gauss' for random displacement or 'vector'
+        to perform a predefined displacement.
+
+        *gauss_std* is the standard deviation of the gauss curve that is
+        used for random displacement.
+
+        *displacement_center* can be either the number of an atom or a 3D
+        position. It must be accompanied by a *radius* (all atoms within it
+        will be displaced) or a *number_of_atoms* which decides how many of
+        the closest atoms will be displaced.
+
+        *mic* controls the usage of the Minimum Image Convention.
+
+        If both *mask* and *displacement_center* are used, the atoms marked
+        as False in the *mask* will not be affected even though they are
+        within reach of the *displacement_center*.
+
+        The parameters priority order:
+        1) displacement_vector
+        2) mask
+        3) displacement_center (with radius and/or number_of_atoms)
+
+        If both *radius* and *number_of_atoms* are supplied with
+        *displacement_center*, only atoms that fulfill both criteria will
+        be displaced.
+
+        """
+
+        # Fetch the default values from the control
+        if mask is None:
+            mask = self.control.get_parameter('mask')
+        if method is None:
+            method = self.control.get_parameter('displacement_method')
+        if gauss_std is None:
+            gauss_std = self.control.get_parameter('gauss_std')
+        if displacement_center is None:
+            displacement_center = \
+                    self.control.get_parameter('displacement_center')
+        if radius is None:
+            radius = self.control.get_parameter('displacement_radius')
+        if number_of_atoms is None:
+            number_of_atoms = \
+                    self.control.get_parameter('number_of_displacement_atoms')
+
+        # Check for conflicts
+        if displacement_vector is not None and method.lower() != 'vector':
+            e = 'displacement_vector was supplied but a different method ' + \
+                '(\'%s\') was chosen.\n' % str(method)
+            raise ValueError(e)
+        elif displacement_vector is None and method.lower() == 'vector':
+            e = 'A displacement_vector must be supplied when using ' + \
+                'method = \'%s\'.\n' % str(method)
+            raise ValueError(e)
+        elif displacement_center is not None and radius is None and \
+           number_of_atoms is None:
+            e = 'When displacement_center is chosen, either radius or ' + \
+                'number_of_atoms must be supplied.\n'
+            raise ValueError(e)
+
+        # Set up the center of displacement mask (c_mask)
+        if displacement_center is not None:
+            c = displacement_center
+            # Construct a distance list
+            # The center is an atom
+            if type(c) is int:
+                # Parse negative indexes
+                c = displacement_center % len(self)
+                d = [(k, self.get_distance(k, c, mic = mic)) for k in \
+                     range(len(self))]
+            # The center is a position in 3D space
+            elif len(c) == 3 and [type(c_k) for c_k in c] == [float]*3:
+                # NB: MIC is not considered.
+                d = [(k, norm(self.get_positions()[k] - c)) \
+                     for k in range(len(self))]
+            else:
+                e = 'displacement_center must be either the number of an ' + \
+                    'atom in MinModeAtoms object or a 3D position ' + \
+                    '(3-tuple of floats).'
+                raise ValueError(e)
+
+            # Set up the mask
+            if radius is not None:
+                r_mask = [dist[1] < radius for dist in d]
+            else:
+                r_mask = [True for _ in self]
+
+            if number_of_atoms is not None:
+                d_sorted = [n[0] for n in sorted(d, key = lambda k: k[1])]
+                n_nearest = d_sorted[:number_of_atoms]
+                n_mask = [k in n_nearest for k in range(len(self))]
+            else:
+                n_mask = [True for _ in self]
+
+            # Resolve n_mask / r_mask conflicts
+            c_mask = [n_mask[k] and r_mask[k] for k in range(len(self))]
+        else:
+            c_mask = None
+
+        # Set up a True mask if there is no mask supplied
+        if mask is None:
+            mask = [True for _ in self]
+            if c_mask is None:
+                w = 'It was not possible to figure out which atoms to ' + \
+                    'displace, Will try to displace all atoms.\n'
+                warnings.warn(w, UserWarning)
+                if self.logfile is not None:
+                    self.logfile.write('MINMODE:WARN: ' + w + '\n')
+                    self.logfile.flush()
+
+        # Resolve mask / c_mask conflicts
+        if c_mask is not None:
+            mask = [mask[k] and c_mask[k] for k in range(len(self))]
+
+        if displacement_vector is None:
+            displacement_vector = []
+            for k in range(len(self)):
+                if mask[k]:
+                    diff_line = []
+                    for _ in range(3):
+                        if method.lower() == 'gauss':
+                            if not gauss_std:
+                                gauss_std = \
+                                self.control.get_parameter('gauss_std')
+                            diff = self.random_state.normal(0.0, gauss_std)
+                        else:
+                            e = 'Invalid displacement method >>%s<<' % \
+                                 str(method)
+                            raise ValueError(e)
+                        diff_line.append(diff)
+                    displacement_vector.append(diff_line)
+                else:
+                    displacement_vector.append([0.0]*3)
+
+        # Remove displacement of masked atoms
+        for k in range(len(mask)):
+            if not mask[k]:
+                displacement_vector[k] = [0.0]*3
+
+        # Perform the displacement and log it
+        if log:
+            pos0 = self.get_positions()
+        self.set_positions(self.get_positions() + displacement_vector)
+        if log:
+            parameters = {'mask': mask,
+                          'displacement_method': method,
+                          'gauss_std': gauss_std,
+                          'displacement_center': displacement_center,
+                          'displacement_radius': radius,
+                          'number_of_displacement_atoms': number_of_atoms}
+            self.displacement_log(self.get_positions() - pos0, parameters)
+
+    def eigenmode_log(self):
+        """Log the eigenmodes (eigenmode estimates)"""
+        if self.mlogfile is not None:
+            l = 'MINMODE:MODE: Optimization Step: %i\n' % \
+                   (self.control.get_counter('optcount'))
+            for m_num, mode in enumerate(self.eigenmodes):
+                l += 'MINMODE:MODE: Order: %i\n' % m_num
+                for k in range(len(mode)):
+                    l += 'MINMODE:MODE: %7i %15.8f %15.8f %15.8f\n' % (k,
+                         mode[k][0], mode[k][1], mode[k][2])
+            self.mlogfile.write(l)
+            self.mlogfile.flush()
+
+    def displacement_log(self, displacement_vector, parameters):
+        """Log the displacement"""
+        if self.logfile is not None:
+            lp = 'MINMODE:DISP: Parameters, different from the control:\n'
+            mod_para = False
+            for key in parameters:
+                if parameters[key] != self.control.get_parameter(key):
+                    lp += 'MINMODE:DISP: %s = %s\n' % (str(key),
+                                                       str(parameters[key]))
+                    mod_para = True
+            if mod_para:
+                l = lp
+            else:
+                l = ''
+            for k in range(len(displacement_vector)):
+                l += 'MINMODE:DISP: %7i %15.8f %15.8f %15.8f\n' % (k,
+                     displacement_vector[k][0], displacement_vector[k][1],
+                     displacement_vector[k][2])
+            self.logfile.write(l)
+            self.logfile.flush()
+
+    def summarize(self):
+        """Summarize the Minimum mode search."""
+        if self.logfile is None:
+            logfile = sys.stdout
+        else:
+            logfile = self.logfile
+
+        c = self.control
+        label = 'MINMODE:SUMMARY: '
+
+        l = label + '-------------------------\n'
+        l += label + 'Barrier: %16.4f\n' % self.get_barrier_energy()
+        l += label + 'Curvature: %14.4f\n' % self.get_curvature()
+        l += label + 'Optimizer steps: %8i\n' % c.get_counter('optcount')
+        l += label + 'Forcecalls: %13i\n' % c.get_counter('forcecalls')
+        l += label + '-------------------------\n'
+
+        logfile.write(l)
+
+class MinModeTranslate(Optimizer):
+    """An Optimizer specifically tailored to minimum mode following."""
+    def __init__(self, atoms, logfile='-', trajectory=None):
+        Optimizer.__init__(self, atoms, None, logfile, trajectory)
+
+        self.control = atoms.get_control()
+
+        # Make a header for the log
+        if self.logfile is not None:
+            l = ''
+            if isinstance(self.control, DimerControl):
+                l = 'MinModeTranslate: STEP      TIME          ENERGY    ' + \
+                    'MAX-FORCE     STEPSIZE    CURVATURE  ROT-STEPS\n'
+            self.logfile.write(l)
+            self.logfile.flush()
+
+        # Load the relevant parameters from control
+        self.cg_on = self.control.get_parameter('cg_translation')
+        self.trial_step = self.control.get_parameter('trial_trans_step')
+        self.max_step = self.control.get_parameter('maximum_translation')
+
+        # Start conjugate gradient
+        if self.cg_on:
+            self.cg_init = True
+
+    def initialize(self):
+        """Set initial values."""
+        self.r0 = None
+        self.f0 = None
+
+    def run(self, fmax=0.05, steps=100000000):
+        """Run structure optimization algorithm.
+
+        This method will return when the forces on all individual
+        atoms are less than *fmax* or when the number of steps exceeds
+        *steps*.
+
+        """
+
+        self.fmax = fmax
+        step = 0
+        while step < steps:
+            f = self.atoms.get_forces()
+            self.call_observers()
+            if self.converged(f):
+                self.log(f, None)
+                return
+            self.step(f)
+            self.nsteps += 1
+            step += 1
+
+    def step(self, f):
+        """Perform the optimization step."""
+        atoms = self.atoms
+        r = atoms.get_positions()
+        curv = atoms.get_curvature()
+        f0p = f.copy()
+        r0 = r.copy()
+        direction = f0p.copy()
+        if self.cg_on:
+            direction = self.get_cg_direction(direction)
+        direction = normalize(direction)
+        if curv > 0.0:
+            step = direction * self.max_step
+        else:
+            r0t = r0 + direction * self.trial_step
+            f0tp = self.atoms.get_projected_forces(r0t)
+            F = np.vdot((f0tp + f0p), direction) / 2.0
+            C = np.vdot((f0tp - f0p), direction) / self.trial_step
+            step = ( -F / C + self.trial_step / 2.0 ) * direction
+            if norm(step) > self.max_step:
+                step = direction * self.max_step
+        self.log(f0p, norm(step))
+
+        atoms.set_positions(r + step)
+
+        self.f0 = f.flat.copy()
+        self.r0 = r.flat.copy()
+
+    def get_cg_direction(self, direction):
+        """Apply the Conjugate Gradient algorithm to the step direction."""
+        if self.cg_init:
+            self.cg_init = False
+            self.direction_old = direction.copy()
+            self.cg_direction = direction.copy()
+        old_norm = np.vdot(self.direction_old, self.direction_old)
+        # Polak-Ribiere Conjugate Gradient
+        if old_norm != 0.0:
+            betaPR = np.vdot(direction, (direction - self.direction_old)) / \
+                     old_norm
+        else:
+            betaPR = 0.0
+        if betaPR < 0.0:
+            betaPR = 0.0
+        self.cg_direction = direction + self.cg_direction * betaPR
+        self.direction_old = direction.copy()
+        return self.cg_direction.copy()
+
+    def log(self, f, stepsize):
+        """Log each step of the optimization."""
+        if self.logfile is not None:
+            T = time.localtime()
+            e = self.atoms.get_potential_energy()
+            fmax = sqrt((f**2).sum(axis = 1).max())
+            rotsteps = self.atoms.control.get_counter('rotcount')
+            curvature = self.atoms.get_curvature()
+            l = ''
+            if stepsize:
+                if isinstance(self.control, DimerControl):
+                    l = '%s: %4d  %02d:%02d:%02d %15.6f %12.4f %12.6f ' \
+                        '%12.6f %10d\n' % ('MinModeTranslate', self.nsteps,
+                         T[3], T[4], T[5], e, fmax, stepsize, curvature,
+                         rotsteps)
+            else:
+                if isinstance(self.control, DimerControl):
+                    l = '%s: %4d  %02d:%02d:%02d %15.6f %12.4f %s ' \
+                        '%12.6f %10d\n' % ('MinModeTranslate', self.nsteps,
+                         T[3], T[4], T[5], e, fmax, '    --------',
+                         curvature, rotsteps)
+            self.logfile.write(l)
+            self.logfile.flush()
+
+def read_eigenmode(mlog, index = -1):
+    """Read an eigenmode.
+    To access the pre optimization eigenmode set index = 'null'.
+
+    """
+    if isinstance(mlog, str):
+        f = open(mlog, 'r')
+    else:
+        f = mlog
+
+    lines = f.readlines()
+
+    # Detect the amount of atoms and iterations
+    k = 2
+    while lines[k].split()[1].lower() not in ['optimization', 'order']:
+        k += 1
+    n = k - 2
+    n_itr = (len(lines) / (n + 1)) - 2
+
+    # Locate the correct image.
+    if type(index) == str:
+        if index.lower() == 'null':
+            i = 0
+        else:
+            i = int(index) + 1
+    else:
+        if index >= 0:
+            i = index + 1
+        else:
+            if index < -n_itr - 1:
+                raise IndexError('list index out of range')
+            else:
+                i = index
+
+    mode = np.ndarray(shape = (n, 3), dtype = float)
+    k_atom = 0
+    for k in range(1, n + 1):
+        line = lines[i * (n + 1) + k].split()
+        for k_dim in range(3):
+            mode[k_atom][k_dim] = float(line[k_dim + 2])
+        k_atom += 1
+
+    return mode
+
+# Aliases
+DimerAtoms = MinModeAtoms
+DimerTranslate = MinModeTranslate
diff --git a/ase/examples/Al100.py b/ase/examples/Al100.py
new file mode 100644
index 0000000..5cc54d8
--- /dev/null
+++ b/ase/examples/Al100.py
@@ -0,0 +1,10 @@
+from ase import Atom, Atoms
+from gpaw import GPAW
+a = 4.0
+b = a / 2**.5
+L = 7.0
+al = Atoms([Atom('Al')], cell=(b, b, L), pbc=True)
+calc = GPAW(kpts=(4, 4, 1))
+al.set_calculator(calc)
+al.get_potential_energy()
+calc.write('Al100.gpw', 'all')
diff --git a/ase/examples/COCu111.py b/ase/examples/COCu111.py
new file mode 100644
index 0000000..4009213
--- /dev/null
+++ b/ase/examples/COCu111.py
@@ -0,0 +1,73 @@
+from math import sqrt
+from ase import Atoms, Atom
+from ase.constraints import FixAtoms
+from ase.optimize import QuasiNewton
+from ase.io import PickleTrajectory
+from ase.neb import NEB
+from ase.calculators.emt import EMT
+
+# Distance between Cu atoms on a (111) surface:
+a = 3.6
+d = a / sqrt(2)
+y = d * sqrt(3) / 2
+fcc111 = Atoms('Cu',
+               cell=[(d, 0, 0),
+                     (d / 2, y, 0),
+                     (d / 2, y / 3, -a / sqrt(3))],
+               pbc=True)
+slab = fcc111 * (2, 2, 4)
+slab.set_cell([2 * d, 2 * y, 1])
+slab.set_pbc((1, 1, 0))
+slab.set_calculator(EMT())
+Z = slab.get_positions()[:, 2]
+indices = [i for i, z in enumerate(Z) if z < Z.mean()]
+constraint = FixAtoms(indices=indices)
+slab.set_constraint(constraint)
+dyn = QuasiNewton(slab)
+dyn.run(fmax=0.05)
+Z = slab.get_positions()[:, 2]
+print Z[0] - Z[1]
+print Z[1] - Z[2]
+print Z[2] - Z[3]
+
+b = 1.2
+h = 2.0
+slab += Atom('C', (d, 2 * y / 3, h))
+slab += Atom('O', (3 * d / 2, y / 3, h))
+traj = PickleTrajectory('initial.traj', 'w', slab)
+dyn = QuasiNewton(slab)
+dyn.attach(traj.write)
+dyn.run(fmax=0.05)
+#view(slab)
+# Make band:
+images = [slab.copy() for i in range(6)]
+neb = NEB(images, climb=True)
+
+# Set constraints and calculator:
+for image in images:
+    image.set_calculator(EMT())
+    image.set_constraint(constraint)
+
+# Displace last image:
+images[-1].positions[-1] = (2 * d, 2 * y / 3, h)
+traj = PickleTrajectory('final.traj', 'w', images[-1])
+dyn = QuasiNewton(images[-1])
+dyn.attach(traj.write)
+dyn.run(fmax=0.05)
+
+# Interpolate positions between initial and final states:
+neb.interpolate()
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+traj = PickleTrajectory('mep.traj', 'w')
+
+#dyn = MDMin(neb, dt=0.4)
+#dyn = FIRE(neb, dt=0.4)
+dyn = QuasiNewton(neb)
+dyn.attach(neb.writer(traj))
+dyn.run(fmax=0.05)
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
diff --git a/ase/examples/Pt_island.py b/ase/examples/Pt_island.py
new file mode 100644
index 0000000..f5079fd
--- /dev/null
+++ b/ase/examples/Pt_island.py
@@ -0,0 +1,74 @@
+import numpy as np
+from math import sqrt
+from ase import Atom, Atoms
+from ase.optimize import QuasiNewton, FIRE
+from ase.constraints import FixAtoms
+from ase.neb import NEB
+from ase.io import write, PickleTrajectory
+from ase.calculators.emt import ASAP
+
+# Distance between Cu atoms on a (100) surface:
+d = 2.74
+h1 = d * sqrt(3) / 2
+h2 = d * sqrt(2.0 / 3)
+initial = Atoms(symbols='Pt',
+                positions=[(0, 0, 0)],#(1.37,0.79,2.24),(2.74,1.58,4.48),(0,0,6.72),(1.37,0.79,8.96),(2.74,1.58,11.2)],
+                cell=([(d,0,0),(d/2,h1,0),(d/2,h1/3,-h2)]),
+                pbc=(True, True, True))
+initial *= (7, 8, 6)  # 5x5 (100) surface-cell
+cell = initial.get_cell()
+cell[2] = (0, 0, 22)
+initial.set_cell(cell)
+#initial.set_pbc((True,True,False))
+# Approximate height of Ag atom on Cu(100) surfece:
+h0 = 2.2373
+initial += Atom('Pt', (10.96, 11.074, h0))
+initial += Atom('Pt', (13.7, 11.074, h0))
+initial += Atom('Pt', (9.59, 8.701, h0))
+initial += Atom('Pt', (12.33, 8.701, h0))
+initial += Atom('Pt', (15.07, 8.701, h0))
+initial += Atom('Pt', (10.96, 6.328, h0))
+initial += Atom('Pt', (13.7, 6.328, h0))
+
+if 0:
+    view(initial)
+
+# Make band:
+images = [initial.copy() for i in range(7)]
+neb = NEB(images)
+
+# Set constraints and calculator:
+indices = np.compress(initial.positions[:, 2] < -5.0, range(len(initial)))
+constraint = FixAtoms(indices)
+for image in images:
+    image.set_calculator(ASAP())
+    image.constraints.append(constraint)
+
+# Displace last image:
+for i in xrange(1,8,1):
+    images[-1].positions[-i] += (d/2, -h1/3, 0)
+
+write('initial.traj', images[0])
+# Relax height of Ag atom for initial and final states:
+for image in [images[0], images[-1]]:
+    QuasiNewton(image).run(fmax=0.01)
+
+if 0:
+    write('initial.pckl', image[0])
+    write('finial.pckl', image[-1])
+# Interpolate positions between initial and final states:
+neb.interpolate()
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+traj = PickleTrajectory('mep.traj', 'w')
+
+dyn = FIRE(neb, dt=0.1)
+#dyn = MDMin(neb, dt=0.1)
+#dyn = QuasiNewton(neb)
+dyn.attach(neb.writer(traj))
+dyn.run(fmax=0.01,steps=150)
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
diff --git a/ase/examples/__init__.py b/ase/examples/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ase/examples/neb1.py b/ase/examples/neb1.py
new file mode 100644
index 0000000..c01650d
--- /dev/null
+++ b/ase/examples/neb1.py
@@ -0,0 +1,55 @@
+from ase import *
+from math import sqrt
+
+a = 4.0614
+b = a / sqrt(2)
+h = b / 2
+initial = Atoms([Atom('Al', (0, 0, 0)),
+                 Atom('Al', (a / 2, b / 2, -h))],
+                pbc=(1, 1, 0),
+                cell=(a, b, 2 * h))
+initial *= (2, 2, 2)
+initial.append(Atom('Al', (a / 2, b / 2, 3 * h)))
+
+#view(initial)
+
+#initial.set_cell((2*2,2*b,)
+final = initial.copy()
+final.positions[-1, 1] += b
+
+# Construct a list of images:
+images = [initial]
+for i in range(5):
+    images.append(initial.copy())
+images.append(final)
+
+# Make a mask of zeros and ones that select the dynamic atoms (the
+# three topmost layers):
+mask = initial.positions[:, 2] < 0.5 * h
+constraint = FixAtoms(mask=mask)
+print mask
+print 'Fixed atoms:', constraint.fixed
+
+for image in images:
+    # Let all images use an EMT calculator:
+    image.set_calculator(EMT())
+    image.set_constraint(constraint)
+
+# Relax the initial and final states:
+QuasiNewton(initial).run(fmax=0.05)
+QuasiNewton(final).run(fmax=0.05)
+
+# Create a Nudged Elastic Band:
+neb = NEB(images)
+
+# Mak a starting guess for the minimum energy path (a straight line
+# from the initial to the final state):
+neb.interpolate()
+
+# Use MDMin to relax the path:
+minimizer = QuasiNewton(neb)
+minimizer.run(fmax=0.05)
+
+# Write the path to a trajectory:
+traj = PickleTrajectory('jump1.traj', 'w')
+neb.write(traj)
diff --git a/ase/examples/plot-ase-atoms.py b/ase/examples/plot-ase-atoms.py
new file mode 100644
index 0000000..c8296a8
--- /dev/null
+++ b/ase/examples/plot-ase-atoms.py
@@ -0,0 +1,51 @@
+from ase import Atoms, Atom, view
+from gpaw import GPAW
+
+logo = """\
+ H   HH HHH
+H H H   H
+HHH  H  HH
+H H   H H
+H H HH  HHH"""
+
+d = 0.8
+atoms = Atoms()
+for y, line in enumerate(logo.split('\n')):
+    for x, c in enumerate(line):
+        if c == 'H':
+            atoms.append(Atom('H', [d * x, -d * y, 0]))
+atoms.center(vacuum=2.0)
+view(atoms)
+
+if 0:
+    calc = GPAW(nbands=30)
+    atoms.set_calculator(calc)
+    atoms.get_potential_energy()
+    calc.write('ase-logo.gpw')
+else:
+    calc = GPAW('ase-logo.gpw', txt=None)
+
+density = calc.get_pseudo_density()
+image = density[..., density.shape[2] // 2]
+
+if 1: # scale colors to wiki background / foreground
+    import numpy as np
+    background = np.array([[[19., 63., 82.]]]).T / 255 # 1c4e63 blueish
+    foreground = np.array([[[1., 1., 1.]]]).T  # white
+    image = background + image / image.max() * (foreground - background)
+    image = image.T
+else: # Use a standard color scheme
+    image = pl.cm.hot(image.T)
+
+import pylab as pl
+x, y, z = atoms.cell.diagonal()
+pl.figure(1, figsize=(8, 8 * y / x), dpi=80)
+pl.axes([0, 0, 1, 1])
+pl.imshow(image,
+          origin='lower',
+          extent=[0, x, 0, y],
+          aspect='equal',
+          interpolation='spline16')
+pl.axis('off')
+pl.savefig('ase-logo.png', dpi=80)
+pl.show()
diff --git a/ase/examples/stm.py b/ase/examples/stm.py
new file mode 100644
index 0000000..2bacf11
--- /dev/null
+++ b/ase/examples/stm.py
@@ -0,0 +1,11 @@
+from ase import Atoms
+from ase.dft import STM
+from gpaw import GPAW
+calc = GPAW('Al100.gpw')
+a0 = calc.get_atoms()
+stm = STM(calc, [0, 1, 2])
+c = stm.get_averaged_current(2.5)
+h = stm.scan(c)
+print h[8]-h[:, 8]
+
+
diff --git a/ase/gui/__init__.py b/ase/gui/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ase/gui/ag.py b/ase/gui/ag.py
new file mode 100644
index 0000000..7133aab
--- /dev/null
+++ b/ase/gui/ag.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+
+# Copyright 2008, 2009
+# CAMd (see accompanying license files for details).
+
+import os
+from optparse import OptionParser, SUPPRESS_HELP
+
+import ase.gui.i18n
+from gettext import gettext as _
+
+# Grrr, older versions (pre-python2.7) of optparse have a bug
+# which prevents non-ascii descriptions.  How do we circumvent this?
+# For now, we'll have to use English in the command line options then.
+
+def build_parser():
+    parser = OptionParser(usage='%prog [options] [file[, file2, ...]]',
+                          version='%prog 0.1',
+                          description='See the online manual ' +
+                          '(https://wiki.fysik.dtu.dk/ase/ase/gui.html) ' +
+                          'for more information.')
+    parser.add_option('-n', '--image-number',
+                      default=':', metavar='NUMBER',
+                      help='Pick image(s) from trajectory.  NUMBER can be a '
+                      'single number (use a negative number to count from '
+                      'the back) or a range: start:stop:step, where the '
+                      '":step" part can be left out - default values are '
+                      '0:nimages:1.')
+    parser.add_option('-u', '--show-unit-cell', type='int',
+                      default=1, metavar='I',
+                      help="0: Don't show unit cell.  1: Show unit cell.  "
+                      '2: Show all of unit cell.')
+    parser.add_option('-r', '--repeat',
+                      default='1',
+                      help='Repeat unit cell.  Use "-r 2" or "-r 2,3,1".')
+    parser.add_option('-R', '--rotations', default='',
+                      help='Examples: "-R -90x", "-R 90z,-30x".')
+    parser.add_option('-o', '--output', metavar='FILE',
+                      help='Write configurations to FILE.')
+    parser.add_option('-g', '--graph',
+                      # TRANSLATORS: EXPR abbreviates 'expression'
+                      metavar='EXPR',
+                      help='Plot x,y1,y2,... graph from configurations or '
+                      'write data to sdtout in terminal mode.  Use the '
+                      'symbols: i, s, d, fmax, e, ekin, A, R, E and F.  See '
+                      'https://wiki.fysik.dtu.dk/ase/ase/gui.html'
+                      '#plotting-data for more details.')
+    parser.add_option('-t', '--terminal',
+                      action='store_true',
+                      default=False,
+                      help='Run in terminal window - no GUI.')
+    parser.add_option('--aneb',
+                      action='store_true',
+                      default=False,
+                      help='Read ANEB data.')
+    parser.add_option('--interpolate',
+                      type='int', metavar='N',
+                      help='Interpolate N images between 2 given images.')
+    parser.add_option('-b', '--bonds',
+                      action='store_true',
+                      default=False,
+                      help='Draw bonds between atoms.')
+    return parser
+
+
+def main():
+    parser = build_parser()
+    opt, args = parser.parse_args()
+
+    try:
+        import ase
+    except ImportError:
+        import sys
+        from os.path import dirname, join, pardir
+        sys.path.append(join(dirname(__file__), pardir))
+
+    from ase.gui.images import Images
+    from ase.atoms import Atoms
+
+    def run(opt, args):
+        images = Images()
+
+        if opt.aneb:
+            opt.image_number = '-1'
+
+        if len(args) > 0:
+            from ase.io import string2index
+            images.read(args, string2index(opt.image_number))
+        else:
+            images.initialize([Atoms()])
+
+        if opt.interpolate:
+            images.interpolate(opt.interpolate)
+
+        if opt.aneb:
+            images.aneb()
+
+        if opt.repeat != '1':
+            r = opt.repeat.split(',')
+            if len(r) == 1:
+                r = 3 * r
+            images.repeat_images([int(c) for c in r])
+
+        if opt.output is not None:
+            images.write(opt.output, rotations=opt.rotations,
+                         show_unit_cell=opt.show_unit_cell)
+            opt.terminal = True
+
+        if opt.terminal:
+            if opt.graph is not None:
+                data = images.graph(opt.graph)
+                for line in data.T:
+                    for x in line:
+                        print x,
+                    print
+        else:
+            from ase.gui.gui import GUI
+            import ase.gui.gtkexcepthook
+            gui = GUI(images, opt.rotations, opt.show_unit_cell, opt.bonds)
+            gui.run(opt.graph)
+
+    import traceback
+
+    try:
+        run(opt, args)
+    except KeyboardInterrupt:
+        pass
+    except Exception:
+        traceback.print_exc()
+        print(_("""
+An exception occurred!  Please report the issue to
+ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if
+it was a user error, so that a better error message can be provided
+next time."""))
diff --git a/ase/gui/bulk_modulus.py b/ase/gui/bulk_modulus.py
new file mode 100644
index 0000000..12ce185
--- /dev/null
+++ b/ase/gui/bulk_modulus.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+from math import sqrt
+
+import numpy as np
+
+from ase.units import kJ
+from ase.utils.eos import EquationOfState
+
+
+def BulkModulus(images):
+    v = np.array([abs(np.linalg.det(A)) for A in images.A])
+    #import matplotlib.pyplot as plt
+    import pylab as plt
+    plt.ion()
+    EquationOfState(v, images.E).plot()
+
+"""
+    fit = np.poly1d(np.polyfit(v**-(1.0 / 3), images.E, 3))
+    fit1 = np.polyder(fit, 1)
+    fit2 = np.polyder(fit1, 1)
+    for t in np.roots(fit1):
+        if t > 0 and fit2(t) > 0:
+            break
+    v0 = t**-3
+    e0 = fit(t)
+    B = t**5 * fit2(t) / 9 / kJ * 1.0e24  # Gpa
+
+    import pylab
+    import matplotlib
+    #matplotlib.use('GTK')
+
+    pylab.ion()
+    x = 3.95
+    pylab.figure(figsize=(x * 2.5**0.5, x))
+
+    pylab.plot(v, images.E, 'o')
+
+    x = np.linspace(min(v), max(v), 100)
+    pylab.plot(x, fit(x**-(1.0 / 3)), '-r')
+    pylab.xlabel(u'volume [Å^3]')
+    pylab.ylabel(u'energy [eV]')
+    pylab.title(u'E: %.3f eV, V: %.3f Å^3, B: %.3f GPa' % (e0, v0, B))
+    pylab.show()
+"""
diff --git a/ase/gui/calculator.py b/ase/gui/calculator.py
new file mode 100644
index 0000000..a4051fd
--- /dev/null
+++ b/ase/gui/calculator.py
@@ -0,0 +1,1955 @@
+# encoding: utf-8
+"calculator.py - module for choosing a calculator."
+
+import gtk
+from gettext import gettext as _
+import os
+import numpy as np
+from copy import copy
+from ase.gui.setupwindow import SetupWindow
+from ase.gui.progress import DefaultProgressIndicator, GpawProgressIndicator
+from ase.gui.widgets import pack, oops, cancel_apply_ok
+from ase import Atoms
+from ase.data import chemical_symbols
+import ase
+
+# Asap and GPAW may be imported if selected.
+
+introtext = _("""\
+To make most calculations on the atoms, a Calculator object must first
+be associated with it.  ASE supports a number of calculators, supporting
+different elements, and implementing different physical models for the
+interatomic interactions.\
+""")
+
+# Informational text about the calculators
+lj_info_txt = _("""\
+The Lennard-Jones pair potential is one of the simplest
+possible models for interatomic interactions, mostly
+suitable for noble gasses and model systems.
+
+Interactions are described by an interaction length and an
+interaction strength.\
+""")
+
+emt_info_txt = _("""\
+The EMT potential is a many-body potential, giving a
+good description of the late transition metals crystalling
+in the FCC crystal structure.  The elements described by the
+main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and
+Au, the Al potential is however not suitable for materials
+science application, as the stacking fault energy is wrong.
+
+A number of parameter sets are provided.
+
+<b>Default parameters:</b>
+
+The default EMT parameters, as published in K. W. Jacobsen,
+P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).
+
+<b>Alternative Cu, Ag and Au:</b>
+
+An alternative set of parameters for Cu, Ag and Au,
+reoptimized to experimental data including the stacking
+fault energies by Torben Rasmussen (partly unpublished).
+
+<b>Ruthenium:</b>
+
+Parameters for Ruthenium, as published in J. Gavnholt and
+J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).
+
+<b>Metallic glasses:</b>
+
+Parameters for MgCu and CuZr metallic glasses. MgCu
+parameters are in N. P. Bailey, J. Schiøtz and
+K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).
+CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and
+J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).
+""")
+
+aseemt_info_txt = _("""\
+The EMT potential is a many-body potential, giving a
+good description of the late transition metals crystalling
+in the FCC crystal structure.  The elements described by the
+main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and
+Au.  In addition, this implementation allows for the use of
+H, N, O and C adatoms, although the description of these is
+most likely not very good.
+
+<b>This is the ASE implementation of EMT.</b> For large
+simulations the ASAP implementation is more suitable; this
+implementation is mainly to make EMT available when ASAP is
+not installed.
+""")
+
+brenner_info_txt = _("""\
+The Brenner potential is a reactive bond-order potential for
+carbon and hydrocarbons.  As a bond-order potential, it takes
+into account that carbon orbitals can hybridize in different
+ways, and that carbon can form single, double and triple
+bonds.  That the potential is reactive means that it can
+handle gradual changes in the bond order as chemical bonds
+are formed or broken.
+
+The Brenner potential is implemented in Asap, based on a
+C implentation published at http://www.rahul.net/pcm/brenner/ .
+
+The potential is documented here:
+  Donald W Brenner, Olga A Shenderova, Judith A Harrison,
+  Steven J Stuart, Boris Ni and Susan B Sinnott:
+  "A second-generation reactive empirical bond order (REBO)
+  potential energy expression for hydrocarbons",
+  J. Phys.: Condens. Matter 14 (2002) 783-802.
+  doi: 10.1088/0953-8984/14/4/312
+""")
+
+
+gpaw_info_txt = _("""\
+GPAW implements Density Functional Theory using a
+<b>G</b>rid-based real-space representation of the wave
+functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave
+method for handling the core regions.  
+""")
+
+aims_info_txt = _("""\
+FHI-aims is an external package implementing density 
+functional theory and quantum chemical methods using 
+all-electron methods and a numeric local orbital basis set. 
+For full details, see http://www.fhi-berlin.mpg.de/aims/ 
+or Comp. Phys. Comm. v180 2175 (2009). The ASE 
+documentation contains information on the keywords and 
+functionalities available within this interface. 
+""")
+
+aims_pbc_warning_text = _("""\
+WARNING:
+Your system seems to have more than zero but less than 
+three periodic dimensions. Please check that this is 
+really what you want to compute. Assuming full 
+3D periodicity for this calculator.""")
+
+vasp_info_txt = _("""\
+VASP is an external package implementing density 
+functional functional theory using pseudopotentials 
+or the projector-augmented wave method together 
+with a plane wave basis set. For full details, see
+http://cms.mpi.univie.ac.at/vasp/vasp/
+""")
+
+emt_parameters = (
+    (_("Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"), None),
+    (_("Alternative Cu, Ag and Au"), "EMTRasmussenParameters"),
+    (_("Ruthenium"), "EMThcpParameters"),
+    (_("CuMg and CuZr metallic glass"), "EMTMetalGlassParameters")
+    )
+
+class SetCalculator(SetupWindow):
+    "Window for selecting a calculator."
+
+    # List the names of the radio button attributes
+    radios = ("none", "lj", "emt", "aseemt", "brenner", "gpaw", "aims", "vasp")
+    # List the names of the parameter dictionaries
+    paramdicts = ("lj_parameters","gpaw_parameters","aims_parameters",)
+    # The name used to store parameters on the gui object
+    classname = "SetCalculator"
+    
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Select calculator"))
+        vbox = gtk.VBox()
+        
+        # Intoductory text
+        self.packtext(vbox, introtext)
+        
+        pack(vbox, [gtk.Label(_("Calculator:"))])
+
+        # No calculator (the default)
+        self.none_radio = gtk.RadioButton(None, _("None"))
+        pack(vbox, [self.none_radio])
+
+        # Lennard-Jones
+        self.lj_radio = gtk.RadioButton(self.none_radio,
+                                        _("Lennard-Jones (ASAP)"))
+        self.lj_setup = gtk.Button(_("Setup"))
+        self.lj_info = InfoButton(lj_info_txt)
+        self.lj_setup.connect("clicked", self.lj_setup_window)
+        self.pack_line(vbox, self.lj_radio, self.lj_setup, self.lj_info)
+
+        # EMT
+        self.emt_radio = gtk.RadioButton(
+            self.none_radio, _("EMT - Effective Medium Theory (ASAP)"))
+        self.emt_setup = gtk.combo_box_new_text()
+        self.emt_param_info = {}
+        for p in emt_parameters:
+            self.emt_setup.append_text(p[0])
+            self.emt_param_info[p[0]] = p[1]
+        self.emt_setup.set_active(0)
+        self.emt_info = InfoButton(emt_info_txt)
+        self.pack_line(vbox, self.emt_radio, self.emt_setup, self.emt_info)
+
+        # EMT (ASE implementation)
+        self.aseemt_radio = gtk.RadioButton(
+            self.none_radio, _("EMT - Effective Medium Theory (ASE)"))
+        self.aseemt_info = InfoButton(aseemt_info_txt)
+        self.pack_line(vbox, self.aseemt_radio, None, self.aseemt_info)
+
+        # Brenner potential
+        self.brenner_radio = gtk.RadioButton(
+            self.none_radio, _("Brenner Potential (ASAP)"))
+        self.brenner_info = InfoButton(brenner_info_txt)
+        self.pack_line(vbox, self.brenner_radio, None, self.brenner_info)
+        
+        # GPAW
+        self.gpaw_radio = gtk.RadioButton(self.none_radio,
+                                          _("Density Functional Theory (GPAW)")
+                                          )
+        self.gpaw_setup = gtk.Button(_("Setup"))
+        self.gpaw_info = InfoButton(gpaw_info_txt)
+        self.gpaw_setup.connect("clicked", self.gpaw_setup_window)
+        self.pack_line(vbox, self.gpaw_radio, self.gpaw_setup, self.gpaw_info)
+        
+        # FHI-aims
+        self.aims_radio = gtk.RadioButton(self.none_radio, 
+                                          _("Density Functional Theory "
+                                            "(FHI-aims)"))
+        self.aims_setup = gtk.Button(_("Setup"))
+        self.aims_info = InfoButton(aims_info_txt)
+        self.aims_setup.connect("clicked", self.aims_setup_window)
+        self.pack_line(vbox, self.aims_radio, self.aims_setup, self.aims_info)
+        
+        # VASP
+        self.vasp_radio = gtk.RadioButton(self.none_radio, 
+                                          _("Density Functional Theory "
+                                            "(VASP)"))
+        self.vasp_setup = gtk.Button(_("Setup"))
+        self.vasp_info = InfoButton(vasp_info_txt)
+        self.vasp_setup.connect("clicked", self.vasp_setup_window)
+        self.pack_line(vbox, self.vasp_radio, self.vasp_setup, self.vasp_info)
+
+        # Buttons etc.
+        pack(vbox, gtk.Label(""))
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [buts], end=True, bottom=True)
+        self.check = gtk.CheckButton(_("Check that the calculator is "
+                                       "reasonable."))
+        self.check.set_active(True)
+        fr = gtk.Frame()
+        fr.add(self.check)
+        fr.show_all()
+        pack(vbox, [fr], end=True, bottom=True)
+        
+        # Finalize setup
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+        self.load_state()
+        
+    def pack_line(self, box, radio, setup, info):
+        hbox = gtk.HBox()
+        hbox.pack_start(radio, 0, 0)
+        hbox.pack_start(gtk.Label("  "), 0, 0)
+        hbox.pack_end(info, 0, 0)
+        if setup is not None:
+            radio.connect("toggled", self.radio_toggled, setup)
+            setup.set_sensitive(False)
+            hbox.pack_end(setup, 0, 0)
+        hbox.show_all()
+        box.pack_start(hbox, 0, 0)
+
+    def radio_toggled(self, radio, button):
+        button.set_sensitive(radio.get_active())
+
+    def lj_setup_window(self, widget):
+        if not self.get_atoms():
+            return
+        lj_param = getattr(self, "lj_parameters", None)
+        LJ_Window(self, lj_param, "lj_parameters")
+        # When control is retuned, self.lj_parameters has been set.
+        
+    def gpaw_setup_window(self, widget):
+        if not self.get_atoms():
+            return
+        gpaw_param = getattr(self, "gpaw_parameters", None)
+        GPAW_Window(self, gpaw_param, "gpaw_parameters")
+        # When control is retuned, self.gpaw_parameters has been set.
+        
+    def aims_setup_window(self, widget):
+        if not self.get_atoms():
+            return
+        aims_param = getattr(self, "aims_parameters", None)
+        AIMS_Window(self, aims_param, "aims_parameters")        
+        # When control is retuned, self.aims_parameters has been set.
+
+    def vasp_setup_window(self, widget):
+        if not self.get_atoms():
+            return
+        vasp_param = getattr(self, "vasp_parameters", None)
+        VASP_Window(self, vasp_param, "vasp_parameters")
+        # When control is retuned, self.vasp_parameters has been set.
+
+    def get_atoms(self):
+        "Make an atoms object from the active frame"
+        images = self.gui.images
+        frame = self.gui.frame
+        if images.natoms < 1:
+            oops(_("No atoms present"))
+            return False
+        self.atoms = Atoms(positions=images.P[frame],
+                           symbols=images.Z,
+                           cell=images.A[frame],
+                           pbc=images.pbc,
+                           magmoms=images.M[frame])
+        if not images.dynamic.all(): 
+            from ase.constraints import FixAtoms
+            self.atoms.set_constraint(FixAtoms(mask=1-images.dynamic))
+        return True
+
+    def apply(self, *widget):
+        if self.do_apply():
+            self.save_state()
+            return True
+        else:
+            return False
+        
+    def do_apply(self):
+        nochk = not self.check.get_active()
+        self.gui.simulation["progress"] = DefaultProgressIndicator()
+        if self.none_radio.get_active():
+            self.gui.simulation['calc'] = None
+            return True
+        elif self.lj_radio.get_active():
+            if nochk or self.lj_check():
+                self.choose_lj()
+                return True
+        elif self.emt_radio.get_active():
+            if nochk or self.emt_check():
+                self.choose_emt()
+                return True
+        elif self.aseemt_radio.get_active():
+            if nochk or self.aseemt_check():
+                self.choose_aseemt()
+                return True
+        elif self.brenner_radio.get_active():
+            if nochk or self.brenner_check():
+                self.choose_brenner()
+                return True
+        elif self.gpaw_radio.get_active():
+            if nochk or self.gpaw_check():
+                self.choose_gpaw()
+                return True
+        elif self.aims_radio.get_active():
+            if nochk or self.aims_check():
+                self.choose_aims()
+                return True
+        elif self.vasp_radio.get_active():
+            if nochk or self.vasp_check():
+                self.choose_vasp()
+                return True  
+        return False
+
+    def ok(self, *widget):
+        if self.apply():
+            self.destroy()
+
+    def save_state(self):
+        state = {}
+        for r in self.radios:
+            radiobutton = getattr(self, r+"_radio")
+            if radiobutton.get_active():
+                state["radio"] = r
+        state["emtsetup"] = self.emt_setup.get_active()
+        state["check"] = self.check.get_active()
+        for p in self.paramdicts:
+            if hasattr(self, p):
+                state[p] = getattr(self, p)
+        self.gui.module_state[self.classname] = state
+
+    def load_state(self):
+        try:
+            state = self.gui.module_state[self.classname]
+        except KeyError:
+            return
+        r = state["radio"]
+        radiobutton = getattr(self, r + "_radio")
+        radiobutton.set_active(True)
+        self.emt_setup.set_active(state["emtsetup"])
+        self.check.set_active(state["check"])
+        for p in self.paramdicts:
+            if state.has_key(p):
+                setattr(self, p, state[p])
+            
+    def lj_check(self):
+        try:
+            import asap3
+        except ImportError:
+            oops(_("ASAP is not installed. (Failed to import asap3)"))
+            return False
+        if not hasattr(self, "lj_parameters"):
+            oops(_("You must set up the Lennard-Jones parameters"))
+            return False
+        try:
+            self.atoms.set_calculator(asap3.LennardJones(**self.lj_parameters))
+        except (asap3.AsapError, TypeError, ValueError), e:
+            oops(_("Could not create useful Lennard-Jones calculator."),
+                 str(e))
+            return False
+        return True
+
+    def choose_lj(self):
+        # Define a function on the fly!
+        import asap3
+        def lj_factory(p=self.lj_parameters, lj=asap3.LennardJones):
+            return lj(**p)
+        self.gui.simulation["calc"] = lj_factory
+
+    def emt_get(self):
+        import asap3
+        provider_name = self.emt_setup.get_active_text()
+        provider =  self.emt_param_info[provider_name]
+        if provider is not None:
+            provider = getattr(asap3, provider)
+        return (asap3.EMT, provider, asap3)
+                                      
+    def emt_check(self):
+        if not self.get_atoms():
+            return False
+        try:
+            emt, provider, asap3 = self.emt_get()
+        except ImportError:
+            oops(_("ASAP is not installed. (Failed to import asap3)"))
+            return False
+        try:
+            if provider is not None:
+                self.atoms.set_calculator(emt(provider()))
+            else:
+                self.atoms.set_calculator(emt())
+        except (asap3.AsapError, TypeError, ValueError), e:
+            oops(_("Could not attach EMT calculator to the atoms."),
+                 str(e))
+            return False
+        return True
+
+    def choose_emt(self):
+        emt, provider, asap3 = self.emt_get()
+        if provider is None:
+            emt_factory = emt
+        else:
+            def emt_factory(emt=emt, prov=provider):
+                return emt(prov())
+        self.gui.simulation["calc"] = emt_factory
+
+    def aseemt_check(self):
+        return self.element_check("ASE EMT", ['H', 'Al', 'Cu', 'Ag', 'Au',
+                                              'Ni', 'Pd', 'Pt', 'C', 'N', 'O'])
+
+    def choose_aseemt(self):
+        self.gui.simulation["calc"] = ase.EMT
+        ase.EMT.disabled = False  # In case Asap has been imported.
+
+    def brenner_check(self):
+        try:
+            import asap3
+        except ImportError:
+            oops(_("ASAP is not installed. (Failed to import asap3)"))
+            return False
+        return self.element_check("Brenner potential", ['H', 'C', 'Si'])
+
+    def choose_brenner(self):
+        import asap3
+        self.gui.simulation["calc"] = asap3.BrennerPotential
+
+    def choose_aseemt(self):
+        self.gui.simulation["calc"] = ase.EMT
+        ase.EMT.disabled = False  # In case Asap has been imported.
+
+    def gpaw_check(self):
+        try:
+            import gpaw
+        except ImportError:
+            oops(_("GPAW is not installed. (Failed to import gpaw)"))
+            return False
+        if not hasattr(self, "gpaw_parameters"):
+            oops(_("You must set up the GPAW parameters"))
+            return False
+        return True
+
+    def choose_gpaw(self):
+        # This reuses the same GPAW object.
+        try:
+            import gpaw
+        except ImportError:
+            oops(_("GPAW is not installed. (Failed to import gpaw)"))
+            return False
+        p = self.gpaw_parameters
+        use = ["xc", "kpts", "mode"]
+        if p["use_h"]:
+            use.append("h")
+        else:
+            use.append("gpts")
+        if p["mode"] == "lcao":
+            use.append("basis")
+        gpaw_param = {}
+        for s in use:
+            gpaw_param[s] = p[s]
+        if p["use mixer"]:
+            mx = getattr(gpaw, p["mixer"])
+            mx_args = {}
+            mx_arg_n = ["beta", "nmaxold", "weight"]
+            if p["mixer"] == "MixerDiff":
+                mx_arg_n.extend(["beta_m", "nmaxold_m", "weight_m"])
+            for s in mx_arg_n:
+                mx_args[s] = p[s]
+            gpaw_param["mixer"] = mx(**mx_args)
+        progress = GpawProgressIndicator()
+        self.gui.simulation["progress"] = progress
+        gpaw_param["txt"] = progress.get_gpaw_stream()
+        gpaw_calc = gpaw.GPAW(**gpaw_param)
+        def gpaw_factory(calc = gpaw_calc):
+            return calc
+        self.gui.simulation["calc"] = gpaw_factory
+                
+    def aims_check(self):
+        if not hasattr(self, "aims_parameters"):
+            oops(_("You must set up the FHI-aims parameters"))
+            return False
+        return True
+
+    def choose_aims(self):
+        param = self.aims_parameters
+        from ase.calculators.aims import Aims
+        calc_aims = Aims(**param)
+        def aims_factory(calc = calc_aims):
+            return calc
+        self.gui.simulation["calc"] = aims_factory
+
+    def vasp_check(self):
+        if not hasattr(self, "vasp_parameters"):
+            oops(_("You must set up the VASP parameters"))
+            return False
+        return True
+
+    def choose_vasp(self):
+        param = self.vasp_parameters
+        from ase.calculators.vasp import Vasp
+        calc_vasp = Vasp(**param)
+        def vasp_factory(calc = calc_vasp):
+            return calc
+        self.gui.simulation["calc"] = vasp_factory
+
+    def element_check(self, name, elements):
+        "Check that all atoms are allowed"
+        elements = [ase.data.atomic_numbers[s] for s in elements]
+        elements_dict = {}
+        for e in elements:
+            elements_dict[e] = True
+        if not self.get_atoms():
+            return False
+        try:
+            for e in self.atoms.get_atomic_numbers():
+                elements_dict[e]
+        except KeyError:
+            oops(_("Element %(sym)s not allowed by the '%(name)s' calculator")
+                 % dict(sym=ase.data.chemical_symbols[e], name=name))
+            return False
+        return True
+    
+class InfoButton(gtk.Button):
+    def __init__(self, txt):
+        gtk.Button.__init__(self, _("Info"))
+        self.txt = txt
+        self.connect('clicked', self.run)
+
+    def run(self, widget):
+        dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
+                                   type=gtk.MESSAGE_INFO,
+                                   buttons=gtk.BUTTONS_CLOSE)
+        dialog.set_markup(self.txt)
+        dialog.connect('response', lambda x, y: dialog.destroy())
+        dialog.show()
+
+
+class LJ_Window(gtk.Window):
+    def __init__(self, owner, param, attrname):
+        gtk.Window.__init__(self)
+        self.set_title(_("Lennard-Jones parameters"))
+        self.owner = owner
+        self.attrname = attrname
+        atoms = owner.atoms
+        atnos = atoms.get_atomic_numbers()
+        found = {}
+        for z in atnos:
+            found[z] = True
+        self.present = found.keys()
+        self.present.sort()  # Sorted list of atomic numbers
+        nelem = len(self.present)
+        vbox = gtk.VBox()
+        label = gtk.Label(_("Specify the Lennard-Jones parameters here"))
+        pack(vbox, [label])
+        pack(vbox, gtk.Label(""))
+        pack(vbox, [gtk.Label(_("Epsilon (eV):"))])
+        tbl, self.epsilon_adj = self.makematrix(self.present)
+        pack(vbox, [tbl])
+        pack(vbox, gtk.Label(""))
+        pack(vbox, [gtk.Label(_("Sigma (Å):"))])
+        tbl, self.sigma_adj = self.makematrix(self.present)
+        pack(vbox, [tbl])
+        # TRANSLATORS: Shift roughly means adjust (about a potential)
+        self.modif = gtk.CheckButton(_("Shift to make smooth at cutoff"))
+        self.modif.set_active(True)
+        pack(vbox, gtk.Label(""))
+        pack(vbox, self.modif)
+        pack(vbox, gtk.Label(""))
+        butbox = gtk.HButtonBox()
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', lambda widget: self.destroy())
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', self.ok)
+        butbox.pack_start(cancel_but, 0, 0)
+        butbox.pack_start(ok_but, 0, 0)
+        butbox.show_all()
+        pack(vbox, [butbox], end=True, bottom=True)
+        vbox.show()
+        self.add(vbox)
+
+        # Now, set the parameters
+        if param and param['elements'] == self.present:
+            self.set_param(self.epsilon_adj, param["epsilon"], nelem)
+            self.set_param(self.sigma_adj, param["sigma"], nelem)
+            self.modif.set_active(param["modified"])
+
+        self.show()
+        self.grab_add()  # Lock all other windows
+        
+    def makematrix(self, present):
+        nelem = len(present)
+        adjdict = {}
+        tbl = gtk.Table(2+nelem, 2+nelem)
+        for i in range(nelem):
+            s = chemical_symbols[present[i]]
+            tbl.attach(gtk.Label(" " + str(present[i])), 0, 1, i, i+1)
+            tbl.attach(gtk.Label("  "+s+" "), 1, 2, i, i+1)
+            tbl.attach(gtk.Label(str(present[i])), i+2, i+3, 1+nelem, 2+nelem)
+            tbl.attach(gtk.Label(s), i+2, i+3, nelem, 1+nelem)
+            for j in range(i+1):
+                adj = gtk.Adjustment(1.0, 0.0, 100.0, 0.1)
+                spin = gtk.SpinButton(adj, 0.1, 3)
+                tbl.attach(spin, 2+j, 3+j, i, i+1)
+                adjdict[(i,j)] = adj
+        tbl.show_all()
+        return tbl, adjdict
+    
+    def set_param(self, adj, params, n):
+        for i in range(n):
+            for j in range(n):
+                if j <= i:
+                    adj[(i,j)].value = params[i,j]
+
+    def get_param(self, adj, params, n):
+        for i in range(n):
+            for j in range(n):
+                if j <= i:
+                    params[i,j] = params[j,i] = adj[(i,j)].value
+
+
+    def destroy(self):
+        self.grab_remove()
+        gtk.Window.destroy(self)
+
+    def ok(self, *args):
+        params = {}
+        params["elements"] = copy(self.present)
+        n = len(self.present)
+        eps = np.zeros((n,n))
+        self.get_param(self.epsilon_adj, eps, n)
+        sigma = np.zeros((n,n))
+        self.get_param(self.sigma_adj, sigma, n)
+        params["epsilon"] = eps
+        params["sigma"] = sigma
+        params["modified"] = self.modif.get_active()
+        setattr(self.owner, self.attrname, params)
+        self.destroy()
+
+
+class GPAW_Window(gtk.Window):
+    gpaw_xc_list = ['LDA', 'PBE', 'RPBE', 'revPBE']
+    gpaw_xc_default = 'PBE'
+    def __init__(self, owner, param, attrname):
+        gtk.Window.__init__(self)
+        self.set_title(_("GPAW parameters"))
+        self.owner = owner
+        self.attrname = attrname
+        atoms = owner.atoms
+        self.ucell = atoms.get_cell()
+        self.size = tuple([self.ucell[i,i] for i in range(3)])
+        self.pbc = atoms.get_pbc()
+        self.orthogonal = self.isorthogonal(self.ucell)
+        self.natoms = len(atoms)
+        
+        vbox = gtk.VBox()
+        #label = gtk.Label("Specify the GPAW parameters here")
+        #pack(vbox, [label])
+
+        # Print some info
+        txt = _("%i atoms.\n") % (self.natoms,)
+        if self.orthogonal:
+            txt += _("Orthogonal unit cell: %.2f x %.2f x %.2f Å.") % self.size
+        else:
+            txt += _("Non-orthogonal unit cell:\n")
+            txt += str(self.ucell)
+        pack(vbox, [gtk.Label(txt)])
+        
+        # XC potential
+        self.xc = gtk.combo_box_new_text()
+        for i, x in enumerate(self.gpaw_xc_list):
+            self.xc.append_text(x)
+            if x == self.gpaw_xc_default:
+                self.xc.set_active(i)
+        pack(vbox, [gtk.Label(_("Exchange-correlation functional: ")),
+                    self.xc])
+        
+        # Grid spacing
+        self.radio_h = gtk.RadioButton(None, _("Grid spacing"))
+        self.h = gtk.Adjustment(0.18, 0.0, 1.0, 0.01)
+        self.h_spin = gtk.SpinButton(self.h, 0, 2)
+        pack(vbox, [self.radio_h, gtk.Label(" h = "), self.h_spin,
+                    gtk.Label(_("Å"))])
+        self.radio_gpts = gtk.RadioButton(self.radio_h, _("Grid points"))
+        self.gpts = []
+        self.gpts_spin = []
+        for i in range(3):
+            g = gtk.Adjustment(4, 4, 1000, 4)
+            s = gtk.SpinButton(g, 0, 0)
+            self.gpts.append(g)
+            self.gpts_spin.append(s)
+        self.gpts_hlabel = gtk.Label("")
+        self.gpts_hlabel_format = _("h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å")
+        pack(vbox, [self.radio_gpts, gtk.Label(" gpts = ("), self.gpts_spin[0],
+                    gtk.Label(", "), self.gpts_spin[1], gtk.Label(", "),
+                    self.gpts_spin[2], gtk.Label(")  "), self.gpts_hlabel])
+        self.radio_h.connect("toggled", self.radio_grid_toggled)
+        self.radio_gpts.connect("toggled", self.radio_grid_toggled)
+        self.radio_grid_toggled(None)
+        for g in self.gpts:
+            g.connect("value-changed", self.gpts_changed)
+        self.h.connect("value-changed", self.h_changed)
+        
+        # K-points
+        self.kpts = []
+        self.kpts_spin = []
+        for i in range(3):
+            if self.pbc[i] and self.orthogonal:
+                default = np.ceil(20.0 / self.size[i])
+            else:
+                default = 1
+            g = gtk.Adjustment(default, 1, 100, 1)
+            s = gtk.SpinButton(g, 0, 0)
+            self.kpts.append(g)
+            self.kpts_spin.append(s)
+            if not self.pbc[i]:
+                s.set_sensitive(False)
+            g.connect("value-changed", self.k_changed)
+        pack(vbox, [gtk.Label(_("k-points  k = (")), self.kpts_spin[0],
+                    gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
+                    self.kpts_spin[2], gtk.Label(")")])
+        self.kpts_label = gtk.Label("")
+        self.kpts_label_format = _("k-points x size:  (%.1f, %.1f, %.1f) Å")
+        pack(vbox, [self.kpts_label])
+        self.k_changed()
+        
+        # Spin polarized
+        self.spinpol = gtk.CheckButton(_("Spin polarized"))
+        pack(vbox, [self.spinpol])
+        pack(vbox, gtk.Label(""))
+
+        # Mode and basis functions
+        self.mode = gtk.combo_box_new_text()
+        self.mode.append_text(_("FD - Finite Difference (grid) mode"))
+        self.mode.append_text(_("LCAO - Linear Combination of Atomic "
+                                "Orbitals"))
+        self.mode.set_active(0)
+        pack(vbox, [gtk.Label(_("Mode: ")), self.mode])
+        self.basis = gtk.combo_box_new_text()
+        self.basis.append_text(_("sz - Single Zeta"))
+        self.basis.append_text(_("szp - Single Zeta polarized"))
+        self.basis.append_text(_("dzp - Double Zeta polarized"))
+        self.basis.set_active(2) # dzp
+        pack(vbox, [gtk.Label(_("Basis functions: ")), self.basis])
+        pack(vbox, gtk.Label(""))
+        self.mode.connect("changed", self.mode_changed)
+        self.mode_changed()
+        
+        # Mixer
+        self.use_mixer = gtk.CheckButton(_("Non-standard mixer parameters"))
+        pack(vbox, [self.use_mixer])
+        self.radio_mixer = gtk.RadioButton(None, "Mixer   ")
+        self.radio_mixersum = gtk.RadioButton(self.radio_mixer, "MixerSum   ")
+        self.radio_mixerdiff = gtk.RadioButton(self.radio_mixer, "MixerDiff")
+        pack(vbox, [self.radio_mixer, self.radio_mixersum,
+                    self.radio_mixerdiff])
+        self.beta_adj = gtk.Adjustment(0.25, 0.0, 1.0, 0.05)
+        self.beta_spin = gtk.SpinButton(self.beta_adj, 0, 2)
+        self.nmaxold_adj = gtk.Adjustment(3, 1, 10, 1)
+        self.nmaxold_spin = gtk.SpinButton(self.nmaxold_adj, 0, 0)
+        self.weight_adj = gtk.Adjustment(50, 1, 500, 1)
+        self.weight_spin = gtk.SpinButton(self.weight_adj, 0, 0)
+        pack(vbox, [gtk.Label("beta = "), self.beta_spin,
+                    gtk.Label("  nmaxold = "), self.nmaxold_spin,
+                    gtk.Label("  weight = "), self.weight_spin])
+        self.beta_m_adj = gtk.Adjustment(0.70, 0.0, 1.0, 0.05)
+        self.beta_m_spin = gtk.SpinButton(self.beta_m_adj, 0, 2)
+        self.nmaxold_m_adj = gtk.Adjustment(2, 1, 10, 1)
+        self.nmaxold_m_spin = gtk.SpinButton(self.nmaxold_m_adj, 0, 0)
+        self.weight_m_adj = gtk.Adjustment(10, 1, 500, 1)
+        self.weight_m_spin = gtk.SpinButton(self.weight_m_adj, 0, 0)
+        pack(vbox, [gtk.Label("beta_m = "), self.beta_m_spin,
+                    gtk.Label("  nmaxold_m = "), self.nmaxold_m_spin,
+                    gtk.Label("  weight_m = "), self.weight_m_spin])
+        for but in (self.spinpol, self.use_mixer, self.radio_mixer,
+                    self.radio_mixersum, self.radio_mixerdiff):
+            but.connect("clicked", self.mixer_changed)
+        self.mixer_changed()
+        
+        # Eigensolver
+        # Poisson-solver
+        
+        vbox.show()
+        self.add(vbox)
+
+        # Buttons at the bottom
+        pack(vbox, gtk.Label(""))
+        butbox = gtk.HButtonBox()
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', lambda widget: self.destroy())
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', self.ok)
+        butbox.pack_start(cancel_but, 0, 0)
+        butbox.pack_start(ok_but, 0, 0)
+        butbox.show_all()
+        pack(vbox, [butbox], end=True, bottom=True)
+
+        # Set stored parameters
+        if param:
+            self.xc.set_active(param["xc#"])
+            if param["use_h"]:
+                self.radio_h.set_active(True)
+            else:
+                self.radio_gpts.set_active(True)
+            for i in range(3):
+                self.gpts[i].value = param["gpts"][i]
+                self.kpts[i].value = param["kpts"][i]
+            self.spinpol.set_active(param["spinpol"])
+            self.mode.set_active(param["mode#"])
+            self.basis.set_active(param["basis#"])
+            self.use_mixer.set_active(param["use mixer"])
+            getattr(self, "radio_" + param["mixer"].lower()).set_active(True)
+            for t in ("beta", "nmaxold", "weight", "beta_m", "nmaxold_m",
+                      "weight_m"):                    
+                getattr(self, t+"_adj").value = param[t]
+
+        self.show()
+        self.grab_add()  # Lock all other windows
+
+    def radio_grid_toggled(self, widget):
+        hmode = self.radio_h.get_active()
+        self.h_spin.set_sensitive(hmode)
+        for s in self.gpts_spin:
+            s.set_sensitive(not hmode)
+        self.gpts_changed()
+
+    def gpts_changed(self, *args):
+        if self.radio_gpts.get_active():
+            g = np.array([int(g.value) for g in self.gpts])
+            size = np.array([self.ucell[i,i] for i in range(3)])
+            txt = self.gpts_hlabel_format % tuple(size / g)
+            self.gpts_hlabel.set_markup(txt)
+        else:
+            self.gpts_hlabel.set_markup("")
+
+    def h_changed(self, *args):
+        h = self.h.value
+        for i in range(3):
+            g = 4 * round(self.ucell[i,i] / (4*h))
+            self.gpts[i].value = g
+
+    def k_changed(self, *args):
+        size = [self.kpts[i].value * np.sqrt(np.vdot(self.ucell[i],
+                                                     self.ucell[i]))
+                for i in range(3)]
+        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
+
+    def mode_changed(self, *args):
+        self.basis.set_sensitive(self.mode.get_active() == 1)
+
+    def mixer_changed(self, *args):
+        radios = (self.radio_mixer, self.radio_mixersum, self.radio_mixerdiff)
+        spin1 = (self.beta_spin, self.nmaxold_spin, self.weight_spin)
+        spin2 = (self.beta_m_spin, self.nmaxold_m_spin, self.weight_m_spin)
+        if self.use_mixer.get_active():
+            # Mixer parameters can be specified.
+            if self.spinpol.get_active():
+                self.radio_mixer.set_sensitive(False)
+                self.radio_mixersum.set_sensitive(True)
+                self.radio_mixerdiff.set_sensitive(True)
+                if self.radio_mixer.get_active():
+                    self.radio_mixersum.set_active(True)
+            else:
+                self.radio_mixer.set_sensitive(True)
+                self.radio_mixersum.set_sensitive(False)
+                self.radio_mixerdiff.set_sensitive(False)
+                self.radio_mixer.set_active(True)
+            if self.radio_mixerdiff.get_active():
+                active = spin1 + spin2
+                passive = ()
+            else:
+                active = spin1
+                passive = spin2
+            for widget in active:
+                widget.set_sensitive(True)
+            for widget in passive:
+                widget.set_sensitive(False)
+        else:
+            # No mixer parameters
+            for widget in radios + spin1 + spin2:
+                widget.set_sensitive(False)
+                
+    def isorthogonal(self, matrix):
+        ortho = True
+        for i in range(3):
+            for j in range(3):
+                if i != j and matrix[i][j] != 0.0:
+                    ortho = False
+        return ortho
+
+    def ok(self, *args):
+        param = {}
+        param["xc"] = self.xc.get_active_text()
+        param["xc#"] = self.xc.get_active()
+        param["use_h"] = self.radio_h.get_active()
+        param["h"] = self.h.value
+        param["gpts"] = [int(g.value) for g in self.gpts]
+        param["kpts"] = [int(k.value) for k in self.kpts]
+        param["spinpol"] = self.spinpol.get_active()
+        param["mode"] = self.mode.get_active_text().split()[0].lower()
+        param["mode#"] = self.mode.get_active()
+        param["basis"] = self.basis.get_active_text().split()[0].lower()
+        param["basis#"] = self.basis.get_active()
+        param["use mixer"] = self.use_mixer.get_active()
+        if self.radio_mixer.get_active():
+            m = "Mixer"
+        elif self.radio_mixersum.get_active():
+            m = "MixerSum"
+        else:
+            assert self.radio_mixerdiff.get_active()
+            m = "MixerDiff"
+        param["mixer"] = m
+        for t in ("beta", "nmaxold", "weight", "beta_m", "nmaxold_m",
+                  "weight_m"):
+            param[t] = getattr(self, t+"_adj").value
+        setattr(self.owner, self.attrname, param)
+        self.destroy()
+
+class AIMS_Window(gtk.Window):
+    aims_xc_cluster = ['pw-lda','pz-lda','pbe','pbesol','rpbe','revpbe',
+                    'blyp','am05','b3lyp','hse03','hse06','pbe0','pbesol0',
+                    'hf','mp2']
+    aims_xc_periodic = ['pw-lda','pz-lda','pbe','pbesol','rpbe','revpbe',
+                        'blyp','am05']
+    aims_xc_default = 'pbe'
+    aims_relativity_list = ['none','atomic_zora','zora']
+    aims_keyword_gui_list = ['xc','vdw_correction_hirshfeld','k_grid','spin','charge','relativistic',
+                             'sc_accuracy_etot','sc_accuracy_eev','sc_accuracy_rho','sc_accuracy_forces',
+                             'compute_forces','run_command','species_dir','default_initial_moment']
+    def __init__(self, owner, param, attrname):
+        self.owner = owner
+        self.attrname = attrname
+        atoms = owner.atoms
+        self.periodic = atoms.get_pbc().all()
+        if not self.periodic and atoms.get_pbc().any():
+            aims_periodic_warning = True
+            self.periodic = True 
+        else:
+            aims_periodic_warning = False
+        from ase.calculators.aims import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,input_keys
+        self.aims_keyword_list =float_keys+exp_keys+string_keys+int_keys+bool_keys+list_keys+input_keys
+        self.expert_keywords = []
+
+        natoms = len(atoms)
+        gtk.Window.__init__(self)
+        self.set_title(_("FHI-aims parameters"))
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        # Print some info
+        txt = _("%i atoms.\n") % (natoms)
+        if self.periodic:
+            self.ucell = atoms.get_cell()
+            txt += _("Periodic geometry, unit cell is:\n")
+            for i in range(3):
+                txt += "(%8.3f %8.3f %8.3f)\n" % (self.ucell[i][0], self.ucell[i][1], self.ucell[i][2])
+            self.xc_list = self.aims_xc_periodic
+        else:
+            txt += _("Non-periodic geometry.\n")
+            self.xc_list = self.aims_xc_cluster
+        pack(vbox, [gtk.Label(txt)])
+
+        # XC functional & dispersion correction
+        self.xc = gtk.combo_box_new_text()
+        self.xc_setup = False
+        self.TS = gtk.CheckButton(_("Hirshfeld-based dispersion correction"))
+        pack(vbox, [gtk.Label(_("Exchange-correlation functional: ")),self.xc])
+        pack(vbox, [self.TS])
+        pack(vbox, [gtk.Label("")])
+        
+        # k-grid?
+        if self.periodic:
+            self.kpts = []
+            self.kpts_spin = []
+            for i in range(3):
+                default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
+                g = gtk.Adjustment(default, 1, 100, 1)
+                s = gtk.SpinButton(g, 0, 0)
+                self.kpts.append(g)
+                self.kpts_spin.append(s)
+                g.connect("value-changed", self.k_changed)
+            pack(vbox, [gtk.Label(_("k-points  k = (")), self.kpts_spin[0],
+                        gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
+                        self.kpts_spin[2], gtk.Label(")")])
+            self.kpts_label = gtk.Label("")
+            self.kpts_label_format = _("k-points x size:  (%.1f, %.1f, %.1f) Å")
+            pack(vbox, [self.kpts_label])
+            self.k_changed()
+            pack(vbox, gtk.Label(""))
+
+        # Spin polarized, charge, relativity
+        self.spinpol = gtk.CheckButton(_("Spin / initial moment "))
+        self.spinpol.connect('toggled',self.spinpol_changed)
+        self.moment  = gtk.Adjustment(0,-100,100,0.1)
+        self.moment_spin = gtk.SpinButton(self.moment, 0, 0)
+        self.moment_spin.set_digits(2)
+        self.moment_spin.set_sensitive(False)
+        self.charge  = gtk.Adjustment(0,-100,100,0.1)
+        self.charge_spin = gtk.SpinButton(self.charge, 0, 0)
+        self.charge_spin.set_digits(2)
+        self.relativity_type = gtk.combo_box_new_text()
+        for i, x in enumerate(self.aims_relativity_list):
+            self.relativity_type.append_text(x)
+        self.relativity_type.connect('changed',self.relativity_changed)
+        self.relativity_threshold = gtk.Entry(max=8)
+        self.relativity_threshold.set_text('1.00e-12')
+        self.relativity_threshold.set_sensitive(False)
+        pack(vbox, [self.spinpol,
+                    self.moment_spin, 
+                    gtk.Label(_("   Charge")), 
+                    self.charge_spin, 
+                    gtk.Label(_("   Relativity")),
+                    self.relativity_type,
+                    gtk.Label(_(" Threshold")),
+                    self.relativity_threshold])
+        pack(vbox, gtk.Label(""))
+
+        # self-consistency criteria
+        pack(vbox,[gtk.Label(_("Self-consistency convergence:"))])
+        self.sc_tot_energy      = gtk.Adjustment(1e-6, 1e-6, 1e0, 1e-6)
+        self.sc_tot_energy_spin = gtk.SpinButton(self.sc_tot_energy, 0, 0)
+        self.sc_tot_energy_spin.set_digits(6)
+        self.sc_tot_energy_spin.set_numeric(True)
+        self.sc_sum_eigenvalue      = gtk.Adjustment(1e-3, 1e-6, 1e0, 1e-6)
+        self.sc_sum_eigenvalue_spin = gtk.SpinButton(self.sc_sum_eigenvalue, 0, 0)
+        self.sc_sum_eigenvalue_spin.set_digits(6)
+        self.sc_sum_eigenvalue_spin.set_numeric(True)
+        self.sc_density      = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-6)
+        self.sc_density_spin = gtk.SpinButton(self.sc_density, 0, 0)
+        self.sc_density_spin.set_digits(6)
+        self.sc_density_spin.set_numeric(True)
+        self.compute_forces = gtk.CheckButton(_("Compute forces"))
+        self.compute_forces.set_active(True)
+        self.compute_forces.connect("toggled", self.compute_forces_toggled,"")
+        self.sc_forces      = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-6)
+        self.sc_forces_spin = gtk.SpinButton(self.sc_forces, 0, 0)
+        self.sc_forces_spin.set_numeric(True)
+        self.sc_forces_spin.set_digits(6)
+        # XXX: use gtk table for layout.  Spaces will not work well otherwise
+        # (depend on fonts, widget style, ...)
+        # TRANSLATORS: Don't care too much about these, just get approximately
+        # the same string lengths
+        pack(vbox, [gtk.Label(_("Energy:                 ")),
+                    self.sc_tot_energy_spin, 
+                    gtk.Label(_(" eV   Sum of eigenvalues:  ")),
+                    self.sc_sum_eigenvalue_spin,
+                    gtk.Label(_(" eV"))])
+        pack(vbox, [gtk.Label(_("Electron density: ")),
+                    self.sc_density_spin,
+                    gtk.Label(_("        Force convergence:  ")),
+                    self.sc_forces_spin,
+                    gtk.Label(_(" eV/Ang  "))])
+
+        pack(vbox, [self.compute_forces])
+        pack(vbox, gtk.Label(""))
+
+        swin = gtk.ScrolledWindow()
+        swin.set_border_width(0)
+        swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+
+        self.expert_keyword_set = gtk.Entry(max = 55)
+        self.expert_keyword_add = gtk.Button(stock = gtk.STOCK_ADD)
+        self.expert_keyword_add.connect("clicked", self.expert_keyword_import)
+        self.expert_keyword_set.connect("activate", self.expert_keyword_import)
+        pack(vbox,[gtk.Label(_("Additional keywords: ")),
+                   self.expert_keyword_set, 
+                   self.expert_keyword_add])
+
+        self.expert_vbox = gtk.VBox()
+        vbox.pack_start(swin, True, True, 0)
+        swin.add_with_viewport(self.expert_vbox)
+        self.expert_vbox.get_parent().set_shadow_type(gtk.SHADOW_NONE)
+        self.expert_vbox.get_parent().set_size_request(-1, 100)
+        swin.show()
+        self.expert_vbox.show()
+        pack(vbox, gtk.Label(""))
+
+        # run command and species defaults:
+        pack(vbox, gtk.Label(_('FHI-aims execution command: ')))
+        self.run_command = pack(vbox, gtk.Entry(max=0))
+        pack(vbox, gtk.Label(_('Directory for species defaults: ')))
+        self.species_defaults = pack(vbox, gtk.Entry(max=0))
+
+        # set defaults from previous instance of the calculator, if applicable:
+        if param is not None:
+            self.set_param(param)
+        else:
+            self.set_defaults()
+
+        # Buttons at the bottom
+        pack(vbox, gtk.Label(""))
+        butbox = gtk.HButtonBox()
+        default_but = gtk.Button(_("Set Defaults"))
+        default_but.connect("clicked",self.set_defaults)
+        import_control_but = gtk.Button(_("Import control.in"))
+        import_control_but.connect("clicked",self.import_control)
+        export_control_but = gtk.Button(_("Export control.in"))
+        export_control_but.connect("clicked", self.export_control)
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', lambda widget: self.destroy())
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', self.ok)
+        butbox.pack_start(default_but, 0, 0)
+        butbox.pack_start(import_control_but, 0, 0)
+        butbox.pack_start(export_control_but, 0, 0)
+        butbox.pack_start(cancel_but, 0, 0)
+        butbox.pack_start(ok_but, 0, 0)
+        butbox.show_all()
+        pack(vbox, [butbox], end=True, bottom=True)
+        self.expert_vbox.show()
+        vbox.show()
+        self.add(vbox)
+        self.show()
+        self.grab_add() 
+        if aims_periodic_warning:
+            oops(aims_pbc_warning_text)
+
+    def set_defaults(self, *args):
+        atoms = self.owner.atoms.copy()
+        if not self.xc_setup:
+            self.xc_setup = True 
+            for i, x in enumerate(self.xc_list):
+                self.xc.append_text(x)
+        for i, x in enumerate(self.xc_list):
+            if x == self.aims_xc_default:
+                self.xc.set_active(i)
+        self.TS.set_active(False)
+        if self.periodic:
+            self.ucell = atoms.get_cell()
+            for i in range(3):
+                default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
+                self.kpts_spin[i].set_value(default)
+        self.spinpol.set_active(False)
+        self.moment.set_value(0)
+        self.moment_spin.set_sensitive(False)
+        self.charge.set_value(0)
+        aims_relativity_default = 'none'
+        for a in atoms:
+            if a.number > 20: 
+                aims_relativity_default = 'atomic_zora'
+        for i, x in enumerate(self.aims_relativity_list):
+            if x == aims_relativity_default:
+                self.relativity_type.set_active(i)
+        self.sc_tot_energy.set_value(1e-6)
+        self.sc_sum_eigenvalue.set_value(1e-3)
+        self.sc_density.set_value(1e-4)
+        self.sc_forces.set_value(1e-4)
+        for key in self.expert_keywords:
+            key[0].destroy()
+            key[1].destroy()
+            key[2].destroy()
+            key[3] = False
+        for child in self.expert_vbox.children():
+            self.expert_vbox.remove(child)
+        if os.environ.has_key('AIMS_COMMAND'):
+            text = os.environ['AIMS_COMMAND']
+        else:
+            text = ""
+        self.run_command.set_text(text)
+        if os.environ.has_key('AIMS_SPECIES_DIR'):
+            text = os.environ['AIMS_SPECIES_DIR']
+        else:
+            text = ""
+        self.species_defaults.set_text(text)
+
+    def set_attributes(self, *args):
+        param = {}
+        param["xc"] = self.xc.get_active_text()
+        if self.periodic:
+            param["k_grid"] = (int(self.kpts[0].value),
+                               int(self.kpts[1].value),
+                               int(self.kpts[2].value))
+        if self.spinpol.get_active():
+            param["spin"] = "collinear"
+            param["default_initial_moment"] = self.moment.get_value()
+        else:
+            param["spin"] = "none"
+            param["default_initial_moment"] = None
+        param["vdw_correction_hirshfeld"] = self.TS.get_active()
+        param["charge"]             = self.charge.value
+        param["relativistic"]       = self.relativity_type.get_active_text()
+        if param["relativistic"] == 'atomic_zora':
+            param["relativistic"] += " scalar "
+        if param["relativistic"] == 'zora':
+            param["relativistic"] += " scalar "+self.relativity_threshold.get_text() 
+        param["sc_accuracy_etot"]   = self.sc_tot_energy.value
+        param["sc_accuracy_eev"]    = self.sc_sum_eigenvalue.value
+        param["sc_accuracy_rho"]    = self.sc_density.value
+        param["compute_forces"]     = self.compute_forces.get_active()
+        param["sc_accuracy_forces"] = self.sc_forces.value
+        param["run_command"]        = self.run_command.get_text()
+        param["species_dir"]        = self.species_defaults.get_text()
+        from ase.calculators.aims import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,input_keys
+        for option in self.expert_keywords:
+            if option[3]:   # set type of parameter according to which list it is in
+                key = option[0].get_text().strip()
+                val = option[1].get_text().strip()
+                if key == 'output':
+                    if param.has_key('output'): 
+                        param[key] += [val]
+                    else:
+                        param[key] = [val]
+                elif key in float_keys or key in exp_keys:
+                    param[key] = float(val)
+                elif key in list_keys or key in string_keys or key in input_keys:
+                    param[key] = val
+                elif key in int_keys:
+                    param[key] = int(val)
+                elif key in bool_keys:
+                    param[key] = bool(val)
+        setattr(self.owner, self.attrname, param)
+
+    def set_param(self, param):
+        if param["xc"] is not None:
+            for i, x in enumerate(self.xc_list):
+                if x == param["xc"]:
+                    self.xc.set_active(i)
+        if isinstance(param["vdw_correction_hirshfeld"],bool):
+            self.TS.set_active(param["vdw_correction_hirshfeld"])
+        if self.periodic and param["k_grid"] is not None:
+            self.kpts[0].value = int(param["k_grid"][0])
+            self.kpts[1].value = int(param["k_grid"][1])
+            self.kpts[2].value = int(param["k_grid"][2])
+        if param["spin"] is not None:
+            self.spinpol.set_active(param["spin"] == "collinear")
+            self.moment_spin.set_sensitive(param["spin"] == "collinear")
+        if param["default_initial_moment"] is not None:
+            self.moment.value = param["default_initial_moment"]
+        if param["charge"] is not None:
+            self.charge.value = param["charge"]
+        if param["relativistic"] is not None:
+            if isinstance(param["relativistic"],(tuple,list)):
+                rel = param["relativistic"]
+            else:
+                rel = param["relativistic"].split()
+            for i, x in enumerate(self.aims_relativity_list):
+                if x == rel[0]:
+                    self.relativity_type.set_active(i)
+                    if x == 'zora':
+                        self.relativity_threshold.set_text(rel[2])
+                        self.relativity_threshold.set_sensitive(True)
+        if param["sc_accuracy_etot"] is not None:
+            self.sc_tot_energy.value     = param["sc_accuracy_etot"]
+        if param["sc_accuracy_eev"] is not None:
+            self.sc_sum_eigenvalue.value = param["sc_accuracy_eev"]
+        if param["sc_accuracy_rho"] is not None:
+            self.sc_density.value        = param["sc_accuracy_rho"]
+        if param["compute_forces"] is not None:
+            if param["compute_forces"]:
+                if param["sc_accuracy_forces"] is not None:
+                    self.sc_forces.value = param["sc_accuracy_forces"]
+                self.compute_forces.set_active(param["compute_forces"])
+            else: 
+                self.compute_forces.set_active(False)
+        if param["run_command"] is not None:
+            self.run_command.set_text(param["run_command"])
+        if param["species_dir"] is not None:
+            self.species_defaults.set_text(param["species_dir"])
+        for (key,val) in param.items():
+            if key in self.aims_keyword_list and key not in self.aims_keyword_gui_list:
+                if val is not None:  # = existing "expert keyword"
+                    if key == 'output': # 'output' can be used more than once
+                        options = val
+                        if isinstance(options,str): options = [options]
+                        for arg in options:
+                            self.expert_keyword_create([key]+[arg])
+                    else:
+                        if isinstance(val,str):
+                            arg = [key]+val.split()
+                        elif isinstance(val,(tuple,list)):
+                            arg = [key]+[str(a) for a in val]
+                        else:
+                            arg = [key]+[str(val)]
+                        self.expert_keyword_create(arg)
+
+    def ok(self, *args):
+        self.set_attributes(*args)
+        self.destroy()
+
+    def export_control(self, *args):
+        filename = "control.in"
+        chooser = gtk.FileChooserDialog(
+            _('Export parameters ... '), None, gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        chooser.set_filename(filename)
+        save = chooser.run()
+        if save == gtk.RESPONSE_OK or save == gtk.RESPONSE_SAVE:
+            filename = chooser.get_filename()
+            self.set_attributes(*args)
+            param = getattr(self.owner, "aims_parameters")
+            from ase.calculators.aims import Aims
+            calc_temp = Aims(**param)
+            atoms_temp = self.owner.atoms.copy()
+            atoms_temp.set_calculator(calc_temp)
+            atoms_temp.calc.write_control(file = filename)
+            atoms_temp.calc.write_species(file = filename)
+        chooser.destroy()
+
+    def import_control(self, *args):
+        filename = "control.in"
+        chooser = gtk.FileChooserDialog(
+            _('Import control.in file ... '), None, 
+            gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        chooser.set_filename(filename)
+        save = chooser.run()
+        if save == gtk.RESPONSE_OK:
+            self.set_defaults()
+            filename = chooser.get_filename()
+            control = open(filename,'r')
+            while True:
+                line = control.readline()
+                if not line:
+                    break
+                if "List of parameters used to initialize the calculator:" in line:
+                    control.readline()
+                    from ase.io.aims import read_aims_calculator
+                    calc = read_aims_calculator(control)
+                    found_aims_calculator = True
+            control.close()
+            if found_aims_calculator:
+                param = calc.float_params
+                for key in calc.exp_params:
+                    param[key] = calc.exp_params[key]
+                for key in calc.string_params:
+                    param[key] = calc.string_params[key]
+                for key in calc.int_params:
+                    param[key] = calc.int_params[key]
+                for key in calc.bool_params:
+                    param[key] = calc.bool_params[key]
+                for key in calc.list_params:
+                    param[key] = calc.list_params[key]
+                for key in calc.input_parameters:
+                    param[key] = calc.input_parameters[key]
+                self.set_defaults()
+                self.set_param(param)
+        chooser.destroy()
+
+    def k_changed(self, *args):
+        size = [self.kpts[i].value * np.sqrt(np.vdot(self.ucell[i],self.ucell[i])) for i in range(3)]
+        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
+
+    def compute_forces_toggled(self, *args):
+        self.sc_forces_spin.set_sensitive(self.compute_forces.get_active())
+
+    def relativity_changed(self, *args):
+        self.relativity_threshold.set_sensitive(self.relativity_type.get_active() == 2)
+
+    def spinpol_changed(self, *args):
+        self.moment_spin.set_sensitive(self.spinpol.get_active())
+
+    def expert_keyword_import(self, *args):
+        command = self.expert_keyword_set.get_text().split()
+        if len(command) > 0 and command[0] in self.aims_keyword_list and not command[0] in self.aims_keyword_gui_list:
+            self.expert_keyword_create(command)
+        elif command[0] in self.aims_keyword_gui_list:
+            oops(_("Please use the facilities provided in this window to "
+                   "manipulate the keyword: %s!") % command[0])
+        else:
+            oops(_("Don't know this keyword: %s\n"
+                   "\nPlease check!\n\n" 
+                   "If you really think it should be available, "
+                   "please add it to the top of ase/calculators/aims.py.")
+                 % command[0])
+        self.expert_keyword_set.set_text("")
+
+    def expert_keyword_create(self, command):
+        key = command[0]
+        argument = command[1]
+        if len(command) > 2:
+            for a in command[2:]:
+                argument += ' '+a
+        index = len(self.expert_keywords) 
+        self.expert_keywords += [[gtk.Label("    " +key+"  "),
+                                  gtk.Entry(max=45),
+                                  ExpertDeleteButton(index),
+                                  True]]
+        self.expert_keywords[index][1].set_text(argument)
+        self.expert_keywords[index][2].connect('clicked',self.expert_keyword_delete)
+        if not self.expert_vbox.get_children():
+            table = gtk.Table(1, 3)
+            table.attach(self.expert_keywords[index][0], 0, 1, 0, 1, 0)
+            table.attach(self.expert_keywords[index][1], 1, 2, 0, 1, 0)
+            table.attach(self.expert_keywords[index][2], 2, 3, 0, 1, 0)
+            table.show_all()
+            pack(self.expert_vbox, table)
+        else:
+            table = self.expert_vbox.get_children()[0]
+            nrows = table.get_property('n-rows')
+            table.resize(nrows + 1, 3)
+            table.attach(self.expert_keywords[index][0],  0, 1, nrows, nrows + 1, 0) 
+            table.attach(self.expert_keywords[index][1],  1, 2, nrows, nrows + 1, 0) 
+            table.attach(self.expert_keywords[index][2],  2, 3, nrows, nrows + 1, 0) 
+            table.show_all()
+
+    def expert_keyword_delete(self, button, *args):
+        index = button.index   # which one to kill 
+        for i in [0,1,2]:
+            self.expert_keywords[index][i].destroy()
+        table = self.expert_vbox.get_children()[0]
+        nrows = table.get_property('n-rows')
+        table.resize(nrows-1, 3)
+        self.expert_keywords[index][3] = False
+
+
+class ExpertDeleteButton(gtk.Button):
+    def __init__(self, index):
+        gtk.Button.__init__(self, stock=gtk.STOCK_DELETE)
+        alignment = self.get_children()[0]
+        hbox = alignment.get_children()[0]
+        #self.set_size_request(1, 3)
+        image, label = hbox.get_children()
+        if image is not None: 
+            label.set_text('Del')
+        self.index = index
+
+
+class VASP_Window(gtk.Window):
+    vasp_xc_list = ['PW91', 'PBE', 'LDA']
+    vasp_xc_default = 'PBE'
+    vasp_prec_default = 'Normal'
+    def __init__(self, owner, param, attrname):
+        self.owner = owner
+        self.attrname = attrname
+        atoms = owner.atoms
+        self.periodic = atoms.get_pbc().all()
+        self.vasp_keyword_gui_list = ['ediff','encut', 'ismear', 'ispin', 'prec', 'sigma']
+        from ase.calculators.vasp import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,special_keys
+        self.vasp_keyword_list = float_keys+exp_keys+string_keys+int_keys+bool_keys+list_keys+special_keys
+        self.expert_keywords = []
+        natoms = len(atoms)
+        gtk.Window.__init__(self)
+        self.set_title(_("VASP parameters"))
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        # Print some info
+        txt = _("%i atoms.\n") % natoms
+        self.ucell = atoms.get_cell()
+        txt += _("Periodic geometry, unit cell is: \n")
+        for i in range(3):
+            txt += "(%8.3f %8.3f %8.3f)\n" % (self.ucell[i][0], self.ucell[i][1], self.ucell[i][2])
+        pack(vbox, [gtk.Label(txt)])
+
+        # XC functional ()
+        self.xc = gtk.combo_box_new_text()
+        for i, x in enumerate(self.vasp_xc_list):
+            self.xc.append_text(x)
+
+        # Spin polarized
+        self.spinpol = gtk.CheckButton(_("Spin polarized"))
+        
+        pack(vbox, [gtk.Label(_("Exchange-correlation functional: ")),
+                    self.xc,
+                    gtk.Label("    "),
+                    self.spinpol])
+        pack(vbox, gtk.Label(""))
+
+        # k-grid
+        self.kpts = []
+        self.kpts_spin = []
+        for i in range(3):
+            default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
+            g = gtk.Adjustment(default, 1, 100, 1)
+            s = gtk.SpinButton(g, 0, 0)
+            self.kpts.append(g)
+            self.kpts_spin.append(s)
+            g.connect("value-changed", self.k_changed)
+
+        # Precision of calculation
+        self.prec = gtk.combo_box_new_text()
+        for i, x in enumerate(['Low', 'Normal', 'Accurate']):
+            self.prec.append_text(x)
+            if x == self.vasp_prec_default:
+                self.prec.set_active(i)
+
+        # cutoff energy
+        if os.environ.has_key('VASP_PP_PATH'):
+            self.encut_min_default, self.encut_max_default = self.get_min_max_cutoff()
+        else:
+            self.encut_max_default = 400.0
+            self.encut_min_default = 100.0
+        self.encut = gtk.Adjustment(self.encut_max_default, 0, 9999, 10)
+        self.encut_spin = gtk.SpinButton(self.encut, 0, 0)
+        self.encut_spin.set_digits(2)
+        self.encut_spin.connect("value-changed",self.check_encut_warning)
+        self.encut_warning = gtk.Label("")
+
+        pack(vbox, [gtk.Label(_("k-points  k = (")), self.kpts_spin[0],
+                    gtk.Label(", "), self.kpts_spin[1], gtk.Label(", "),
+                    self.kpts_spin[2], 
+                    gtk.Label(_(")    Cutoff: ")),self.encut_spin,
+                    gtk.Label(_("    Precision: ")),self.prec])
+        self.kpts_label = gtk.Label("")
+        self.kpts_label_format = _("k-points x size:  (%.1f, %.1f, %.1f) Å       ")
+        pack(vbox, [self.kpts_label, self.encut_warning])
+        self.k_changed()
+        pack(vbox, gtk.Label(""))
+
+        self.ismear = gtk.combo_box_new_text()
+        for x in ['Fermi', 'Gauss', 'Methfessel-Paxton']:
+            self.ismear.append_text(x)
+        self.ismear.set_active(2)
+        self.smearing_order = gtk.Adjustment(2,0,9,1)
+        self.smearing_order_spin = gtk.SpinButton(self.smearing_order,0,0)
+        self.smearing_order_spin.set_digits(0)
+        self.ismear.connect("changed", self.check_ismear_changed)
+        self.sigma = gtk.Adjustment(0.1, 0.001, 9.0, 0.1)
+        self.sigma_spin = gtk.SpinButton(self.sigma,0,0)
+        self.sigma_spin.set_digits(3)
+        pack(vbox, [gtk.Label(_("Smearing: ")),
+                    self.ismear,
+                    gtk.Label(_(" order: ")),
+                    self.smearing_order_spin,
+                    gtk.Label(_(" width: ")),
+                    self.sigma_spin])
+        pack(vbox, gtk.Label(""))
+        
+        self.ediff = gtk.Adjustment(1e-4, 1e-6, 1e0, 1e-4)
+        self.ediff_spin = gtk.SpinButton(self.ediff, 0, 0)
+        self.ediff_spin.set_digits(6)
+        pack(vbox,[gtk.Label(_("Self-consistency convergence: ")),
+                   self.ediff_spin,
+                   gtk.Label(_(" eV"))])
+        pack(vbox,gtk.Label(""))
+
+        swin = gtk.ScrolledWindow()
+        swin.set_border_width(0)
+        swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+
+        self.expert_keyword_set = gtk.Entry(max = 55)
+        self.expert_keyword_add = gtk.Button(stock = gtk.STOCK_ADD)
+        self.expert_keyword_add.connect("clicked", self.expert_keyword_import)
+        self.expert_keyword_set.connect("activate", self.expert_keyword_import)
+        pack(vbox,[gtk.Label(_("Additional keywords: ")),
+                   self.expert_keyword_set, 
+                   self.expert_keyword_add])
+        self.expert_vbox = gtk.VBox()
+        vbox.pack_start(swin, True, True, 0)
+        swin.add_with_viewport(self.expert_vbox)
+        self.expert_vbox.get_parent().set_shadow_type(gtk.SHADOW_NONE)
+        self.expert_vbox.get_parent().set_size_request(-1, 100)
+        swin.show()
+        self.expert_vbox.show()
+        pack(vbox, gtk.Label(""))
+
+        # run command and location of POTCAR files:
+        pack(vbox, gtk.Label(_('VASP execution command: ')))
+        self.run_command = pack(vbox, gtk.Entry(max=0))
+        if os.environ.has_key('VASP_COMMAND'):
+            self.run_command.set_text(os.environ['VASP_COMMAND'])
+        pack(vbox, gtk.Label(_('Directory for species defaults: ')))
+        self.pp_path = pack(vbox, gtk.Entry(max=0))
+        if os.environ.has_key('VASP_PP_PATH'):
+            self.pp_path.set_text(os.environ['VASP_PP_PATH'])
+
+        # Buttons at the bottom
+        pack(vbox, gtk.Label(""))
+        butbox = gtk.HButtonBox()
+        set_default_but = gtk.Button(_("Set Defaults"))
+        set_default_but.connect("clicked", self.set_defaults)
+        import_vasp_but = gtk.Button(_("Import VASP files"))
+        import_vasp_but.connect("clicked", self.import_vasp_files)
+        export_vasp_but = gtk.Button(_("Export VASP files"))
+        export_vasp_but.connect("clicked", self.export_vasp_files)
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', lambda widget: self.destroy())
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', self.ok)
+        butbox.pack_start(set_default_but, 0, 0)
+        butbox.pack_start(import_vasp_but, 0, 0)
+        butbox.pack_start(export_vasp_but, 0, 0)
+        butbox.pack_start(cancel_but, 0, 0)
+        butbox.pack_start(ok_but, 0, 0)
+        butbox.show_all()
+        pack(vbox, [butbox], end=True, bottom=True)
+        vbox.show()
+        self.add(vbox)
+        self.show()
+        self.grab_add()  # Lock all other windows
+
+        self.load_attributes()
+
+    def load_attributes(self, directory = "."):
+        """Sets values of fields of the window according to the values 
+        set inside the INCAR, KPOINTS and POTCAR file in 'directory'."""
+        from os import chdir
+        chdir(directory)
+
+
+       
+        # Try and load INCAR, in the current directory
+        from ase.calculators.vasp import Vasp
+        calc_temp = Vasp()
+        try:
+            calc_temp.read_incar("INCAR")
+        except IOError:
+            pass
+        else:
+            if calc_temp.spinpol:
+                self.spinpol.set_active(True)
+            else:
+                self.spinpol.set_active(False)
+
+            if calc_temp.float_params['encut']:
+                self.encut.set_value(calc_temp.float_params['encut'])
+ 
+            if calc_temp.int_params['ismear'] == -1: # Fermi
+                vasp_ismear_default = 'Fermi'
+            elif calc_temp.int_params['ismear'] == 0: # Gauss
+                vasp_ismear_default = 'Gauss'
+            elif calc_temp.int_params['ismear'] > 0: # Methfessel-Paxton
+                vasp_ismear_default = 'Methfessel-Paxton'
+            else:
+                vasp_ismear_default = None
+
+            for i, x in enumerate(['Fermi', 'Gauss', 'Methfessel-Paxton']):
+                if vasp_ismear_default == x:
+                    self.ismear.set_active(i)
+
+            if calc_temp.exp_params['ediff']:
+                self.ediff.set_value(calc_temp.exp_params['ediff'])
+
+            for i, x in enumerate(['Low', 'Normal', 'Accurate']):
+                if x == calc_temp.string_params['prec']:
+                    self.prec.set_active(i)
+
+            if calc_temp.float_params['sigma']:
+                self.sigma.set_value(calc_temp.float_params['sigma'])
+
+            import copy
+            all_params = copy.deepcopy(calc_temp.float_params)
+            all_params.update(calc_temp.exp_params)
+            all_params.update(calc_temp.string_params)
+            all_params.update(calc_temp.int_params)
+            all_params.update(calc_temp.bool_params)
+            all_params.update(calc_temp.special_params)
+
+            for (key, value) in all_params.items(): 
+                if key in self.vasp_keyword_list \
+                        and key not in self.vasp_keyword_gui_list \
+                        and value is not None:
+                    command = key + " " + str(value)
+                    self.expert_keyword_create(command.split())
+
+            for (key, value) in calc_temp.list_params.items():
+                if key == "magmom" and value is not None:
+                    command = key + " "
+                    rep = 1
+                    previous = value[0]
+                    for v in value[1:]:
+                        if v == previous:
+                            rep += 1
+                        else:
+                            if rep > 1:
+                                command += "%d*%f " % (rep, previous)
+                            else:
+                                command += "%f " % previous
+                            rep = 1
+                        previous = v
+                    if rep > 1:
+                        command += "%d*%f " % (rep, previous)
+                    else:
+                        command += "%f" % previous
+                    self.expert_keyword_create(command.split())
+                elif value is not None:
+                    command = key + " "
+                    for v in value:
+                        command += str(v) + " "
+                    self.expert_keyword_create(command.split())
+                 
+
+
+        # Try and load POTCAR, in the current directory
+        try:
+            calc_temp.read_potcar()
+        except IOError:
+            pass
+        else:
+            #Set xc read from POTCAR
+            for i, x in enumerate(self.vasp_xc_list):
+                if x == calc_temp.input_params['xc']:
+                    self.xc.set_active(i)
+
+        # Try and load KPOINTS, in the current directory
+        try:
+            calc_temp.read_kpoints("KPOINTS")
+        except IOError:
+            pass
+        else:
+            # Set KPOINTS grid dimensions
+            for i in range(3):
+                self.kpts_spin[i].set_value(calc_temp.input_params['kpts'][i])
+
+    def set_attributes(self, *args):
+        self.param = {}
+        self.param["xc"] = self.xc.get_active_text()
+        self.param["prec"] = self.prec.get_active_text()
+        self.param["kpts"] = (int(self.kpts[0].value),
+                              int(self.kpts[1].value),
+                              int(self.kpts[2].value))
+        self.param["encut"] = self.encut.value
+        self.param["ediff"] = self.ediff.value
+        self.param["ismear"] = self.get_ismear()
+        self.param["sigma"] = self.sigma.value
+        if self.spinpol.get_active(): 
+            self.param["ispin"] = 2
+        else:
+            self.param["ispin"] = 1
+        from ase.calculators.vasp import float_keys,exp_keys,string_keys,int_keys,bool_keys,list_keys,special_keys
+        for option in self.expert_keywords:
+            if option[3]:   # set type of parameter accoding to which list it is in
+                key = option[0].get_text().split()[0].strip()
+                val = option[1].get_text().strip()
+                if key in float_keys or key in exp_keys:
+                    self.param[key] = float(val)
+                elif key == "magmom":
+                    val = val.replace("*", " * ")
+                    c = val.split()
+                    val = []
+                    i = 0
+                    while i < len(c):
+                        if c[i] == "*":
+                            b = val.pop()
+                            i += 1
+                            for j in range(int(b)):
+                                val.append(float(c[i]))
+                        else:
+                            val.append(float(c[i]))
+                        i += 1
+                    self.param[key] = val
+                elif key in list_keys:
+                    c = val.split()
+                    val = []
+                    for i in c:
+                        val.append(float(i))
+                    self.param[key] = val
+                elif key in string_keys:
+                    self.param[key] = val
+                elif key in int_keys:
+                    self.param[key] = int(val)
+                elif key in bool_keys:
+                    self.param[key] = bool(val)
+        setattr(self.owner, self.attrname, self.param)
+        os.environ['VASP_COMMAND'] = self.run_command.get_text()
+        os.environ['VASP_PP_PATH'] = self.pp_path.get_text()
+        
+    def ok(self, *args):
+        self.set_attributes(*args)
+        self.destroy()
+
+    def get_min_max_cutoff(self, *args):
+        # determine the recommended energy cutoff limits 
+        from ase.calculators.vasp import Vasp
+        calc_temp = Vasp()
+        atoms_temp = self.owner.atoms.copy()
+        calc_temp.initialize(atoms_temp)
+        calc_temp.write_potcar(suffix = '.check_energy_cutoff')
+        enmin = -1e6
+        enmax = -1e6
+        for line in open("POTCAR.check_energy_cutoff",'r').readlines():
+            if "ENMIN" in line:
+                enmax = max(enmax,float(line.split()[2].split(';')[0]))
+                enmin = max(enmin,float(line.split()[5]))
+        from os import system
+        system("rm POTCAR.check_energy_cutoff")
+        return enmin, enmax
+
+    def k_changed(self, *args):
+        size = [self.kpts[i].value * np.sqrt(np.vdot(self.ucell[i],self.ucell[i])) for i in range(3)]
+        self.kpts_label.set_text(self.kpts_label_format % tuple(size))
+
+    def check_encut_warning(self,*args):
+        if self.encut.value < self.encut_min_default:
+            self.encut_warning.set_markup(_("<b>WARNING:</b> cutoff energy is lower than recommended minimum!"))
+        else:
+            self.encut_warning.set_markup("")
+
+    def check_ismear_changed(self,*args):
+        if self.ismear.get_active_text() == 'Methfessel-Paxton':
+            self.smearing_order_spin.set_sensitive(True)
+        else:
+            self.smearing_order_spin.set_sensitive(False)
+
+    def get_ismear(self,*args):
+        type = self.ismear.get_active_text()
+        if type == 'Methfessel-Paxton':
+            ismear_value = self.smearing_order.value
+        elif type == 'Fermi':
+            ismear_value = -1
+        else:
+            ismear_value = 0
+        return ismear_value
+
+    def destroy(self):
+        self.grab_remove()
+        gtk.Window.destroy(self)
+
+    def set_defaults(self, *args):
+         # Reset fields to what they were
+        self.spinpol.set_active(False)
+
+        for i, x in enumerate(['Low', 'Normal', 'Accurate']):
+            if x == self.vasp_prec_default:
+                self.prec.set_active(i)
+
+        self.encut_spin.set_value(self.encut_max_default)
+
+        self.ismear.set_active(2)
+        self.smearing_order.set_value(2)
+        self.ediff.set_value(1e-4)
+
+        for child in self.expert_vbox.children():
+            self.expert_vbox.remove(child)
+
+        for i, x in enumerate(self.vasp_xc_list):
+                if x == self.vasp_xc_default:
+                    self.xc.set_active(i) 
+
+        default = np.ceil(20.0 / np.sqrt(np.vdot(self.ucell[i],self.ucell[i])))
+        for i in range(3):
+            self.kpts_spin[i].set_value(default)
+
+    def import_vasp_files(self, *args):
+        dirname = ""
+        chooser = gtk.FileChooserDialog(
+            _('Import VASP input files: choose directory ... '),
+            None, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        chooser.set_filename(dirname)
+        openr = chooser.run()
+        if openr == gtk.RESPONSE_OK or openr == gtk.RESPONSE_SAVE:
+            dirname = chooser.get_filename()
+            self.load_attributes(dirname)
+        chooser.destroy()
+            
+
+    def export_vasp_files(self, *args):
+        filename = ""
+        chooser = gtk.FileChooserDialog(
+            _('Export VASP input files: choose directory ... '), 
+            None, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        chooser.set_filename(filename)
+        save = chooser.run()
+        if save == gtk.RESPONSE_OK or save == gtk.RESPONSE_SAVE:
+            filename = chooser.get_filename()
+            from os import chdir
+            chdir(filename)
+            self.set_attributes(*args)
+            param = getattr(self.owner, "vasp_parameters")
+            from ase.calculators.vasp import Vasp
+            calc_temp = Vasp(**param)
+            atoms_temp = self.owner.atoms.copy()
+            atoms_temp.set_calculator(calc_temp)
+            calc_temp.initialize(atoms_temp)
+            calc_temp.write_incar(atoms_temp)
+            calc_temp.write_potcar()
+            calc_temp.write_kpoints()
+            calc_temp.write_sort_file()
+            from ase.io.vasp import write_vasp
+            write_vasp('POSCAR', calc_temp.atoms_sorted, symbol_count = calc_temp.symbol_count)
+        chooser.destroy()
+
+    def expert_keyword_import(self, *args):
+        command = self.expert_keyword_set.get_text().split()
+        if len(command) > 0 and command[0] in self.vasp_keyword_list and not command[0] in self.vasp_keyword_gui_list:
+            self.expert_keyword_create(command)
+        elif command[0] in self.vasp_keyword_gui_list:
+            oops(_("Please use the facilities provided in this window to "
+                   "manipulate the keyword: %s!") % command[0])
+        else:
+            oops(_("Don't know this keyword: %s"
+                   "\nPlease check!\n\n" 
+                   "If you really think it should be available, "
+                   "please add it to the top of ase/calculators/vasp.py.")
+                 % command[0])
+        self.expert_keyword_set.set_text("")
+
+    def expert_keyword_create(self, command):
+        key = command[0]
+        if command[1] == "=":
+            command.remove("=")
+        argument = command[1]
+        if len(command) > 2:
+            for a in command[2:]:
+                argument += ' '+a
+        index = len(self.expert_keywords) 
+        self.expert_keywords += [[gtk.Label("    " +key+" = "),
+                                  gtk.Entry(max=55),
+                                  ExpertDeleteButton(index),
+                                  True]]
+        self.expert_keywords[index][1].set_text(argument)
+        self.expert_keywords[index][2].connect('clicked',self.expert_keyword_delete)
+        if not self.expert_vbox.get_children():
+            table = gtk.Table(1, 3)
+            table.attach(self.expert_keywords[index][0], 0, 1, 0, 1, 0)
+            table.attach(self.expert_keywords[index][1], 1, 2, 0, 1, 0)
+            table.attach(self.expert_keywords[index][2], 2, 3, 0, 1, 0)
+            table.show_all()
+            pack(self.expert_vbox, table)
+        else:
+            table = self.expert_vbox.get_children()[0]
+            nrows = table.get_property('n-rows')
+            table.resize(nrows + 1, 3)
+            table.attach(self.expert_keywords[index][0],  0, 1, nrows, nrows + 1, 0) 
+            table.attach(self.expert_keywords[index][1],  1, 2, nrows, nrows + 1, 0) 
+            table.attach(self.expert_keywords[index][2],  2, 3, nrows, nrows + 1, 0) 
+            table.show_all()
+        
+    def expert_keyword_delete(self, button, *args):
+        index = button.index   # which one to kill 
+        for i in [0,1,2]:
+            self.expert_keywords[index][i].destroy()
+        table = self.expert_vbox.get_children()[0]
+        nrows = table.get_property('n-rows')
+        table.resize(nrows-1, 3)
+        self.expert_keywords[index][3] = False
diff --git a/ase/gui/colors.py b/ase/gui/colors.py
new file mode 100644
index 0000000..dda3920
--- /dev/null
+++ b/ase/gui/colors.py
@@ -0,0 +1,576 @@
+# encoding: utf-8
+"""colors.py - select how to color the atoms in the GUI."""
+
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops, help
+import ase
+from ase.data.colors import jmol_colors
+import numpy as np
+import colorsys
+
+named_colors = ('Green', 'Yellow', 'Blue', 'Red', 'Orange', 'Cyan',
+                'Magenta', 'Black', 'White', 'Grey', 'Violet', 'Brown',
+                'Navy')
+
+class ColorWindow(gtk.Window):
+    "A window for selecting how to color the atoms."
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.gui = gui
+        self.colormode = gui.colormode
+        self.actual_colordata = None
+        self.set_title(_("Colors"))
+        vbox = gtk.VBox()
+        self.add(vbox)
+        vbox.show()
+        # The main layout consists of two columns, the leftmost split in an upper and lower part.
+        self.maintable = gtk.Table(2,2)
+        pack(vbox, self.maintable)
+        self.methodbox = gtk.VBox()
+        self.methodbox.show()
+        self.maintable.attach(self.methodbox, 0, 1, 0, 1)
+        self.scalebox = gtk.VBox()
+        self.scalebox.show()
+        self.maintable.attach(self.scalebox, 0, 1, 1, 2)
+        self.colorbox = gtk.Frame()
+        self.colorbox.show()
+        self.maintable.attach(self.colorbox, 1, 2, 0, 2, gtk.EXPAND)
+        # Upper left: Choose how the atoms are colored.
+        lbl = gtk.Label(_("Choose how the atoms are colored:"))
+        pack(self.methodbox, [lbl])
+        self.radio_jmol = gtk.RadioButton(None, _('By atomic number, default "jmol" colors'))
+        self.radio_atno = gtk.RadioButton(self.radio_jmol,
+                                          _('By atomic number, user specified'))
+        self.radio_tag = gtk.RadioButton(self.radio_jmol, _('By tag'))
+        self.radio_force = gtk.RadioButton(self.radio_jmol, _('By force'))
+        self.radio_velocity = gtk.RadioButton(self.radio_jmol, _('By velocity'))
+        self.radio_manual = gtk.RadioButton(self.radio_jmol, _('Manually specified'))
+        self.radio_same = gtk.RadioButton(self.radio_jmol, _('All the same color'))
+        self.force_box = gtk.VBox()
+        self.velocity_box = gtk.VBox()
+        for widget in (self.radio_jmol, self.radio_atno, self.radio_tag,
+                      self.radio_force, self.force_box, self.radio_velocity,
+                      self.velocity_box, self.radio_manual, self.radio_same):
+            pack(self.methodbox, [widget])
+            if isinstance(widget, gtk.RadioButton):
+                widget.connect('toggled', self.method_radio_changed)
+        # Now fill in the box for additional information in case the force is used.
+        self.force_label = gtk.Label(_("This should not be displayed!"))
+        pack(self.force_box, [self.force_label])
+        self.force_min = gtk.Adjustment(0.0, 0.0, 100.0, 0.05)
+        self.force_max = gtk.Adjustment(0.0, 0.0, 100.0, 0.05)
+        self.force_steps = gtk.Adjustment(10, 2, 500, 1)
+        force_apply = gtk.Button(_('Update'))
+        force_apply.connect('clicked', self.set_force_colors)
+        pack(self.force_box, [gtk.Label(_('Min: ')),
+                              gtk.SpinButton(self.force_min, 10.0, 2),
+                              gtk.Label(_('  Max: ')),
+                              gtk.SpinButton(self.force_max, 10.0, 2),
+                              gtk.Label(_('  Steps: ')),
+                              gtk.SpinButton(self.force_steps, 1, 0),
+                              gtk.Label('  '),
+                              force_apply])
+        self.force_box.hide()
+        # Now fill in the box for additional information in case the velocity is used.
+        self.velocity_label = gtk.Label("This should not be displayed!")
+        pack(self.velocity_box, [self.velocity_label])
+        self.velocity_min = gtk.Adjustment(0.0, 0.0, 10.0, 0.005)
+        self.velocity_max = gtk.Adjustment(0.0, 0.0, 10.0, 0.005)
+        self.velocity_steps = gtk.Adjustment(10, 2, 500, 1)
+        velocity_apply = gtk.Button(_('Update'))
+        velocity_apply.connect('clicked', self.set_velocity_colors)
+        pack(self.velocity_box, [gtk.Label(_('Min: ')),
+                                 gtk.SpinButton(self.velocity_min, 10.0, 3),
+                                 gtk.Label(_('  Max: ')),
+                                 gtk.SpinButton(self.velocity_max, 10.0, 3),
+                                 gtk.Label(_('  Steps: ')),
+                                 gtk.SpinButton(self.velocity_steps, 1, 0),
+                                 gtk.Label('  '),
+                                 velocity_apply])
+        self.velocity_box.hide()
+        # Lower left: Create a color scale
+        pack(self.scalebox, gtk.Label(""))
+        lbl = gtk.Label(_('Create a color scale:'))
+        pack(self.scalebox, [lbl])
+        color_scales = (
+            _('Black - white'),
+            _('Black - red - yellow - white'),
+            _('Black - green - white'),
+            _('Black - blue - cyan'),
+            _('Hue'),
+            _('Named colors')
+            )
+        self.scaletype_created = None
+        self.scaletype = gtk.combo_box_new_text()
+        for s in color_scales:
+            self.scaletype.append_text(s)
+        self.createscale = gtk.Button(_("Create"))
+        pack(self.scalebox, [self.scaletype, self.createscale])
+        self.createscale.connect('clicked', self.create_color_scale)
+        # The actually colors are specified in a box possibly with scrollbars
+        self.colorwin = gtk.ScrolledWindow()
+        self.colorwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        self.colorwin.show()
+        self.colorbox.add(self.colorwin)
+        self.colorwin.add_with_viewport(gtk.VBox()) # Dummy contents
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [buts], end=True, bottom=True)
+        # Make the initial setup of the colors
+        self.color_errors = {}
+        self.init_colors_from_gui()
+        self.show()
+        gui.register_vulnerable(self)
+
+    def notify_atoms_changed(self):
+        "Called by gui object when the atoms have changed."
+        self.destroy()
+  
+    def init_colors_from_gui(self):
+        cm = self.gui.colormode
+        # Disallow methods if corresponding data is not available
+        if not self.gui.images.T.any():
+            self.radio_tag.set_sensitive(False)
+            if self.radio_tag.get_active() or cm == 'tag':
+                self.radio_jmol.set_active(True)
+                return
+        else:
+            self.radio_tag.set_sensitive(True)
+        if np.isnan(self.gui.images.F).any() or not self.gui.images.F.any():
+            self.radio_force.set_sensitive(False)
+            if self.radio_force.get_active() or cm == 'force':
+                self.radio_jmol.set_active(True)
+                return
+        else:
+            self.radio_force.set_sensitive(True)
+        if np.isnan(self.gui.images.V).any() or not self.gui.images.V.any():
+            self.radio_velocity.set_sensitive(False)
+            if self.radio_velocity.get_active() or cm == 'velocity':
+                self.radio_jmol.set_active(True)
+                return
+        else:
+            self.radio_velocity.set_sensitive(True)
+        self.radio_manual.set_sensitive(self.gui.images.natoms <= 1000)
+        # Now check what the current color mode is
+        if cm == 'jmol':
+            self.radio_jmol.set_active(True)
+            self.set_jmol_colors()
+        elif cm == 'Z':
+            self.radio_atno.set_active(True)
+        elif cm == 'tag':
+            self.radio_tag.set_active(True)
+        elif cm == 'force':
+            self.radio_force.set_active(True)
+        elif cm == 'velocity':
+            self.radio_velocity.set_active(True)
+        elif cm == 'manual':
+            self.radio_manual.set_active(True)
+        elif cm == 'same':
+            self.radio_same.set_active(True)
+            
+    def method_radio_changed(self, widget=None):
+        "Called when a radio button is changed."
+        self.scaletype_created = None
+        self.scaletype.set_active(-1)
+        if not widget.get_active():
+            # Ignore most events when a button is turned off.
+            if widget is self.radio_force:
+                self.force_box.hide()
+            if widget is self.radio_velocity:
+                self.velocity_box.hide()
+            return  
+        if widget is self.radio_jmol:
+            self.set_jmol_colors()
+        elif widget is self.radio_atno:
+            self.set_atno_colors()
+        elif widget is self.radio_tag:
+            self.set_tag_colors()
+        elif widget is self.radio_force:
+            self.show_force_stuff()
+            self.set_force_colors()
+        elif widget is self.radio_velocity:
+            self.show_velocity_stuff()
+            self.set_velocity_colors()
+        elif widget is self.radio_manual:
+            self.set_manual_colors()
+        elif widget is self.radio_same:
+            self.set_same_color()
+        else:
+            raise RuntimeError('Unknown widget in method_radio_changed')
+            
+    def make_jmol_colors(self):
+        "Set the colors to the default jmol colors"
+        self.colordata_z = []
+        hasfound = {}
+        for z in self.gui.images.Z:
+            if z not in hasfound:
+                hasfound[z] = True
+                self.colordata_z.append([z, jmol_colors[z]])
+
+    def set_jmol_colors(self):
+        "We use the immutable jmol colors."
+        self.make_jmol_colors()
+        self.set_atno_colors()
+        for entry in self.color_entries:
+            entry.set_sensitive(False)
+        self.colormode = 'jmol'
+        
+    def set_atno_colors(self):
+        "We use user-specified per-element colors."
+        if not hasattr(self, 'colordata_z'):
+            # No initial colors.  Use jmol colors
+            self.make_jmol_colors()
+        self.actual_colordata = self.colordata_z
+        self.color_labels = ["%i (%s):" % (z, ase.data.chemical_symbols[z])
+                             for z, col in self.colordata_z]
+        self.make_colorwin()
+        self.colormode = "atno"
+
+    def set_tag_colors(self):
+        "We use per-tag colors."
+        # Find which tags are in use
+        tags = self.gui.images.T
+        existingtags = []
+        for t in range(tags.min(), tags.max()+1):
+            if t in tags:
+                existingtags.append(t)
+        if not hasattr(self, 'colordata_tags') or len(self.colordata_tags) != len(existingtags):
+            colors = self.get_named_colors(len(existingtags))
+            self.colordata_tags = [[x, y] for x, y in
+                                   zip(existingtags, colors)]
+        self.actual_colordata = self.colordata_tags
+        self.color_labels = [str(x)+':' for x, y in self.colordata_tags]
+        self.make_colorwin()
+        self.colormode = 'tags'
+
+    def set_same_color(self):
+        "All atoms have the same color"
+        if not hasattr(self, 'colordata_same'):
+            try:
+                self.colordata_same = self.actual_colordata[0:1]
+            except AttributeError:
+                self.colordata_same = self.get_named_colors(1)
+        self.actual_colordata = self.colordata_same
+        self.actual_colordata[0][0] = 0
+        self.color_labels = ['all:']
+        self.make_colorwin()
+        self.colormode = 'same'
+
+    def set_force_colors(self, *args):
+        "Use the forces as basis for the colors."
+        borders = np.linspace(self.force_min.value,
+                              self.force_max.value,
+                              self.force_steps.value,
+                              endpoint=False)
+        if self.scaletype_created is None:
+            colors = self.new_color_scale([[0, [1,1,1]],
+                                           [1, [0,0,1]]], len(borders))
+        elif (not hasattr(self, 'colordata_force') or
+            len(self.colordata_force) != len(borders)):
+            colors = self.get_color_scale(len(borders), self.scaletype_created)
+        else:
+            colors = [y for x, y in self.colordata_force]
+        self.colordata_force = [[x, y] for x, y in zip(borders, colors)]
+        self.actual_colordata = self.colordata_force
+        self.color_labels = ["%.2f:" % x for x, y in self.colordata_force]
+        self.make_colorwin()
+        self.colormode = 'force'
+        fmin = self.force_min.value
+        fmax = self.force_max.value
+        factor = self.force_steps.value / (fmax -fmin)
+        self.colormode_force_data = (fmin, factor)
+
+    def set_velocity_colors(self, *args):
+        "Use the velocities as basis for the colors."
+        borders = np.linspace(self.velocity_min.value,
+                              self.velocity_max.value,
+                              self.velocity_steps.value,
+                              endpoint=False)
+        if self.scaletype_created is None:
+            colors = self.new_color_scale([[0, [1,1,1]],
+                                           [1, [1,0,0]]], len(borders))
+        elif (not hasattr(self, 'colordata_velocity') or
+            len(self.colordata_velocity) != len(borders)):
+            colors = self.get_color_scale(len(borders), self.scaletype_created)
+        else:
+            colors = [y for x, y in self.colordata_velocity]
+        self.colordata_velocity = [[x, y] for x, y in zip(borders, colors)]
+        self.actual_colordata = self.colordata_velocity
+        self.color_labels = ["%.2f:" % x for x, y in self.colordata_velocity]
+        self.make_colorwin()
+        self.colormode = 'velocity'
+        vmin = self.velocity_min.value
+        vmax = self.velocity_max.value
+        factor = self.velocity_steps.value / (vmax -vmin)
+        self.colormode_velocity_data = (vmin, factor)
+
+    def set_manual_colors(self):
+        "Set colors of all atoms from the last selection."
+        # We cannot directly make np.arrays of the colors, as they may
+        # be sequences of the same length, causing creation of a 2D
+        # array of characters/numbers instead of a 1D array of
+        # objects.
+        colors = np.array([None] * self.gui.images.natoms)
+        if self.colormode in ['atno', 'jmol', 'tags']:
+            maxval = max([x for x, y in self.actual_colordata])
+            oldcolors = np.array([None] * (maxval+1))
+            for x, y in self.actual_colordata:
+                oldcolors[x] = y
+            if self.colormode == 'tags':
+                colors[:] = oldcolors[self.gui.images.T[self.gui.frame]]
+            else:
+                colors[:] = oldcolors[self.gui.images.Z]
+        elif self.colormode == 'force':
+            oldcolors = np.array([None] * len(self.actual_colordata))
+            oldcolors[:] = [y for x, y in self.actual_colordata]
+            F = self.gui.images.F[self.gui.frame]
+            F = np.sqrt((F * F).sum(axis=-1))
+            nF = (F - self.colormode_force_data[0]) * self.colormode_force_data[1]
+            nF = np.clip(nF.astype(int), 0, len(oldcolors)-1)
+            colors[:] = oldcolors[nF]
+        elif self.colormode == 'velocity':
+            oldcolors = np.array([None] * len(self.actual_colordata))
+            oldcolors[:] = [y for x, y in self.actual_colordata]
+            V = self.gui.images.V[self.gui.frame]
+            V = np.sqrt((V * V).sum(axis=-1))
+            nV = (V - self.colormode_velocity_data[0]) * self.colormode_velocity_data[1]
+            nV = np.clip(nV.astype(int), 0, len(oldcolors)-1)
+            colors[:] = oldcolors[nV]
+        elif self.colormode == 'same':
+            oldcolor = self.actual_colordata[0][1]
+            if len(colors) == len(oldcolor):
+                # Direct assignment would be e.g. one letter per atom. :-(
+                colors[:] = [oldcolor] * len(colors)
+            else:
+                colors[:] = oldcolor
+        elif self.colormode == 'manual':
+            if self.actual_colordata is None:   # import colors from gui, if they don't exist already
+                colors = [y for x,y in self.gui.colordata]
+
+        self.color_labels = ["%d:" % i for i in range(len(colors))]
+        self.actual_colordata = [[i, x] for i, x in enumerate(colors)]
+        self.make_colorwin()
+        self.colormode = 'manual'
+
+    def show_force_stuff(self):
+        "Show and update widgets needed for selecting the force scale."
+        self.force_box.show()
+        F = np.sqrt(((self.gui.images.F*self.gui.images.dynamic[:,np.newaxis])**2).sum(axis=-1))
+        fmax = F.max()
+        nimages = self.gui.images.nimages
+        assert len(F) == nimages
+        if nimages > 1:
+            fmax_frame = self.gui.images.F[self.gui.frame].max()
+            txt = _("Max force: %.2f (this frame), %.2f (all frames)") % (fmax_frame, fmax)
+        else:
+            txt = _("Max force: %.2f.") % (fmax,)
+        self.force_label.set_text(txt)
+        if self.force_max.value == 0.0:
+            self.force_max.value = fmax
+
+    def show_velocity_stuff(self):
+        "Show and update widgets needed for selecting the velocity scale."
+        self.velocity_box.show()
+        V = np.sqrt((self.gui.images.V * self.gui.images.V).sum(axis=-1))
+        vmax = V.max()
+        nimages = self.gui.images.nimages
+        assert len(V) == nimages
+        if nimages > 1:
+            vmax_frame = self.gui.images.V[self.gui.frame].max()
+            txt = _("Max velocity: %.2f (this frame), %.2f (all frames)") % (vmax_frame, vmax)
+        else:
+            txt = _("Max velocity: %.2f.") % (vmax,)
+        self.velocity_label.set_text(txt)
+        if self.velocity_max.value == 0.0:
+            self.velocity_max.value = vmax
+        
+    def make_colorwin(self):
+        """Make the list of editable color entries.
+
+        Uses self.actual_colordata and self.color_labels.  Produces self.color_entries.
+        """
+        assert len(self.actual_colordata) == len(self.color_labels)
+        self.color_entries = []
+        old = self.colorwin.get_child()
+        self.colorwin.remove(old)
+        del old
+        table = gtk.Table(len(self.actual_colordata)+1, 4)
+        self.colorwin.add_with_viewport(table)
+        table.show()
+        self.color_display = []
+        for i in range(len(self.actual_colordata)):
+            lbl = gtk.Label(self.color_labels[i])
+            entry = gtk.Entry(max=20)
+            val = self.actual_colordata[i][1]
+            error = False
+            if not isinstance(val, str):
+                assert len(val) == 3
+                intval = tuple(np.round(65535*np.array(val)).astype(int))
+                val = "%.3f, %.3f, %.3f" % tuple(val)
+                clr = gtk.gdk.Color(*intval)
+            else:
+                try:
+                    clr = gtk.gdk.color_parse(val)
+                except ValueError:
+                    error = True
+            entry.set_text(val)
+            blob = gtk.EventBox()
+            space = gtk.Label
+            space = gtk.Label("    ")
+            space.show()
+            blob.add(space)
+            if error:
+                space.set_text(_("ERROR"))
+            else:
+                blob.modify_bg(gtk.STATE_NORMAL, clr)
+            table.attach(lbl, 0, 1, i, i+1, yoptions=0)
+            table.attach(entry, 1, 2, i, i+1, yoptions=0)
+            table.attach(blob, 2, 3, i, i+1, yoptions=0)
+            lbl.show()
+            entry.show()
+            blob.show()
+            entry.connect('activate', self.entry_changed, i)
+            self.color_display.append(blob)
+            self.color_entries.append(entry)
+            
+    def entry_changed(self, widget, index):
+        """The user has changed a color."""
+        txt = widget.get_text()
+        txtfields = txt.split(',')
+        if len(txtfields) == 3:
+            self.actual_colordata[index][1] = [float(x) for x in txtfields]
+            val = tuple([int(65535*float(x)) for x in txtfields])
+            clr = gtk.gdk.Color(*val)
+        else:
+            self.actual_colordata[index][1] = txt
+            try:
+                clr = gtk.gdk.color_parse(txt)
+            except ValueError:
+                # Cannot parse the color
+                displ = self.color_display[index]
+                displ.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))
+                displ.get_child().set_text(_("ERR"))
+                self.color_errors[index] = (self.color_labels[index], txt)
+                return
+        self.color_display[index].get_child().set_text("    ") # Clear error message
+        self.color_errors.pop(index, None)
+        self.color_display[index].modify_bg(gtk.STATE_NORMAL, clr)
+        
+    def create_color_scale(self, *args):
+        if self.radio_jmol.get_active():
+            self.radio_atno.set_active(1)
+        n = len(self.color_entries)
+        s = self.scaletype.get_active()
+        scale = self.get_color_scale(n, s)
+        self.scaletype_created = s
+        for i in range(n):
+            if isinstance(scale[i], str):
+                self.color_entries[i].set_text(scale[i])
+            else:
+                s = "%.3f, %.3f, %.3f" % tuple(scale[i])
+                self.color_entries[i].set_text(s)
+            self.color_entries[i].activate()
+
+    def get_color_scale(self, n, s):
+        if s == 0:
+            # Black - White
+            scale = self.new_color_scale([[0, [0,0,0]],
+                                          [1, [1,1,1]]], n)
+        elif s == 1:
+            # Black - Red - Yellow - White (STM colors)
+            scale = self.new_color_scale([[0, [0,0,0]],
+                                          [0.33, [1,0,0]],
+                                          [0.67, [1,1,0]],
+                                          [1, [1,1,1]]], n)
+        elif s == 2:
+            # Black - Green - White
+            scale = self.new_color_scale([[0, [0,0,0]],
+                                          [0.5, [0,0.9,0]],
+                                          [0.75, [0.2,1.0,0.2]],
+                                          [1, [1,1,1]]], n)
+        elif s == 3:
+            # Black - Blue - Cyan
+            scale = self.new_color_scale([[0, [0,0,0]],
+                                          [0.5, [0,0,1]],
+                                          [1, [0,1,1]]], n)
+        elif s == 4:
+            # Hues
+            hues = np.linspace(0.0, 1.0, n, endpoint=False)
+            scale = ["%.3f, %.3f, %.3f" % colorsys.hls_to_rgb(h, 0.5, 1)
+                     for h in hues]
+        elif s == 5:
+            # Named colors
+            scale = self.get_named_colors(n)
+        else:
+            scale = None
+        return scale
+
+    def new_color_scale(self, fixpoints, n):
+        "Create a homogeneous color scale."
+        x = np.array([a[0] for a in fixpoints], float)
+        y = np.array([a[1] for a in fixpoints], float)
+        assert y.shape[1] == 3
+        res = []
+        for a in np.linspace(0.0, 1.0, n, endpoint=True):
+            n = x.searchsorted(a)
+            if n == 0:
+                v = y[0]  # Before the start
+            elif n == len(x):
+                v = x[-1] # After the end
+            else:
+                x0 = x[n-1]
+                x1 = x[n]
+                y0 = y[n-1]
+                y1 = y[n]
+                v = y0 + (y1 - y0) / (x1 - x0) * (a - x0)
+            res.append(v)
+        return res
+
+    def get_named_colors(self, n):
+        if n <= len(named_colors):
+            return named_colors[:n]
+        else:
+            return named_colors + ('Black',) * (n - len(named_colors))
+        
+    def apply(self, *args):
+        #if self.colormode in ['atno', 'jmol', 'tags']:
+        # Color atoms according to an integer value number
+        if self.color_errors:
+            oops(_("Incorrect color specification"),
+                 "%s: %s" % self.color_errors.values()[0])
+            return False
+        colordata = self.actual_colordata
+        if self.colormode == 'force':
+            # Use integers instead for border values
+            colordata = [[i, x[1]] for i, x in enumerate(self.actual_colordata)]
+            self.gui.colormode_force_data = self.colormode_force_data
+        if self.colormode == 'velocity':
+            # Use integers instead for border values
+            colordata = [[i, x[1]] for i, x in enumerate(self.actual_colordata)]
+            self.gui.colormode_velocity_data = self.colormode_velocity_data
+        maxval = max([x for x, y in colordata])
+        self.gui.colors = [None] * (maxval + 1)
+        new = self.gui.drawing_area.window.new_gc
+        alloc = self.gui.colormap.alloc_color
+        for z, val in colordata:
+            if isinstance(val, str):
+                self.gui.colors[z] = new(alloc(val))
+            else:
+                clr = tuple([int(65535*x) for x in val])
+                assert len(clr) == 3
+                self.gui.colors[z] = new(alloc(*clr))
+        self.gui.colormode = self.colormode
+        self.gui.colordata = self.actual_colordata
+        self.gui.draw()
+        return True
+
+    def cancel(self, *args):
+        self.destroy()
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+        
diff --git a/ase/gui/constraints.py b/ase/gui/constraints.py
new file mode 100644
index 0000000..e0ba151
--- /dev/null
+++ b/ase/gui/constraints.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+from math import sqrt
+
+import gtk
+from gettext import gettext as _
+
+from ase.gui.widgets import pack, Help
+
+
+class Constraints(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_title(_('Constraints'))
+        vbox = gtk.VBox()
+        b = pack(vbox, [gtk.Button(_('Constrain')),
+                        gtk.Label(_(' selected atoms'))])[0]
+        b.connect('clicked', self.selected)
+        b = pack(vbox, [gtk.Button(_('Constrain')),
+                        gtk.Label(_(' immobile atoms:'))])[0]
+        b.connect('clicked', self.immobile)
+        b = pack(vbox, [gtk.Button(_('Unconstrain')),
+                        gtk.Label(_(' selected atoms:'))])[0]
+        b.connect('clicked', self.unconstrain)
+        b = pack(vbox, gtk.Button(_('Clear constraints')))
+        b.connect('clicked', self.clear)
+        close = pack(vbox, gtk.Button(_('Close')))
+        close.connect('clicked', lambda widget: self.destroy())
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def selected(self, button):
+        self.gui.images.dynamic[self.gui.images.selected] = False
+        self.gui.draw()
+
+    def unconstrain(self, button):
+        self.gui.images.dynamic[self.gui.images.selected] = True
+        self.gui.draw()
+        
+    def immobile(self, button):
+        self.gui.images.set_dynamic()
+        self.gui.draw()
+
+    def clear(self, button):
+        self.gui.images.dynamic[:] = True
+        self.gui.draw()
+
diff --git a/ase/gui/crystal.py b/ase/gui/crystal.py
new file mode 100644
index 0000000..002fc8b
--- /dev/null
+++ b/ase/gui/crystal.py
@@ -0,0 +1,476 @@
+# encoding: utf-8
+"""crystal.py - Window for setting up arbitrary crystal lattices
+"""
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops
+from ase.gui.pybutton import PyButton
+from ase.gui.setupwindow import SetupWindow
+from ase.gui.status import formula
+from ase.lattice.spacegroup import crystal, Spacegroup
+
+import ase
+import numpy as np
+
+introtext = _("""\
+  Use this dialog to create crystal lattices. First select the structure,
+  either from a set of common crystal structures, or by space group description.
+  Then add all other lattice parameters.
+
+  If an experimental crystal structure is available for an atom, you can
+  look up the crystal type and lattice constant, otherwise you have to specify it
+  yourself.  """)
+
+py_template = """
+from ase.lattice.spacegroup.crystal import crystal
+
+atoms = crystal(spacegroup=%(spacegroup)d,
+                symbols=%(symbols)s,
+                basis=%(basis)s,
+                cellpar=%(cellpar)s)
+"""
+label_template = _(""" %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>""")
+
+# all predefined crystals go into tuples here:
+# (selection name, spacegroup, group_active, [repeats], [a,b,c,alpha,beta,gamma],[lattice constraints],[constraints_active],basis)
+crystal_definitions = [('Spacegroup',   1,  True, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,0,0,0,0,0], [ True, True, True, True, True, True], [['','','','']]),
+                       ('fcc',        225, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,1,1,3,3,3], [False,False,False,False,False,False], [['','','','']]),
+                       ('bcc',        229, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,1,1,3,3,3], [False,False,False,False,False,False], [['','','','']]),
+                       ('diamond',    227, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,1,1,3,3,3], [False,False,False,False,False,False], [['','','','']]),
+                       ('hcp',        194, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0, 120.0],
+                        [0,1,0,3,3,3], [False,False,False,False,False,False], [['','1./3.','2./3.','3./4.']]),
+                       ('graphite',   186, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0, 120.0],
+                        [0,1,0,3,3,3], [False,False,False,False,False,False], [['','0','0','0'],['','1./3.','2./3.','0']]),
+                       ('rocksalt',   225, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,1,1,3,3,3], [False,False,False,False,False,False], [['','0','0','0'],['','0.5','0.5','0.5']]),
+                       ('rutile',     136, False, [1,1,1], [3.0, 3.0, 3.0, 90.0, 90.0,  90.0],
+                        [0,1,0,3,3,3], [False,False,False,False,False,False], [['','0','0','0'],['O','0.3','0.3','0'  ]])]
+
+class SetupBulkCrystal(SetupWindow):
+    """Window for setting up a surface."""
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Create Bulk Crystal by Spacegroup"))
+        self.atoms = None
+        vbox = gtk.VBox()
+        self.packtext(vbox, introtext)
+        self.structinfo = gtk.combo_box_new_text()
+        self.structures = {}
+        for c in crystal_definitions:
+            self.structinfo.append_text(c[0])
+            self.structures[c[0]] = c
+        self.structinfo.set_active(0)
+        self.structinfo.connect("changed",self.set_lattice_type)
+        self.spacegroup = gtk.Entry(max=14)
+        self.spacegroup.set_text('P 1')
+        self.elementinfo = gtk.Label("")
+        self.spacegroupinfo = gtk.Label(_('Number: 1'))
+        pack(vbox,[gtk.Label(_("Lattice: ")),self.structinfo,gtk.Label(_("\tSpace group: ")),self.spacegroup,gtk.Label('  '),self.spacegroupinfo,gtk.Label('  '),self.elementinfo])
+        pack(vbox,[gtk.Label("")])
+        self.size = [gtk.Adjustment(1, 1, 100, 1) for i in range(3)]
+        buttons = [gtk.SpinButton(s, 0, 0) for s in self.size]
+        pack(vbox, [gtk.Label(_("Size: x: ")), buttons[0],
+                    gtk.Label(_("  y: ")), buttons[1],
+                    gtk.Label(_("  z: ")), buttons[2],
+                    gtk.Label(_(" unit cells"))])
+        pack(vbox,[gtk.Label("")])
+        self.lattice_lengths = [gtk.Adjustment(3.0, 0.0, 1000.0, 0.01) for i in range(3)]
+        self.lattice_angles  = [gtk.Adjustment(90.0,0.0, 180.0, 1) for i in range(3)]
+        self.lattice_lbuts = [gtk.SpinButton(self.lattice_lengths[i], 0, 0) for i in range(3)]
+        self.lattice_abuts = [gtk.SpinButton(self.lattice_angles[i] , 0, 0) for i in range(3)]
+        for i in self.lattice_lbuts:
+            i.set_digits(5)
+        for i in self.lattice_abuts:
+            i.set_digits(3)
+        self.lattice_lequals = [gtk.combo_box_new_text() for i in range(3)]
+        self.lattice_aequals = [gtk.combo_box_new_text() for i in range(3)]
+        self.lattice_lequals[0].append_text(_('free'))
+        self.lattice_lequals[0].append_text(_('equals b'))
+        self.lattice_lequals[0].append_text(_('equals c'))
+        self.lattice_lequals[0].append_text(_('fixed'))
+        self.lattice_lequals[1].append_text(_('free'))
+        self.lattice_lequals[1].append_text(_('equals a'))
+        self.lattice_lequals[1].append_text(_('equals c'))
+        self.lattice_lequals[1].append_text(_('fixed'))
+        self.lattice_lequals[2].append_text(_('free'))
+        self.lattice_lequals[2].append_text(_('equals a'))
+        self.lattice_lequals[2].append_text(_('equals b'))
+        self.lattice_lequals[2].append_text(_('fixed'))
+        self.lattice_aequals[0].append_text(_('free'))
+        self.lattice_aequals[0].append_text(_('equals beta'))
+        self.lattice_aequals[0].append_text(_('equals gamma'))
+        self.lattice_aequals[0].append_text(_('fixed'))
+        self.lattice_aequals[1].append_text(_('free'))
+        self.lattice_aequals[1].append_text(_('equals alpha'))
+        self.lattice_aequals[1].append_text(_('equals gamma'))
+        self.lattice_aequals[1].append_text(_('fixed'))
+        self.lattice_aequals[2].append_text(_('free'))
+        self.lattice_aequals[2].append_text(_('equals alpha'))
+        self.lattice_aequals[2].append_text(_('equals beta'))
+        self.lattice_aequals[2].append_text(_('fixed'))
+        for i in range(3):
+            self.lattice_lequals[i].set_active(0)
+            self.lattice_aequals[i].set_active(0)
+        pack(vbox,[gtk.Label(_('Lattice parameters'))])
+        pack(vbox,[gtk.Label(_('\t\ta:\t'))  , self.lattice_lbuts[0],gtk.Label('  '),self.lattice_lequals[0],
+                   gtk.Label(_('\talpha:\t')), self.lattice_abuts[0],gtk.Label('  '),self.lattice_aequals[0]])
+        pack(vbox,[gtk.Label(_('\t\tb:\t'))  , self.lattice_lbuts[1],gtk.Label('  '),self.lattice_lequals[1],
+                   gtk.Label(_('\tbeta:\t')) , self.lattice_abuts[1],gtk.Label('  '),self.lattice_aequals[1]])
+        pack(vbox,[gtk.Label(_('\t\tc:\t'))  , self.lattice_lbuts[2],gtk.Label('  '),self.lattice_lequals[2],
+                   gtk.Label(_('\tgamma:\t')), self.lattice_abuts[2],gtk.Label('  '),self.lattice_aequals[2]])
+        self.get_data = gtk.Button(_("Get from database"))
+        self.get_data.connect("clicked", self.get_from_database)
+        self.get_data.set_sensitive(False)
+        pack(vbox,[gtk.Label("     "),self.get_data])
+        pack(vbox,[gtk.Label("")])
+        pack(vbox,[gtk.Label(_("Basis: "))])
+        self.elements = [[gtk.Entry(max=3),gtk.Entry(max=8),gtk.Entry(max=8),gtk.Entry(max=8),True]]
+        self.element = self.elements[0][0]
+        add_atom = gtk.Button(stock = gtk.STOCK_ADD)
+        add_atom.connect("clicked",self.add_basis_atom)
+        add_atom.connect("activate",self.add_basis_atom)
+        pack(vbox,[gtk.Label(_('  Element:\t')),self.elements[0][0],gtk.Label(_('\tx: ')),
+                   self.elements[0][1],gtk.Label(_('  y: ')),self.elements[0][2],
+                   gtk.Label(_('  z: ')),self.elements[0][3],gtk.Label('\t'),add_atom])
+        self.vbox_basis = gtk.VBox()
+        swin = gtk.ScrolledWindow()
+        swin.set_border_width(0)
+        swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        vbox.pack_start(swin, True, True, 0)
+        swin.add_with_viewport(self.vbox_basis)
+        self.vbox_basis.get_parent().set_shadow_type(gtk.SHADOW_NONE)
+        self.vbox_basis.get_parent().set_size_request(-1, 100)
+        swin.show()
+
+        pack(self.vbox_basis,[gtk.Label('')])
+        pack(vbox,[self.vbox_basis])
+        self.vbox_basis.show()
+        pack(vbox,[gtk.Label("")])
+        self.status = gtk.Label("")
+        pack(vbox,[self.status])
+        pack(vbox,[gtk.Label("")])
+        self.pybut = PyButton(_("Creating a crystal."))
+        self.pybut.connect('clicked', self.update)
+
+        clear = gtk.Button(stock = gtk.STOCK_CLEAR)
+        clear.connect("clicked", self.clear)
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [self.pybut, clear, buts], end=True, bottom=True)
+        self.structinfo.connect("changed", self.update)
+        self.spacegroup.connect("activate", self.update)
+        for s in self.size:
+            s.connect("value-changed",self.update)
+        for el in self.elements:
+            if el[-1]:
+                for i in el[:-1]:
+                    i.connect("activate", self.update)
+                    i.connect("changed", self.update)
+        for i in range(3):
+            self.lattice_lbuts[i].connect("value-changed", self.update)
+            self.lattice_abuts[i].connect("value-changed", self.update)
+            self.lattice_lequals[i].connect("changed", self.update)
+            self.lattice_aequals[i].connect("changed", self.update)
+        self.clearing_in_process = False
+        self.gui = gui
+        self.add(vbox)
+        vbox.show()
+        self.show()
+
+    def update(self, *args):
+        """ all changes of physical constants are handled here, atoms are set up"""
+        if self.clearing_in_process:
+            return True
+        self.update_element()
+        a_equals = self.lattice_lequals[0].get_active()
+        b_equals = self.lattice_lequals[1].get_active()
+        c_equals = self.lattice_lequals[2].get_active()
+        alpha_equals = self.lattice_aequals[0].get_active()
+        beta_equals  = self.lattice_aequals[1].get_active()
+        gamma_equals = self.lattice_aequals[2].get_active()
+        sym = self.spacegroup.get_text()
+        valid = True
+        try:
+            no = int(sym)
+            spg = Spacegroup(no).symbol
+            self.spacegroupinfo.set_label(_('Symbol: %s') % str(spg))
+            spg = no
+        except:
+            try:
+                no = Spacegroup(sym).no
+                self.spacegroupinfo.set_label(_('Number: %s') % str(no))
+                spg = no
+            except:
+                self.spacegroupinfo.set_label(_('Invalid Spacegroup!'))
+                valid = False
+                
+        if a_equals == 0:
+            self.lattice_lbuts[0].set_sensitive(True)
+        elif a_equals == 1:
+            self.lattice_lbuts[0].set_sensitive(False)
+            self.lattice_lbuts[0].set_value(self.lattice_lbuts[1].get_value())
+        elif a_equals == 2:
+            self.lattice_lbuts[0].set_sensitive(False)
+            self.lattice_lbuts[0].set_value(self.lattice_lbuts[2].get_value())
+        else:
+            self.lattice_lbuts[0].set_sensitive(False)
+        if b_equals == 0:
+            self.lattice_lbuts[1].set_sensitive(True)
+        elif b_equals == 1:
+            self.lattice_lbuts[1].set_sensitive(False)
+            self.lattice_lbuts[1].set_value(self.lattice_lbuts[0].get_value())
+        elif b_equals == 2:
+            self.lattice_lbuts[1].set_sensitive(False)
+            self.lattice_lbuts[1].set_value(self.lattice_lbuts[2].get_value())
+        else:
+            self.lattice_lbuts[1].set_sensitive(False)
+        if c_equals == 0:
+            self.lattice_lbuts[2].set_sensitive(True)
+        elif c_equals == 1:
+            self.lattice_lbuts[2].set_sensitive(False)
+            self.lattice_lbuts[2].set_value(self.lattice_lbuts[0].get_value())
+        elif c_equals == 2:
+            self.lattice_lbuts[2].set_sensitive(False)
+            self.lattice_lbuts[2].set_value(self.lattice_lbuts[1].get_value())
+        else:
+            self.lattice_lbuts[2].set_sensitive(False)
+        if alpha_equals == 0:
+            self.lattice_abuts[0].set_sensitive(True)
+        elif alpha_equals == 1:
+            self.lattice_abuts[0].set_sensitive(False)
+            self.lattice_abuts[0].set_value(self.lattice_abuts[1].get_value())
+        elif alpha_equals == 2:
+            self.lattice_abuts[0].set_sensitive(False)
+            self.lattice_abuts[0].set_value(self.lattice_abuts[2].get_value())
+        else:
+            self.lattice_abuts[0].set_sensitive(False)
+        if beta_equals == 0:
+            self.lattice_abuts[1].set_sensitive(True)
+        elif beta_equals == 1:
+            self.lattice_abuts[1].set_sensitive(False)
+            self.lattice_abuts[1].set_value(self.lattice_abuts[0].get_value())
+        elif beta_equals == 2:
+            self.lattice_abuts[1].set_sensitive(False)
+            self.lattice_abuts[1].set_value(self.lattice_abuts[2].get_value())
+        else:
+            self.lattice_abuts[1].set_sensitive(False)
+        if gamma_equals == 0:
+            self.lattice_abuts[2].set_sensitive(True)
+        elif gamma_equals == 1:
+            self.lattice_abuts[2].set_sensitive(False)
+            self.lattice_abuts[2].set_value(self.lattice_abuts[0].get_value())
+        elif gamma_equals == 2:
+            self.lattice_abuts[2].set_sensitive(False)
+            self.lattice_abuts[2].set_value(self.lattice_abuts[1].get_value())
+        else:
+            self.lattice_abuts[2].set_sensitive(False)
+            
+        valid = len(self.elements[0][0].get_text()) and valid
+        self.get_data.set_sensitive(valid and self.get_n_elements() == 1 and self.update_element())
+        self.atoms = None
+        if valid:
+            basis_count = -1
+            for el in self.elements:
+                if el[-1]:
+                    basis_count += 1
+            if basis_count:
+                symbol_str = '['
+                basis_str = "["
+                symbol = []
+                basis = []
+            else:
+                symbol_str = ''
+                basis_str = ''
+                basis = None
+            for el in self.elements:
+                if el[-1]:
+                    symbol_str += "'"+el[0].get_text()+"'"
+                    if basis_count:
+                        symbol_str += ','
+                        symbol += [el[0].get_text()]
+                        exec 'basis += [[float('+el[1].get_text()+'),float('+el[2].get_text()+'),float('+el[3].get_text()+')]]'
+                    else:
+                        symbol = el[0].get_text()
+                        exec 'basis = [[float('+el[1].get_text()+'),float('+el[2].get_text()+'),float('+el[3].get_text()+')]]'
+                    basis_str += '['+el[1].get_text()+','+el[2].get_text()+','+el[3].get_text()+'],'
+            basis_str = basis_str[:-1]
+            if basis_count:
+                symbol_str = symbol_str[:-1]+']'
+                basis_str += ']'
+            size_str = '('+str(int(self.size[0].get_value()))+','+str(int(self.size[1].get_value()))+','+str(int(self.size[2].get_value()))+')'
+            size = (int(self.size[0].get_value()),int(self.size[1].get_value()),int(self.size[2].get_value()))
+            cellpar_str = ''
+            cellpar = []
+            for i in self.lattice_lbuts:
+                cellpar_str += str(i.get_value())+','
+                cellpar += [i.get_value()]
+            for i in self.lattice_abuts:
+                cellpar_str += str(i.get_value())+','
+                cellpar += [i.get_value()]
+            cellpar_str = '['+cellpar_str[:-1]+']'
+            args = {'symbols' : symbol,
+                    'basis'  : basis,
+                    'size'   : size,
+                    'spacegroup' : spg,
+                    'cellpar' : cellpar}
+            args_str = {'symbols' : symbol_str,
+                        'basis'   : basis_str,
+                        'size'    : size_str,
+                        'spacegroup' : spg,
+                        'cellpar' : cellpar_str} 
+            self.pybut.python = py_template % args_str
+            try: 
+                self.atoms = crystal(**args)
+                label = label_template % {'natoms'  : self.atoms.get_number_of_atoms(),
+                                          'symbols' : formula(self.atoms.get_atomic_numbers()),
+                                          'volume'  : self.atoms.get_volume()}
+                self.status.set_label(label)                
+            except:
+                self.atoms = None
+                self.status.set_markup(_("Please specify a consistent set of atoms."))
+        else:
+            self.atoms = None
+            self.status.set_markup(_("Please specify a consistent set of atoms."))
+
+    def apply(self, *args):
+        """ create gui atoms from currently active atoms"""
+        self.update()
+        if self.atoms is not None:
+            self.gui.new_atoms(self.atoms)
+            return True
+        else:
+            oops(_("No valid atoms.",
+                   "You have not (yet) specified a consistent set of "
+                   "parameters."))
+            return False
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+        
+    def add_basis_atom(self,*args):
+        """ add an atom to the customizable basis """
+        n = len(self.elements)
+        self.elements += [[gtk.Entry(max=3),gtk.Entry(max=8),gtk.Entry(max=8),gtk.Entry(max=8),
+                           gtk.Label('\t\t\t'),gtk.Label('\tx: '),gtk.Label('  y: '),
+                           gtk.Label('  z: '),gtk.Label(' '),
+                           gtk.Button(stock=gtk.STOCK_DELETE),True]]
+        self.elements[n][-2].connect("clicked",self.delete_basis_atom,{'n':n}) 
+        pack(self.vbox_basis,[self.elements[n][4],self.elements[n][0],self.elements[n][5],
+                              self.elements[n][1],self.elements[n][6],self.elements[n][2],
+                              self.elements[n][7],self.elements[n][3],self.elements[n][8],
+                              self.elements[n][9]])
+        self.update()
+
+    def delete_basis_atom(self, button, index, *args):
+        """ delete atom index from customizable basis"""
+        n = index['n']
+        self.elements[n][-1] = False
+        for i in range(10):
+            self.elements[n][i].destroy()
+        self.update()
+
+    def get_n_elements(self):
+        """ counts how many basis atoms are actually active """
+        n = 0
+        for el in self.elements:
+            if el[-1]:
+                n += 1
+        return n
+
+    def clear(self, *args):
+        """ reset to original state """ 
+        self.clearing_in_process = True
+        self.clear_lattice()
+        self.structinfo.set_active(0)
+        self.set_lattice_type()
+        self.clearing_in_process = False
+        self.update()
+        
+    def clear_lattice(self, *args):
+        """ delete all custom settings """
+        self.atoms = None
+        if len(self.elements) > 1:
+            for n, el in enumerate(self.elements[1:]):
+                self.elements[n+1][-1] = False
+                for i in range(10):
+                    self.elements[n+1][i].destroy()
+        for i in range(4):
+            self.elements[0][i].set_text("")
+        self.spacegroup.set_sensitive(True)
+        for i in self.lattice_lbuts:
+            i.set_sensitive(True)
+        for i in self.lattice_abuts:
+            i.set_sensitive(True)
+        for i in range(3):
+            self.lattice_lequals[i].set_sensitive(True)
+            self.lattice_aequals[i].set_sensitive(True)
+            self.lattice_lequals[i].set_active(0)
+            self.lattice_aequals[i].set_active(0)
+        for s in self.size:
+            s.set_value(1)
+
+    def set_lattice_type(self, *args):
+        """ set defaults from original """ 
+        self.clearing_in_process = True
+        self.clear_lattice()
+        lattice = crystal_definitions[self.structinfo.get_active()]
+        self.spacegroup.set_text(str(lattice[1]))
+        self.spacegroup.set_sensitive(lattice[2])
+        for s, i in zip(self.size,lattice[3]):
+            s.set_value(i)
+        self.lattice_lbuts[0].set_value(lattice[4][0])
+        self.lattice_lbuts[1].set_value(lattice[4][1])
+        self.lattice_lbuts[2].set_value(lattice[4][2])
+        self.lattice_abuts[0].set_value(lattice[4][3])
+        self.lattice_abuts[1].set_value(lattice[4][4])
+        self.lattice_abuts[2].set_value(lattice[4][5])
+        self.lattice_lequals[0].set_active(lattice[5][0])
+        self.lattice_lequals[1].set_active(lattice[5][1])
+        self.lattice_lequals[2].set_active(lattice[5][2])
+        self.lattice_aequals[0].set_active(lattice[5][3])
+        self.lattice_aequals[1].set_active(lattice[5][4])
+        self.lattice_aequals[2].set_active(lattice[5][5])
+        self.lattice_lequals[0].set_sensitive(lattice[6][0])
+        self.lattice_lequals[1].set_sensitive(lattice[6][1])
+        self.lattice_lequals[2].set_sensitive(lattice[6][2])
+        self.lattice_aequals[0].set_sensitive(lattice[6][3])
+        self.lattice_aequals[1].set_sensitive(lattice[6][4])
+        self.lattice_aequals[2].set_sensitive(lattice[6][5])
+        for n, at in enumerate(lattice[7]):
+            l = 0
+            if n > 0:
+                l = len(self.elements)
+                self.add_basis_atom()
+            for i, s in enumerate(at):
+                self.elements[l][i].set_text(s)
+        self.clearing_in_process = False
+        self.update()
+
+    def get_from_database(self, *args):
+        element = self.elements[0][0].get_text()
+        z = ase.atomic_numbers[self.legal_element]        
+        ref = ase.data.reference_states[z]
+        lattice = ref['symmetry']
+        index = 0
+        while index < len(crystal_definitions) and crystal_definitions[index][0] != lattice:
+            index += 1
+        if index == len(crystal_definitions) or not self.legal_element:
+            oops(_("Can't find lattice definition!"))
+            return False
+        self.structinfo.set_active(index)
+        self.lattice_lbuts[0].set_value(ref['a'])
+        if lattice == 'hcp':
+            self.lattice_lbuts[2].set_value(ref['c/a']*ref['a'])
+        self.elements[0][0].set_text(element)
+        if lattice in ['fcc', 'bcc', 'diamond']:
+            self.elements[0][1].set_text('0')
+            self.elements[0][2].set_text('0')
+            self.elements[0][3].set_text('0')
+
diff --git a/ase/gui/debug.py b/ase/gui/debug.py
new file mode 100644
index 0000000..9eb876c
--- /dev/null
+++ b/ase/gui/debug.py
@@ -0,0 +1,21 @@
+import sys
+
+import gtk
+from gettext import gettext as _
+import numpy as np
+
+class Debug(gtk.Window):
+    def __init__(self, gui):
+        self.gui = gui
+        gtk.Window.__init__(self)
+        self.set_title(_('Debug'))
+        entry = gtk.Entry(200)
+        self.add(entry)
+        entry.connect('activate', self.enter, entry)
+        entry.show()
+        self.show()
+
+    def enter(self, widget, entry):
+        g = self.gui
+        print >> sys.stderr, eval(entry.get_text())
+    
diff --git a/ase/gui/defaults.py b/ase/gui/defaults.py
new file mode 100644
index 0000000..bd0826b
--- /dev/null
+++ b/ase/gui/defaults.py
@@ -0,0 +1,19 @@
+""" This is a module to handle generic ASE (gui) defaults from a ~/.ase/gui.py configuration file, if it exists.
+It is imported when opening ag and can then be modified at runtime, if necessary.
+syntax for each entry:
+
+gui_default_settings['key'] = value
+"""
+
+gui_default_settings = {
+    'gui_graphs_string' : 'i, e - E[-1]',   # default for the graph command in the gui
+    'covalent_radii' : None
+    }
+
+def read_defaults():
+    import os.path
+    name = os.path.expanduser('~/.ase/gui.py')
+    config = gui_default_settings
+    if os.path.exists(name):
+        execfile(name)
+    return config
diff --git a/ase/gui/dft.py b/ase/gui/dft.py
new file mode 100644
index 0000000..2fe4eae
--- /dev/null
+++ b/ase/gui/dft.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+from math import sqrt
+
+import gtk
+from gettext import gettext as _
+
+from ase.gui.widgets import pack, Help
+
+
+class DFT(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_title(_('DFT'))
+        vbox = gtk.VBox()
+        combo = gtk.combo_box_new_text()
+        self.xcfuncs = 'None LDA PBE revPBE RPBE PW91 EXX PBE0'.split()
+        for xc in self.xcfuncs:
+            combo.append_text(xc)
+        pack(vbox, [gtk.Label(_('XC-functional: ')), combo])
+
+        button=radio(None,monkhorstpack)
+        button=radio(button, special)
+        pack(vbox, gtk.Label(_('Repeat atoms:')))
+        self.kpts = [gtk.Adjustment(r, 1, 99, 1) for r in gui.atoms.repeat]
+        pack(vbox, [gtk.SpinButton(r, 0, 0) for r in self.repeat])
+        for r in self.repeat:
+            r.connect('value-changed', self.change)
+
+        close = pack(vbox, gtk.Button(_('Close')))
+        close.connect('clicked', lambda widget: self.destroy())
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+        xc = gui.atoms.dft.get('xc', 'None')
+        combo.set_active(self.xcfuncs.index(xc))
+
+    def selected(self, button):
+        self.gui.atoms.dynamic = ~self.gui.atoms.selected
+        self.gui.draw()
+
+    def immobile(self, button):
+        self.gui.atoms.set_dynamic()
+        self.gui.draw()
+
+    def clear(self, button):
+        self.gui.atoms.dynamic[:] = True
+        self.gui.draw()
+
diff --git a/ase/gui/energyforces.py b/ase/gui/energyforces.py
new file mode 100644
index 0000000..b9d3283
--- /dev/null
+++ b/ase/gui/energyforces.py
@@ -0,0 +1,96 @@
+# encoding: utf-8
+
+"Module for calculating energies and forces."
+
+import gtk
+from gettext import gettext as _
+from ase.gui.simulation import Simulation
+from ase.gui.widgets import oops, pack
+
+class OutputFieldMixin:
+    def makeoutputfield(self, box, label=_("Output:"), heading = None):
+        frame = gtk.Frame(label)
+        if box is not None:
+            box.pack_start(frame, True, True, 0)
+        box2 = gtk.VBox()
+        frame.add(box2)
+        if heading is not None:
+            pack(box2, [gtk.Label(heading)])
+        #pack(box, [gtk.Label("Output:")])
+        scrwin = gtk.ScrolledWindow()
+        scrwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.output = gtk.TextBuffer()
+        txtview = gtk.TextView(self.output)
+        txtview.set_editable(False)
+        scrwin.add(txtview)
+        scrwin.show_all()
+        box2.pack_start(scrwin, True, True, 0)
+        self.savebutton = gtk.Button(stock=gtk.STOCK_SAVE)
+        self.savebutton.connect('clicked', self.saveoutput)
+        self.savebutton.set_sensitive(False)
+        pack(box2, [self.savebutton])
+        box2.show()
+        frame.show()
+        return frame
+    
+    def activate_output(self):
+        self.savebutton.set_sensitive(True)
+
+    def saveoutput(self, widget):
+        chooser = gtk.FileChooserDialog(
+            _('Save output'), None, gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        ok = chooser.run()
+        if ok == gtk.RESPONSE_OK:
+            filename = chooser.get_filename()
+            txt = self.output.get_text(self.output.get_start_iter(),
+                                       self.output.get_end_iter())
+            f = open(filename, "w")
+            f.write(txt)
+            f.close()
+        chooser.destroy()
+        
+class EnergyForces(Simulation, OutputFieldMixin):
+    def __init__(self, gui):
+        Simulation.__init__(self, gui)
+        self.set_title(_("Potential energy and forces"))
+        self.set_default_size(-1, 400)
+        vbox = gtk.VBox()
+        self.packtext(vbox,
+                      _("Calculate potential energy and the force on all "
+                        "atoms"))
+        self.packimageselection(vbox)
+        pack(vbox, gtk.Label(""))
+        self.forces = gtk.CheckButton(_("Write forces on the atoms"))
+        self.forces.set_active(True)
+        pack(vbox, [self.forces])
+        pack(vbox, [gtk.Label("")])
+        self.makeoutputfield(vbox)
+        pack(vbox, gtk.Label(""))
+        self.makebutbox(vbox)
+        vbox.show()
+        self.add(vbox)
+        self.show()
+        self.gui.register_vulnerable(self)
+        
+    def run(self, *args):
+        if not self.setup_atoms():
+            return
+        self.begin()
+        e = self.atoms.get_potential_energy()
+        txt = _("Potential Energy:\n")
+        txt += _("  %8.2f eV\n") % (e,)
+        txt += _("  %8.4f eV/atom\n\n") % (e/len(self.atoms),)
+        if self.forces.get_active():
+            txt += _("Forces:\n")
+            forces = self.atoms.get_forces()
+            for f in forces:
+                txt += "  %8.3f, %8.3f, %8.3f eV/Å\n" % tuple(f)
+        self.output.set_text(txt)
+        self.activate_output()
+        self.end()
+                
+    def notify_atoms_changed(self):
+        "When atoms have changed, check for the number of images."
+        self.setupimageselection()
diff --git a/ase/gui/execute.py b/ase/gui/execute.py
new file mode 100644
index 0000000..1b7564b
--- /dev/null
+++ b/ase/gui/execute.py
@@ -0,0 +1,329 @@
+#!/usr/bin/env python
+import __future__
+import gtk
+from gettext import gettext as _
+import os.path
+import numpy as np
+import sys
+
+from ase.gui.widgets import pack, Help
+from ase.data.colors import jmol_colors
+from ase.atoms import Atoms
+
+class Execute(gtk.Window):
+    """ The Execute class provides an expert-user window for modification
+    and evaluation of system properties with a simple one-line command structure.
+    There are two types of commands, one set only applies to the global image and
+    one set applies to all atoms. If the command line contains any of the atom
+    commands, then it is executed separately for all atoms and for all images.
+    Otherwise it is executed only once per image. 
+
+    Please do not mix global and atom commands."""
+    
+    terminal_help_txt=_("""
+    Global commands work on all frames or only on the current frame
+    - Assignment of a global variable may not reference a local one
+    - use 'Current frame' switch to switch off application to all frames
+    <c>e</c>:\t\ttotal energy of one frame
+    <c>fmax</c>:\tmaximal force in one frame
+    <c>A</c>:\tunit cell
+    <c>E</c>:\t\ttotal energy array of all frames
+    <c>F</c>:\t\tall forces in one frame
+    <c>M</c>:\tall magnetic moments
+    <c>R</c>:\t\tall atomic positions
+    <c>S</c>:\tall selected atoms (boolean array)
+    <c>D</c>:\tall dynamic atoms (boolean array)
+    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>
+
+    Atom commands work on each atom (or a selection) individually
+    - these can use global commands on the RHS of an equation
+    - use 'selected atoms only' to restrict application of command
+    <c>x,y,z</c>:\tatomic coordinates
+    <c>r,g,b</c>:\tatom display color, range is [0..1]
+    <c>rad</c>:\tatomic radius for display
+    <c>s</c>:\t\tatom is selected
+    <c>d</c>:\t\tatom is movable
+    <c>f</c>:\t\tforce
+    <c>Z</c>:\tatomic number
+    <c>m</c>:\tmagnetic moment
+    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>
+
+    Special commands and objects:
+    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame
+    <c>frame</c>:\tframe number
+    <c>center</c>:\tcenters the system in its existing unit cell
+    <c>del S</c>:\tdelete selection
+    <c>CM</c>:\tcenter of mass
+    <c>ans[-i]</c>:\tith last calculated result
+    <c>exec file</c>: executes commands listed in file
+    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z
+    <c>gui</c>:\tadvanced: ag window python object
+    <c>img</c>:\tadvanced: ag images object
+    """)
+    
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.gui = gui
+        self.set_title(_('Expert user mode'))
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.sw = gtk.ScrolledWindow()
+        self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.textview = gtk.TextView()
+        self.textbuffer = self.textview.get_buffer()
+        self.textview.set_editable(False)
+        self.textview.set_cursor_visible(False)
+        self.sw.add(self.textview)
+        pack(vbox, self.sw, expand=True, padding = 5)
+        self.sw.set_size_request(540, 150)
+        self.textview.show()
+        self.add_text(_('Welcome to the ASE Expert user mode'))
+        self.cmd = gtk.Entry(60)
+        self.cmd.connect('activate', self.execute)
+        self.cmd.connect('key-press-event', self.update_command_buffer)
+        pack(vbox, [gtk.Label('>>>'),self.cmd])
+        self.cmd_buffer = getattr(gui,'expert_mode_buffer',[''])
+        self.cmd_position = len(self.cmd_buffer)-1
+        self.selected = gtk.CheckButton(_('Only selected atoms (sa)   '))
+        self.selected.connect('toggled',self.selected_changed)
+        self.images_only = gtk.CheckButton(_('Only current frame (cf)  '))
+        self.images_only.connect('toggled',self.images_changed)
+        pack(vbox, [self.selected, self.images_only])
+        save_button = gtk.Button(stock=gtk.STOCK_SAVE)
+        save_button.connect('clicked',self.save_output)
+        help_button = gtk.Button(stock=gtk.STOCK_HELP)
+        help_button.connect('clicked',self.terminal_help,"")
+        stop_button = gtk.Button(stock=gtk.STOCK_STOP)
+        stop_button.connect('clicked',self.stop_execution)
+        self.stop = False
+        pack(vbox, [gtk.Label(_('Global: Use A, D, E, M, N, R, S, n, frame;'
+                                ' Atoms: Use a, f, m, s, x, y, z, Z     ')),
+                    stop_button, help_button, save_button], end = True)
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        # set color mode to manual when opening this window for rgb manipulation
+        self.colors = self.gui.get_colors()
+        rgb_data = self.gui.get_colors(rgb = True)
+        self.rgb_data = []  # ensure proper format of rgb_data
+        for i, rgb in enumerate(rgb_data):
+            self.rgb_data += [[i, rgb]]
+        self.gui.colordata = self.rgb_data
+        self.gui.colors = list(self.colors)
+        self.gui.colormode = 'manual'        
+        self.cmd.grab_focus()
+
+    def execute(self, widget=None, cmd = None):
+        global_commands = ['A','Col','D','e','E','F','frame','M','n','N','R','S']  # explicitly 'implemented' commands for use on whole system or entire single frame
+        index_commands  = ['a','b','d','f','g','m','r','rad','s','x','y','z','Z']  # commands for use on all (possibly selected) atoms
+
+        new = self.gui.drawing_area.window.new_gc
+        alloc = self.gui.colormap.alloc_color
+
+        self.stop = False
+        if cmd is None:
+            cmd = self.cmd.get_text().strip()
+            if len(cmd) == 0:
+                return
+            self.add_text('>>> '+cmd)
+            self.cmd_buffer[-1] = cmd
+            self.cmd_buffer += ['']
+            setattr(self.gui,'expert_mode_buffer', self.cmd_buffer)
+            self.cmd_position = len(self.cmd_buffer)-1
+            self.cmd.set_text('')
+        else:
+            self.add_text('--> '+cmd)
+
+        gui = self.gui
+        img = gui.images
+        frame = gui.frame
+        N = img.nimages
+        n = img.natoms
+        S = img.selected
+        D = img.dynamic[:, np.newaxis]
+        E = img.E
+        if self.selected.get_active():
+            indices = np.where(S)[0]
+        else:
+            indices = range(n)
+
+        ans = getattr(gui,'expert_mode_answers',[])
+
+        loop_images = range(N)
+        if self.images_only.get_active():
+            loop_images = [self.gui.frame]
+
+        # split off the first valid command in cmd to determine whether
+        # it is global or index based, this includes things such as 4*z and z*4
+        index_based = False
+        first_command = cmd.split()[0]
+        special = ['=',',','+','-','/','*',';','.','[',']','(',')',
+                   '{','}','0','1','2','3','4','5','6','7','8','9']
+        while first_command[0] in special and len(first_command)>1:
+            first_command = first_command[1:]
+        for c in special:
+            if c in first_command:
+                first_command = first_command[:first_command.find(c)]
+        for c in index_commands:
+            if c == first_command:
+                index_based = True
+
+        name = os.path.expanduser('~/.ase/'+cmd)
+        # check various special commands: 
+        if os.path.exists(name):   # run script from default directory
+            self.run_script(name)
+        elif cmd == 'del S':       # delete selection
+            gui.delete_selected_atoms()
+        elif cmd == 'sa':          # selected atoms only
+            self.selected.set_active(not self.selected.get_active())
+        elif cmd == 'cf':          # current frame only
+            self.images_only.set_active(not self.images_only.get_active())
+        elif cmd == 'center':      # center system
+            img.center()
+        elif cmd == 'CM':          # calculate center of mass
+            for i in loop_images:
+                if self.stop:
+                    break
+                atoms = Atoms(positions=img.P[i][indices],
+                              numbers=img.Z[indices])
+                self.add_text(repr(atoms.get_center_of_mass()))
+                ans += [atoms.get_center_of_mass()]
+        elif first_command == 'exec': # execute script
+            name = cmd.split()[1]
+            if '~' in name:
+                name = os.path.expanduser(name)
+            if os.path.exists(name):
+                self.run_script(name)
+            else:
+                self.add_text(_('*** WARNING: file does not exist - %s') % name)
+        else:
+            code = compile(cmd + '\n', 'execute.py', 'single',
+                           __future__.CO_FUTURE_DIVISION)
+            if index_based and len(indices) == 0 and self.selected.get_active():
+                self.add_text(_("*** WARNING: No atoms selected to work with"))
+            for i in loop_images:
+                if self.stop:
+                    break
+                R = img.P[i][indices]
+                A = img.A[i]
+                F = img.F[i][indices]
+                e = img.E[i]
+                M = img.M[i][indices]
+                Col = []
+                cov = img.covalent_radii
+                for j in indices:
+                    Col += [gui.colordata[j]]
+                if len(indices) > 0:
+                    fmax = max(((F * D[indices])**2).sum(1)**.5)
+                else:
+                    fmax = None
+                frame = gui.frame
+            
+                if not index_based:
+                    try:
+                        self.add_text(repr(eval(cmd)))
+                        ans += [eval(cmd)]
+                    except:
+                        exec code
+                    gui.set_frame(frame)
+                    if gui.movie_window is not None:
+                        gui.movie_window.frame_number.value = frame
+                    img.selected      = S
+                    img.A[i]          = A
+                    img.P[i][indices] = R
+                    img.M[i][indices] = M
+                else:
+                    for n,a in enumerate(indices):
+                        if self.stop:
+                            break
+                        x, y, z = R[n]
+                        r, g, b = Col[n][1]
+                        d = D[a]
+                        f = np.vdot(F[n]*d,F[n]*d)**0.5
+                        s = S[a]
+                        Z = img.Z[a]
+                        Zold = Z
+                        m = M[n]
+                        rad = img.r[a]
+                        try:
+                            self.add_text(repr(eval(cmd)))
+                            ans += [eval(cmd)]
+                        except:
+                            exec code
+                        S[a] = s
+                        img.P[i][a] = x, y, z
+                        img.Z[a] = Z
+                        img.r[a] = rad
+                        img.dynamic[a] = d
+                        if Z != Zold:
+                            img.r[a] = cov[Z] * 0.89
+                            r,g,b = jmol_colors[Z]
+                        gui.colordata[a] = [a,[r,g,b]]                            
+                        color = tuple([int(65535*x) for x in [r,g,b]])
+                        gui.colors[a] = new(alloc(*color))
+                        img.M[i][a] = m
+        setattr(self.gui,'expert_mode_answers', ans)
+        gui.set_frame(frame,init=True)
+
+    def add_text(self,val):
+        text_end = self.textbuffer.get_end_iter()
+        self.textbuffer.insert(text_end,val+'\n');
+        if self.sw.get_vscrollbar() is not None:
+            scroll = self.sw.get_vscrollbar().get_adjustment()
+            scroll.set_value(scroll.get_upper())
+        
+    def selected_changed(self, *args):
+        if self.selected.get_active():
+            self.add_text(_('*** Only working on selected atoms'))
+        else:
+            self.add_text(_('*** Working on all atoms'))
+
+    def images_changed(self, *args):
+        if self.images_only.get_active():
+            self.add_text(_('*** Only working on current image'))
+        else:
+            self.add_text(_('*** Working on all images'))
+
+    def update_command_buffer(self, entry, event, *args):
+        arrow = {gtk.keysyms.Up: -1, gtk.keysyms.Down: 1}.get(event.keyval, None)
+        if arrow is not None:
+            self.cmd_position += arrow
+            self.cmd_position = max(self.cmd_position,0)
+            self.cmd_position = min(self.cmd_position,len(self.cmd_buffer)-1)
+            cmd = self.cmd_buffer[self.cmd_position]
+            self.cmd.set_text(cmd)
+            return True
+        else:
+            return False
+
+    def save_output(self, *args):
+        chooser = gtk.FileChooserDialog(
+            _('Save Terminal text ...'), None, gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        save = chooser.run()
+        if save == gtk.RESPONSE_OK or save == gtk.RESPONSE_SAVE:
+            filename = chooser.get_filename()
+            text = self.textbuffer.get_text(self.textbuffer.get_start_iter(),
+                                            self.textbuffer.get_end_iter())
+            fd = open(filename,'w')
+            fd.write(text)
+            fd.close()
+            chooser.destroy()
+
+    def run_script(self, name):
+        commands = open(name,'r').readlines()
+        for c_parse in commands:
+            c = c_parse.strip()
+            if '#' in c:
+                c = c[:c.find('#')].strip()
+            if len(c) > 0:
+                self.execute(cmd = c.strip())
+            
+    def terminal_help(self,*args):
+        Help(self.terminal_help_txt)
+
+    def stop_execution(self, *args):
+        self.stop = True
+        
+    python = execute
diff --git a/ase/gui/graphene.py b/ase/gui/graphene.py
new file mode 100644
index 0000000..4c9a229
--- /dev/null
+++ b/ase/gui/graphene.py
@@ -0,0 +1,240 @@
+# encoding: utf-8
+"""nanotube.py - Window for setting up Graphene sheets and ribbons.
+"""
+
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops
+from ase.gui.setupwindow import SetupWindow
+from ase.gui.pybutton import PyButton
+from ase.structure import graphene_nanoribbon
+import ase
+import numpy as np
+
+introtext = _("""\
+Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may
+optionally be saturated with hydrogen (or another element).\
+""")
+
+py_template = """
+from ase.structure import nanotube
+
+atoms = nanotube(%(n)i, %(m)i, length=%(length)i, bond=%(bl).3f, symbol=%(symb)s)
+"""
+
+class SetupGraphene(SetupWindow):
+    "Window for setting up a graphene sheet or nanoribbon."
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Graphene"))
+        vbox = gtk.VBox()
+
+        # Intoductory text
+        self.packtext(vbox, introtext)
+
+        # Choose structure
+        label = gtk.Label(_("Structure: "))
+        self.struct = gtk.combo_box_new_text()
+        for s in (_("Infinite sheet"), _("Unsaturated ribbon"), 
+                  _("Saturated ribbon")):
+            self.struct.append_text(s)
+        self.struct.set_active(0)
+        self.struct.connect('changed', self.update_gui)
+        pack(vbox, [label, self.struct])
+
+        # Orientation
+        label = gtk.Label(_("Orientation: "))
+        self.orient = gtk.combo_box_new_text()
+        self.orient_text = []
+        for s in (_("zigzag"), _("armchair")):
+            self.orient.append_text(s)
+            self.orient_text.append(s)
+        self.orient.set_active(0)
+        self.orient.connect('changed', self.update_gui)
+        pack(vbox, [label, self.orient])
+        pack(vbox, gtk.Label(""))
+
+
+        # Choose the element and bond length
+        label1 = gtk.Label("Element: ")
+        #label.set_alignment(0.0, 0.2)
+        self.element = gtk.Entry(max=3)
+        self.element.set_text("C")
+        self.element.connect('activate', self.update_element)
+        self.bondlength = gtk.Adjustment(1.42, 0.0, 1000.0, 0.01)
+        label2 = gtk.Label(_("  Bond length: "))
+        label3 = gtk.Label(_("Å"))
+        bond_box = gtk.SpinButton(self.bondlength, 10.0, 3)
+        pack(vbox, [label1, self.element, label2, bond_box, label3])
+
+        # Choose the saturation element and bond length
+        self.sat_label1 = gtk.Label(_("Saturation: "))
+        #label.set_alignment(0.0, 0.2)
+        self.element2 = gtk.Entry(max=3)
+        self.element2.set_text(_("H"))
+        self.element2.connect('activate', self.update_element)
+        self.bondlength2 = gtk.Adjustment(1.12, 0.0, 1000.0, 0.01)
+        self.sat_label2 = gtk.Label(_("  Bond length: "))
+        self.sat_label3 = gtk.Label(_("Å"))
+        self.bond_box = gtk.SpinButton(self.bondlength2, 10.0, 3)
+        pack(vbox, [self.sat_label1, self.element2, self.sat_label2,
+                    self.bond_box, self.sat_label3])
+
+        self.elementinfo = gtk.Label("")
+        self.elementinfo.modify_fg(gtk.STATE_NORMAL,
+                                   gtk.gdk.color_parse('#FF0000'))
+        pack(vbox, [self.elementinfo])
+        pack(vbox, gtk.Label(""))
+
+        # Size
+        label1 = gtk.Label(_("Width: "))
+        label2 = gtk.Label(_("  Length: "))
+        self.n = gtk.Adjustment(1, 1, 100, 1)
+        self.m = gtk.Adjustment(1, 1, 100, 1)
+        spinn = gtk.SpinButton(self.n, 0, 0)
+        spinm = gtk.SpinButton(self.m, 0, 0)
+        pack(vbox, [label1, spinn, label2, spinm])
+
+        # Vacuum
+        label1 = gtk.Label(_("Vacuum: "))
+        self.vacuum = gtk.Adjustment(5.0, 0.0, 1000.0, 0.1)
+        label2 = gtk.Label(_("Å"))
+        vac_box = gtk.SpinButton(self.vacuum, 10.0, 2)
+        pack(vbox, [label1, vac_box, label2])
+        pack(vbox, gtk.Label(""))
+
+
+        # Buttons
+        #self.pybut = PyButton("Creating a nanoparticle.")
+        #self.pybut.connect('clicked', self.makeatoms)
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [buts], end=True, bottom=True)
+
+        # Finalize setup
+        self.update_gui()
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def update_element(self, *args):
+        "Called when a new element may have been entered."
+        # Assumes the element widget is self.element and that a label
+        # for errors is self.elementinfo.  The chemical symbol is
+        # placed in self.legalelement - or None if the element is
+        # invalid.
+        symb = []
+        if self.struct.get_active() == 2:
+            # Saturated nanoribbon
+            elements = (self.element.get_text(), self.element2.get_text())
+        else:
+            elements = (self.element.get_text(), )
+            
+        for elem in elements:
+            if not elem:
+                self.invalid_element(_("  No element specified!"))
+                return False
+            try:
+                z = int(elem)
+            except ValueError:
+                # Probably a symbol
+                try:
+                    z = ase.data.atomic_numbers[elem]
+                except KeyError:
+                    self.invalid_element()
+                    return False
+            try:
+                symb.append(ase.data.chemical_symbols[z])
+            except KeyError:
+                self.invalid_element()
+                return False
+        self.elementinfo.set_text("")
+        self.legal_element = symb[0]
+        if len(symb) == 2:
+            self.legal_element2 = symb[1]
+        else:
+            self.legal_element2 = None
+        return True
+        
+    def update_gui(self, *args):
+        # Saturation element is only relevant for saturated nanoribbons
+        satur = self.struct.get_active() == 2
+        for w in (self.element2, self.bond_box):
+            w.set_sensitive(satur)
+        # Infinite zigzag sheets must have even width
+        if self.struct.get_active() == 0 and self.orient.get_active() == 0:
+            if self.n.value % 2 == 1:
+                self.n.value += 1
+            self.n.lower = 2
+            self.n.step_increment = 2
+        else:
+            self.n.lower = 1
+            self.n.step_increment = 1
+        
+    def makeatoms(self, *args):
+        self.update_element()
+        if self.legal_element is None or (self.struct.get_active() == 2 and
+                                          self.legal_element2 is None):
+            self.atoms = None
+            self.pybut.python = None
+        else:
+            n = int(self.n.value)
+            m = int(self.m.value)
+            CC = self.bondlength.value
+            vacuum = self.vacuum.value
+            orient = self.orient_text[self.orient.get_active()]
+            elem = self.legal_element
+            if self.struct.get_active() == 0:
+                # Extended sheet
+                self.atoms = graphene_nanoribbon(n, m, type=orient, C_C=CC,
+                                                 vacc=vacuum, sheet=True,
+                                                 main_element=elem)
+            elif self.struct.get_active() == 1:
+                # Unsaturated nanoribbon
+                self.atoms = graphene_nanoribbon(n, m, type=orient, C_C=CC,
+                                                 vacc=vacuum,
+                                                 main_element=elem)
+            elif self.struct.get_active() == 2:
+                # Saturated nanoribbon
+                elem2 = self.legal_element2
+                self.atoms = graphene_nanoribbon(n, m, type=orient, C_C=CC,
+                                                 C_H=self.bondlength2.value,
+                                                 vacuum=vacuum,
+                                                 saturated=True,
+                                                 main_element=elem,
+                                                 saturate_element=elem2)
+            else:
+                raise RuntimeError("Unknown structure in SetupGraphene!")
+        # Now, rotate into the xy plane (ase.gui's default view plane)
+        pos = self.atoms.get_positions()
+        cell = self.atoms.get_cell()
+        pbc = self.atoms.get_pbc()
+        cell[1,1], cell[2,2] = cell[2,2], cell[1,1]
+        x = pos[:,1].copy()
+        z = pos[:,2].copy()
+        pos[:,1] = z
+        pos[:,2] = x
+        self.atoms.set_cell(cell)
+        self.atoms.set_positions(pos)
+        self.atoms.set_pbc([pbc[0], pbc[2], pbc[1]])
+
+    def apply(self, *args):
+        self.makeatoms()
+        if self.atoms is not None:
+            self.gui.new_atoms(self.atoms)
+            return True
+        else:
+            oops(_("No valid atoms."),
+                 _("You have not (yet) specified a consistent set of "
+                   "parameters."))
+            return False
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+            
+            
+                                                 
diff --git a/ase/gui/graphs.py b/ase/gui/graphs.py
new file mode 100644
index 0000000..1ad7c59
--- /dev/null
+++ b/ase/gui/graphs.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+from math import sqrt
+
+import gtk
+from gettext import gettext as _
+
+from ase.gui.widgets import pack, help
+
+
+class Graphs(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        #self.window.set_position(gtk.WIN_POS_CENTER)
+        #self.window.connect("destroy", lambda w: gtk.main_quit())
+        #self.window.connect('delete_event', self.exit)
+        self.set_title('Graphs')
+        vbox = gtk.VBox()
+        self.expr = pack(vbox, [gtk.Entry(64),
+                                help(_('Help for plot ...'))])[0]
+        self.expr.connect('activate', self.plot)
+
+        completion = gtk.EntryCompletion()
+        self.liststore = gtk.ListStore(str)
+        for s in ['fmax', 's, e-E[0]', 'i, d(0,1)']:
+            self.liststore.append([s])
+        completion.set_model(self.liststore)
+        self.expr.set_completion(completion)
+        completion.set_text_column(0)
+
+        button = pack(vbox, [gtk.Button(_('Plot')),
+                             gtk.Label(' x, y1, y2, ...')])[0]
+        button.connect('clicked', self.plot, 'xy')
+        button = pack(vbox, [gtk.Button(_('Plot')),
+                             gtk.Label(' y1, y2, ...')])[0]
+        button.connect('clicked', self.plot, 'y')
+        save_button = gtk.Button(stock=gtk.STOCK_SAVE)
+        save_button.connect('clicked',self.save)
+        clear_button = gtk.Button(_('clear'))
+        clear_button.connect('clicked', self.clear)
+        pack(vbox, [save_button,clear_button])
+        
+
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def plot(self, button=None, type=None, expr=None):
+        if expr is None:
+            expr = self.expr.get_text()
+        else:
+            self.expr.set_text(expr)
+
+        if expr not in [row[0] for row in self.liststore]:
+            self.liststore.append([expr])
+
+        data = self.gui.images.graph(expr)
+        
+        import matplotlib
+        matplotlib.interactive(True)
+        matplotlib.use('GTK')
+        #matplotlib.use('GTK', warn=False)# Not avail. in 0.91 (it is in 0.98)
+        import pylab
+        pylab.ion()
+
+        x = 2.5
+        self.gui.graphs.append(pylab.figure(figsize=(x * 2.5**0.5, x)))
+        i = self.gui.frame
+        m = len(data)
+
+        if type is None:
+            if m == 1:
+                type = 'y'
+            else:
+                type = 'xy'
+                
+        if type == 'y':
+            for j in range(m):
+                pylab.plot(data[j])
+                pylab.plot([i], [data[j, i]], 'o')
+        else:
+            for j in range(1, m):
+                pylab.plot(data[0], data[j])
+                pylab.plot([data[0, i]], [data[j, i]], 'o')
+        pylab.title(expr)
+        #pylab.show()
+
+    python = plot
+
+    def save(self, filename):
+        chooser = gtk.FileChooserDialog(
+            _('Save data to file ... '), None, gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        save = chooser.run()
+        if save == gtk.RESPONSE_OK:
+            filename = chooser.get_filename()
+            expr = self.expr.get_text()
+            data = self.gui.images.graph(expr)
+            expr = '# '+expr
+            fd = open(filename,'w')
+            fd.write("%s \n" % (expr))
+            for s in range(len(data[0])):
+                for i in range(len(data)):
+                    val = data[i,s]
+                    fd.write("%12.8e\t" % (val))
+                fd.write("\n")
+            fd.close()
+        chooser.destroy()
+
+    def clear(self, button):
+        import pylab
+        for graph in self.gui.graphs:
+            pylab.close(graph)
+        self.gui.graphs = []
+        
diff --git a/ase/gui/gtkexcepthook.py b/ase/gui/gtkexcepthook.py
new file mode 100644
index 0000000..73e0430
--- /dev/null
+++ b/ase/gui/gtkexcepthook.py
@@ -0,0 +1,227 @@
+# vim: sw=4 ts=4:
+#
+# (c) 2003 Gustavo J A M Carneiro gjc at inescporto.pt
+# 	2004-2005 Filip Van Raemdonck
+#
+# http://www.daa.com.au/pipermail/pygtk/2003-August/005775.html
+# Message-ID: <1062087716.1196.5.camel at emperor.homelinux.net>
+# 	"The license is whatever you want."
+#
+# This file was downloaded from http://www.sysfs.be/downloads/
+# Minor adaptions 2009 by Martin Renold:
+# - let KeyboardInterrupt through
+# - print traceback to stderr before showing the dialog
+# - nonzero exit code when hitting the "quit" button
+# - suppress more dialogs while one is already active
+# see also http://faq.pygtk.org/index.py?req=show&file=faq20.010.htp
+# (The license is still whatever you want.)
+
+import inspect, linecache, pydoc, sys, traceback
+from cStringIO import StringIO
+from gettext import gettext as _
+from smtplib import SMTP
+
+import pygtk
+pygtk.require ('2.0')
+import gtk, pango
+
+def analyse_simple (exctyp, value, tb):
+	trace = StringIO()
+	traceback.print_exception (exctyp, value, tb, None, trace)
+	return trace
+
+def lookup (name, frame, lcls):
+	'''Find the value for a given name in the given frame'''
+	if name in lcls:
+		return 'local', lcls[name]
+	elif name in frame.f_globals:
+		return 'global', frame.f_globals[name]
+	elif '__builtins__' in frame.f_globals:
+		builtins = frame.f_globals['__builtins__']
+		if type (builtins) is dict:
+			if name in builtins:
+				return 'builtin', builtins[name]
+		else:
+			if hasattr (builtins, name):
+				return 'builtin', getattr (builtins, name)
+	return None, []
+
+def analyse (exctyp, value, tb):
+	import tokenize, keyword
+
+	trace = StringIO()
+	nlines = 3
+	frecs = inspect.getinnerframes (tb, nlines)
+	trace.write ('Traceback (most recent call last):\n')
+	for frame, fname, lineno, funcname, context, cindex in frecs:
+		trace.write ('  File "%s", line %d, ' % (fname, lineno))
+		args, varargs, varkw, lcls = inspect.getargvalues (frame)
+
+		def readline (lno=[lineno], *args):
+			if args: print args
+			try: return linecache.getline (fname, lno[0])
+			finally: lno[0] += 1
+		all, prev, name, scope = {}, None, '', None
+		for ttype, tstr, stup, etup, line in tokenize.generate_tokens (readline):
+			if ttype == tokenize.NAME and tstr not in keyword.kwlist:
+				if name:
+					if name[-1] == '.':
+						try:
+							val = getattr (prev, tstr)
+						except AttributeError:
+							# XXX skip the rest of this identifier only
+							break
+						name += tstr
+				else:
+					assert not name and not scope
+					scope, val = lookup (tstr, frame, lcls)
+					name = tstr
+				if hasattr(val, "shape") and len(val):
+					prev = val
+				elif val:
+					prev = val
+				#print '  found', scope, 'name', name, 'val', val, 'in', prev, 'for token', tstr
+			elif tstr == '.':
+				if prev:
+					name += '.'
+			else:
+				if name:
+					all[name] = (scope, prev)
+				prev, name, scope = None, '', None
+				if ttype == tokenize.NEWLINE:
+					break
+
+		trace.write (funcname +
+		  inspect.formatargvalues (args, varargs, varkw, lcls, formatvalue=lambda v: '=' + pydoc.text.repr (v)) + '\n')
+		trace.write (''.join (['    ' + x.replace ('\t', '  ') for x in filter (lambda a: a.strip(), context)]))
+		if len (all):
+			trace.write ('  variables: %s\n' % str (all))
+
+	trace.write ('%s: %s' % (exctyp.__name__, value))
+	return trace
+
+def _info (exctyp, value, tb):
+	global exception_dialog_active
+	if exctyp is KeyboardInterrupt:
+		return original_excepthook(exctyp, value, tb)
+	sys.stderr.write(analyse_simple (exctyp, value, tb).getvalue())
+	if exception_dialog_active:
+		return
+
+	gtk.gdk.pointer_ungrab()
+	gtk.gdk.keyboard_ungrab()
+
+	exception_dialog_active = True
+	trace = None
+	dialog = gtk.MessageDialog (parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE)
+	dialog.set_title(_("Bug Detected"))
+	if gtk.check_version (2, 4, 0) is not None:
+		dialog.set_has_separator (False)
+
+	primary = "<big><b>%s</b></big>" % _("A programming error has been "
+					     "detected.")
+	primary += '\n\n<span color="red">'+str(value)+'</span>'
+	secondary = _("It probably isn't fatal, but the details should be "
+		      "reported to the developers nonetheless.")
+
+	try:
+		setsec = dialog.format_secondary_text
+	except AttributeError:
+		raise
+		dialog.vbox.get_children()[0].get_children()[1].set_markup ('%s\n\n%s' % (primary, secondary))
+		#lbl.set_property ("use-markup", True)
+	else:
+		del setsec
+		dialog.set_markup (primary)
+		dialog.format_secondary_text (secondary)
+
+	try:
+		email = feedback
+		dialog.add_button(_("Report..."), 3)
+	except NameError:
+		# could ask for an email address instead...
+		pass
+	dialog.add_button (_("Details..."), 2)
+	dialog.add_button (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+	dialog.add_button (gtk.STOCK_QUIT, 1)
+
+	while True:
+		resp = dialog.run()
+		if resp == 3:
+			if trace == None:
+				trace = analyse (exctyp, value, tb)
+
+			# TODO: prettyprint, deal with problems in sending feedback, &tc
+			try:
+				server = smtphost
+			except NameError:
+				server = 'localhost'
+
+			message = _('From: buggy_application"\nTo: bad_programmer\nSubject: Exception feedback\n\n%s') % trace.getvalue()
+
+			s = SMTP()
+			s.connect (server)
+			s.sendmail (email, (email,), message)
+			s.quit()
+			break
+
+		elif resp == 2:
+			if trace == None:
+				trace = analyse (exctyp, value, tb)
+
+			# Show details...
+			details = gtk.Dialog (_("Bug Details"), dialog,
+			  gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+			  (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, ))
+			details.set_property ("has-separator", False)
+
+			textview = gtk.TextView(); textview.show()
+			textview.set_editable (False)
+			textview.modify_font (pango.FontDescription ("Monospace"))
+
+			sw = gtk.ScrolledWindow(); sw.show()
+			sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+			sw.add (textview)
+			details.vbox.add (sw)
+			textbuffer = textview.get_buffer()
+			textbuffer.set_text (trace.getvalue())
+
+			monitor = gtk.gdk.screen_get_default ().get_monitor_at_window (dialog.window)
+			area = gtk.gdk.screen_get_default ().get_monitor_geometry (monitor)
+			try:
+				w = area.width // 1.6
+				h = area.height // 1.6
+			except SyntaxError:
+				# python < 2.2
+				w = area.width / 1.6
+				h = area.height / 1.6
+			details.set_default_size (int (w), int (h))
+
+			details.run()
+			details.destroy()
+
+		elif resp == 1 and gtk.main_level() > 0:
+			#gtk.main_quit() - why...? Exit code 0 is bad for IDEs.
+			sys.exit(1)
+			break
+		else:
+			break
+
+	dialog.destroy()
+	exception_dialog_active = False
+
+original_excepthook = sys.excepthook
+sys.excepthook = _info
+exception_dialog_active = False
+
+if __name__ == '__main__':
+	class X (object):
+		pass
+	x = X()
+	x.y = 'Test'
+	x.z = x
+	w = ' e'
+	#feedback = 'developer at bigcorp.comp'
+	#smtphost = 'mx.bigcorp.comp'
+	1, x.z.y, f, w
+	raise Exception (x.z.y + w)
diff --git a/ase/gui/gui.py b/ase/gui/gui.py
new file mode 100644
index 0000000..742bc49
--- /dev/null
+++ b/ase/gui/gui.py
@@ -0,0 +1,1299 @@
+# husk:
+# Exit*2?  remove pylab.show()
+# close button
+# DFT
+# ADOS
+# grey-out stuff after one second: vmd, rasmol, ...
+# Show with ....
+# rasmol: set same rotation as ag
+# Graphs: save, Python, 3D
+# start from python (interactive mode?)
+# ascii-art option (colored)|
+# option -o (output) and -f (force overwrite)
+# surfacebuilder
+# screen-dump
+# icon
+# ag-community-server
+# translate option: record all translations, 
+# and check for missing translations.
+
+#TODO: Add possible way of choosing orinetations. \
+#TODO: Two atoms defines a direction, three atoms their normal does
+#TODO: Align orientations chosen in Rot_selected v unselcted
+#TODO: Get the atoms_rotate_0 thing string
+#TODO: Use set atoms instead og the get atoms
+#TODO: Arrow keys will decide how the orientation changes
+#TODO: Undo redo que should be implemented
+#TODO: Update should have possibility to change positions
+#TODO: Window for rotation modes and move moves which can be chosen
+#TODO: WHen rotate and move / hide the movie menu
+
+import os
+import sys
+import weakref
+from gettext import gettext as _
+from gettext import ngettext
+import numpy as np
+
+import pygtk
+pygtk.require("2.0")
+
+import gtk
+from ase.gui.view import View
+from ase.gui.status import Status
+from ase.gui.widgets import pack, help, Help, oops
+#from ase.gui.languages import translate as _
+
+from ase.gui.settings import Settings
+from ase.gui.crystal import SetupBulkCrystal
+from ase.gui.surfaceslab import SetupSurfaceSlab
+from ase.gui.nanoparticle import SetupNanoparticle
+from ase.gui.nanotube import SetupNanotube
+from ase.gui.graphene import SetupGraphene
+from ase.gui.calculator import SetCalculator
+from ase.gui.energyforces import EnergyForces
+from ase.gui.minimize import Minimize
+from ase.gui.scaling import HomogeneousDeformation
+from ase.gui.quickinfo import QuickInfo
+from ase.gui.defaults import read_defaults
+
+ui_info = """\
+<ui>
+  <menubar name='MenuBar'>
+    <menu action='FileMenu'>
+      <menuitem action='Open'/>
+      <menuitem action='New'/>
+      <menuitem action='Save'/>
+      <separator/>
+      <menuitem action='Quit'/>
+    </menu>
+    <menu action='EditMenu'>
+      <menuitem action='SelectAll'/>
+      <menuitem action='Invert'/>
+      <menuitem action='SelectConstrained'/>
+      <menuitem action='SelectImmobile'/>
+      <separator/>
+      <menuitem action='Copy'/>
+      <menuitem action='Paste'/>
+      <separator/>
+      <menuitem action='Modify'/>
+      <menuitem action='AddAtoms'/>
+      <menuitem action='DeleteAtoms'/>
+      <separator/>      
+      <menuitem action='First'/>
+      <menuitem action='Previous'/>
+      <menuitem action='Next'/>
+      <menuitem action='Last'/>
+    </menu>
+    <menu action='ViewMenu'>
+      <menuitem action='ShowUnitCell'/>
+      <menuitem action='ShowAxes'/>
+      <menuitem action='ShowBonds'/>
+      <separator/>
+      <menuitem action='QuickInfo'/>
+      <menuitem action='Repeat'/>
+      <menuitem action='Rotate'/>
+      <menuitem action='Colors'/>
+      <menuitem action='Focus'/>
+      <menuitem action='ZoomIn'/>
+      <menuitem action='ZoomOut'/>
+      <menuitem action='ResetView'/>
+      <menuitem action='Settings'/>
+      <menuitem action='VMD'/>
+      <menuitem action='RasMol'/>
+      <menuitem action='XMakeMol'/>
+      <menuitem action='Avogadro'/>
+    </menu>
+    <menu action='ToolsMenu'>
+      <menuitem action='Graphs'/>
+      <menuitem action='Movie'/>
+      <menuitem action='EModify'/>
+      <menuitem action='Constraints'/>
+      <menuitem action='RenderScene'/>
+      <menuitem action='MoveAtoms'/>
+      <menuitem action='RotateAtoms'/>
+      <menuitem action='OrientAtoms'/>
+      <menuitem action='DFT'/>
+      <menuitem action='NEB'/>
+      <menuitem action='BulkModulus'/>
+    </menu>
+    <menu action='SetupMenu'>
+      <menuitem action='Bulk'/>
+      <menuitem action='Surface'/>
+      <menuitem action='Nanoparticle'/>
+      <menuitem action='Graphene'/>
+      <menuitem action='Nanotube'/>
+    </menu>
+    <menu action='CalculateMenu'>
+      <menuitem action='SetCalculator'/>
+      <separator/>
+      <menuitem action='EnergyForces'/>
+      <menuitem action='Minimize'/>
+      <menuitem action='Scaling'/>
+    </menu>
+    <menu action='HelpMenu'>
+      <menuitem action='About'/>
+      <menuitem action='Webpage'/>
+      <menuitem action='Debug'/>
+    </menu>
+  </menubar>
+</ui>"""
+
+class GUI(View, Status):
+    def __init__(self, images, rotations='', show_unit_cell=True,
+                 show_bonds=False):
+        # Try to change into directory of file you are viewing
+        try:
+            os.chdir(os.path.split(sys.argv[1])[0])
+        # This will fail sometimes (e.g. for starting a new session)
+        except:
+            pass
+        self.images = images
+        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+        #self.window.set_icon(gtk.gdk.pixbuf_new_from_file('guiase.png'))
+        self.window.set_position(gtk.WIN_POS_CENTER)
+        #self.window.connect("destroy", lambda w: gtk.main_quit())
+        self.window.connect('delete_event', self.exit)
+        vbox = gtk.VBox()
+        self.window.add(vbox)
+        if gtk.pygtk_version < (2, 12):
+            self.set_tip = gtk.Tooltips().set_tip
+
+        actions = gtk.ActionGroup("Actions")
+        actions.add_actions([
+            ('FileMenu', None, _('_File')),
+            ('EditMenu', None, _('_Edit')),
+            ('ViewMenu', None, _('_View')),
+            ('ToolsMenu', None, _('_Tools')),
+            # TRANSLATORS: Set up (i.e. build) surfaces, nanoparticles, ...
+            ('SetupMenu', None, _('_Setup')),
+            ('CalculateMenu', None, _('_Calculate')),
+            ('HelpMenu', None, _('_Help')),
+            ('Open', gtk.STOCK_OPEN, _('_Open'), '<control>O',
+             _('Create a new file'),
+             self.open),
+             ('New', gtk.STOCK_NEW, _('_New'), '<control>N',
+             _('New ase.gui window'),
+             lambda widget: os.system('ag &')),
+            ('Save', gtk.STOCK_SAVE, _('_Save'), '<control>S',
+             _('Save current file'),
+             self.save),
+            ('Quit', gtk.STOCK_QUIT, _('_Quit'), '<control>Q',
+             _('Quit'),
+             self.exit),
+            ('SelectAll', None, _('Select _all'), None,
+             '',
+             self.select_all),
+            ('Invert', None, _('_Invert selection'), None,
+             '',
+             self.invert_selection),
+            ('SelectConstrained', None, _('Select _constrained atoms'), None,
+             '',
+             self.select_constrained_atoms),
+            ('SelectImmobile', None, _('Select _immobile atoms'), '<control>I',
+             '',
+             self.select_immobile_atoms),
+             ('Copy', None, _('_Copy'), '<control>C',
+              _('Copy current selection and its orientation to clipboard'),
+              self.copy_atoms),
+             ('Paste', None, _('_Paste'), '<control>V',
+              _('Insert current clipboard selection'),
+              self.paste_atoms),
+             ('Modify', None, _('_Modify'), '<control>Y',
+              _('Change tags, moments and atom types of the selected atoms'),
+              self.modify_atoms),
+             ('AddAtoms', None, _('_Add atoms'), '<control>A',
+              _('Insert or import atoms and molecules'),
+              self.add_atoms),
+             ('DeleteAtoms', None, _('_Delete selected atoms'), 'BackSpace',
+              _('Delete the selected atoms'),
+              self.delete_selected_atoms),             
+            ('First', gtk.STOCK_GOTO_FIRST, _('_First image'), 'Home',
+             '',
+             self.step),
+            ('Previous', gtk.STOCK_GO_BACK, _('_Previous image'), 'Page_Up',
+             '',
+             self.step),
+            ('Next', gtk.STOCK_GO_FORWARD, _('_Next image'), 'Page_Down',
+             '',
+             self.step),
+            ('Last', gtk.STOCK_GOTO_LAST, _('_Last image'), 'End',
+             '',
+             self.step),
+            ('QuickInfo', None, _('Quick Info ...'), None,
+             '',
+             self.quick_info_window),
+            ('Repeat', None, _('Repeat ...'), None,
+             '',
+             self.repeat_window),
+            ('Rotate', None, _('Rotate ...'), None,
+             '',
+             self.rotate_window),
+            ('Colors', None, _('Colors ...'), None, '',
+             self.colors_window),
+            # TRANSLATORS: verb
+            ('Focus', gtk.STOCK_ZOOM_FIT, _('Focus'), 'F',
+             '',
+             self.focus),
+            ('ZoomIn', gtk.STOCK_ZOOM_IN, _('Zoom in'), 'plus',
+             '',
+             self.zoom),
+            ('ZoomOut', gtk.STOCK_ZOOM_OUT, _('Zoom out'), 'minus',
+             '',
+             self.zoom),
+            ('ResetView', None, _('Reset View'), 'equal',
+             '',
+             self.reset_view),
+            ('Settings', gtk.STOCK_PREFERENCES, _('Settings ...'), None,
+             '',
+             self.settings),
+            ('VMD', None, _('VMD'), None,
+             '',
+             self.external_viewer),
+            ('RasMol', None, _('RasMol'), None,
+             '',
+             self.external_viewer),
+            ('XMakeMol', None, _('xmakemol'), None,
+             '',
+             self.external_viewer),
+            ('Avogadro', None, _('avogadro'), None,
+             '',
+             self.external_viewer),
+            ('Graphs', None, _('Graphs ...'), None,
+             '',
+             self.plot_graphs),
+            ('Movie', None, _('Movie ...'), None,
+             '',
+             self.movie),
+            ('EModify', None, _('Expert mode ...'), '<control>E',
+             '',
+             self.execute),
+            ('Constraints', None, _('Constraints ...'), None,
+             '',
+             self.constraints_window),
+            ('RenderScene', None, _('Render scene ...'), None,
+             '',
+             self.render_window),
+            ('DFT', None, _('DFT ...'), None,
+             '',
+             self.dft_window),
+            ('NEB', None, _('NE_B'), None,
+             '',
+             self.NEB),
+            ('BulkModulus', None, _('B_ulk Modulus'), None,
+             '',
+             self.bulk_modulus),
+            ('Bulk', None, _('_Bulk Crystal'), None,
+             _("Create a bulk crystal with arbitrary orientation"),
+             self.bulk_window),
+            ('Surface', None, _('_Surface slab'), None,
+             _("Create the most common surfaces"),
+             self.surface_window),
+            ('Nanoparticle', None, _('_Nanoparticle'), None,
+             _("Create a crystalline nanoparticle"),
+             self.nanoparticle_window),
+            ('Nanotube', None, _('Nano_tube'), None,
+             _("Create a nanotube"),
+             self.nanotube_window),
+            ('Graphene', None, _('Graphene'), None,
+             _("Create a graphene sheet or nanoribbon"),
+             self.graphene_window),
+            ('SetCalculator', None, _('Set _Calculator'), None,
+             _("Set a calculator used in all calculation modules"),
+             self.calculator_window),
+            ('EnergyForces', None, _('_Energy and Forces'), None,
+             _("Calculate energy and forces"),
+             self.energy_window),
+            ('Minimize', None, _('Energy _Minimization'), None,
+             _("Minimize the energy"),
+             self.energy_minimize_window),
+            ('Scaling', None, _('Scale system'), None,
+             _("Deform system by scaling it"),
+             self.scaling_window),
+            ('About', None, _('_About'), None,
+             None,
+             self.about),
+            ('Webpage', gtk.STOCK_HELP, _('Webpage ...'), None, None, webpage),
+            ('Debug', None, _('Debug ...'), None, None, self.debug)])
+        actions.add_toggle_actions([
+            ('ShowUnitCell', None, _('Show _unit cell'), '<control>U',
+             'Bold',
+             self.toggle_show_unit_cell,
+             show_unit_cell > 0),
+            ('ShowAxes', None, _('Show _axes'), None,
+             'Bold',
+             self.toggle_show_axes,
+             True),
+            ('ShowBonds', None, _('Show _bonds'), '<control>B',
+             'Bold',
+             self.toggle_show_bonds,
+             show_bonds),
+            ('MoveAtoms', None, _('_Move atoms'), '<control>M',
+             'Bold',
+             self.toggle_move_mode,
+             False),
+            ('RotateAtoms', None, _('_Rotate atoms'), '<control>R',
+             'Bold',
+             self.toggle_rotate_mode,
+             False),
+            ('OrientAtoms', None, _('Orien_t atoms'), '<control>T',
+             'Bold',
+             self.toggle_orient_mode,
+             False)             
+            ])
+        self.ui = ui = gtk.UIManager()
+        ui.insert_action_group(actions, 0)
+        self.window.add_accel_group(ui.get_accel_group())
+
+        try:
+            mergeid = ui.add_ui_from_string(ui_info)
+        except gobject.GError, msg:
+            print _('building menus failed: %s') % msg
+
+        vbox.pack_start(ui.get_widget('/MenuBar'), False, False, 0)
+        
+        View.__init__(self, vbox, rotations)
+        Status.__init__(self, vbox)
+        vbox.show()
+        #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+        self.window.connect('key-press-event', self.scroll)
+        self.window.connect('scroll_event', self.scroll_event)
+        self.window.show()
+        self.graphs = []       # List of open pylab windows
+        self.graph_wref = []   # List of weakrefs to Graph objects
+        self.movie_window = None
+        self.clipboard = None  # initialize copy/paste functions
+        self.vulnerable_windows = []
+        self.simulation = {}   # Used by modules on Calculate menu.
+        self.module_state = {} # Used by modules to store their state.
+        self.config = read_defaults()
+
+    def run(self, expr=None):
+        self.set_colors()
+        self.set_coordinates(self.images.nimages - 1, focus=True)
+
+        if self.images.nimages > 1:
+            self.movie()
+
+        if expr is None and not np.isnan(self.images.E[0]):
+            expr = self.config['gui_graphs_string']
+
+        if expr is not None and expr != '' and self.images.nimages > 1:
+            self.plot_graphs(expr=expr)
+
+        gtk.main()
+    
+            
+    def step(self, action):
+        d = {'First': -10000000,
+             'Previous': -1,
+             'Next': 1,
+             'Last': 10000000}[action.get_name()]
+        i = max(0, min(self.images.nimages - 1, self.frame + d))
+        self.set_frame(i)
+        if self.movie_window is not None:
+            self.movie_window.frame_number.value = i
+            
+    def _do_zoom(self, x):
+        """Utility method for zooming"""
+        self.scale *= x
+        self.draw()
+        
+    def zoom(self, action):
+        """Zoom in/out on keypress or clicking menu item"""
+        x = {'ZoomIn': 1.2, 'ZoomOut':1 /1.2}[action.get_name()]
+        self._do_zoom(x)
+
+    def scroll_event(self, window, event):
+        """Zoom in/out when using mouse wheel"""
+        SHIFT = event.state == gtk.gdk.SHIFT_MASK
+        x = 1.0
+        if event.direction == gtk.gdk.SCROLL_UP:
+            x = 1.0 + (1-SHIFT)*0.2 + SHIFT*0.01
+        elif event.direction == gtk.gdk.SCROLL_DOWN:
+            x = 1.0 / (1.0 + (1-SHIFT)*0.2 + SHIFT*0.01)
+        self._do_zoom(x)
+
+    def settings(self, menuitem):
+        Settings(self)
+        
+    def scroll(self, window, event):
+        from copy import copy    
+        CTRL = event.state == gtk.gdk.CONTROL_MASK
+        SHIFT = event.state == gtk.gdk.SHIFT_MASK
+        dxdydz = {gtk.keysyms.KP_Add: ('zoom', 1.0 + (1-SHIFT)*0.2 + SHIFT*0.01, 0),
+                gtk.keysyms.KP_Subtract: ('zoom', 1 / (1.0 + (1-SHIFT)*0.2 + SHIFT*0.01), 0),
+                gtk.keysyms.Up:    ( 0, +1 - CTRL, +CTRL),
+                gtk.keysyms.Down:  ( 0, -1 + CTRL, -CTRL),
+                gtk.keysyms.Right: (+1,  0, 0),
+                gtk.keysyms.Left:  (-1,  0, 0)}.get(event.keyval, None)
+        try:
+            inch = chr(event.keyval)
+        except:
+            inch = None
+            
+        sel = []
+
+        atom_move = self.ui.get_widget('/MenuBar/ToolsMenu/MoveAtoms'
+                                    ).get_active()
+        atom_rotate = self.ui.get_widget('/MenuBar/ToolsMenu/RotateAtoms'
+                                      ).get_active()
+        atom_orient = self.ui.get_widget('/MenuBar/ToolsMenu/OrientAtoms'
+                                      ).get_active()
+        if dxdydz is None:
+            return
+        dx, dy, dz = dxdydz
+        if dx == 'zoom':
+            self._do_zoom(dy)
+            return
+
+        d = self.scale * 0.1
+        tvec = np.array([dx, dy, dz])
+
+        dir_vec = np.dot(self.axes, tvec)
+        if (atom_move):
+            rotmat = self.axes
+            s = 0.1
+            if SHIFT: 
+                s = 0.01
+            add = s * dir_vec
+            for i in range(len(self.R)):
+                if self.atoms_to_rotate_0[i]: 
+                    self.R[i] += add
+                    for jx in range(self.images.nimages):
+                        self.images.P[jx][i] += add
+        elif atom_rotate:
+            from rot_tools import rotate_about_vec, \
+                                  rotate_vec
+            sel = self.images.selected
+            if sum(sel) == 0:
+                sel = self.atoms_to_rotate_0
+            nsel = sum(sel)
+            # this is the first one to get instatiated
+            if nsel != 2: 
+                self.rot_vec = dir_vec
+                
+            change = False
+            z_axis = np.dot(self.axes, np.array([0, 0, 1]))
+            if self.atoms_to_rotate == None:
+                change = True 
+                self.z_axis_old = z_axis.copy()
+                self.dx_change = [0, 0]
+                self.atoms_to_rotate = self.atoms_to_rotate_0.copy()
+                self.atoms_selected = sel.copy()
+                self.rot_vec = dir_vec
+                    
+            if nsel != 2 or sum(self.atoms_to_rotate) == 2:
+                self.dx_change = [0, 0]
+                
+            for i in range(len(sel)):
+                if sel[i] != self.atoms_selected[i]: 
+                    change = True
+            cz = [dx, dy+dz]      
+            
+            if cz[0] or cz[1]: 
+                change = False
+            if not(cz[0] * (self.dx_change[1])):
+                change = True 
+            for i in range(2):
+                if cz[i] and self.dx_change[i]:
+                    self.rot_vec = self.rot_vec * cz[i] * self.dx_change[i]
+                    if cz[1]:
+                        change = False
+                
+            if np.prod(self.z_axis_old != z_axis):
+                change = True
+            self.z_axis_old = z_axis.copy()           
+            self.dx_change = copy(cz)
+            dihedral_rotation = len(self.images.selected_ordered) == 4
+
+            if change:
+                self.atoms_selected = sel.copy()
+
+                if nsel == 2 and sum(self.atoms_to_rotate) != 2:
+                    asel = []
+                    for i, j in enumerate(sel):
+                        if j: 
+                            asel.append(i)
+                    a1, a2 = asel
+
+                    rvx = self.images.P[self.frame][a1] - \
+                          self.images.P[self.frame][a2]
+                        
+                    rvy = np.cross(rvx,
+                                   np.dot(self.axes,
+                                   np.array([0, 0, 1])))     
+                    self.rot_vec = rvx * dx + rvy * (dy + dz)
+                    self.dx_change = [dx, dy+dz]
+                    
+                # dihedral rotation?
+                if dihedral_rotation:
+                    sel = self.images.selected_ordered
+                    self.rot_vec = (dx+dy+dz)*(self.R[sel[2]]-self.R[sel[1]])
+
+            rot_cen = np.array([0.0, 0.0, 0.0])
+            if dihedral_rotation:
+                sel = self.images.selected_ordered
+                rot_cen = self.R[sel[1]].copy()
+            elif nsel: 
+                for i, b in enumerate( sel):
+                    if b: 
+                        rot_cen += self.R[i]
+                rot_cen /= float(nsel)     
+
+            degrees = 5 * (1 - SHIFT) + SHIFT
+            degrees = abs(sum(dxdydz)) * 3.1415 / 360.0 * degrees
+            rotmat = rotate_about_vec(self.rot_vec, degrees)
+            
+            # now rotate the atoms that are to be rotated            
+            for i in range(len(self.R)):
+                if self.atoms_to_rotate[i]: 
+                    self.R[i] -= rot_cen
+                    for jx in range(self.images.nimages):
+                        self.images.P[jx][i] -= rot_cen
+                    
+                    self.R[i] = rotate_vec(rotmat, self.R[i])
+                    for jx in range(self.images.nimages):
+                        self.images.P[jx][i] = rotate_vec(rotmat, self.images.P[jx][i])
+                    
+                    self.R[i] += rot_cen
+                    for jx in range(self.images.nimages):
+                        self.images.P[jx][i] += rot_cen
+        elif atom_orient:
+            to_vec  = np.array([dx, dy, dz])
+
+            from rot_tools import rotate_vec_into_newvec
+            rot_mat = rotate_vec_into_newvec(self.orient_normal, to_vec)
+            self.axes = rot_mat
+            
+            self.set_coordinates()
+        else:
+            self.center -= (dx * 0.1 * self.axes[:, 0] -
+                            dy * 0.1 * self.axes[:, 1])
+        self.draw()
+        
+    def copy_atoms(self, widget):
+        "Copies selected atoms to a clipboard, if no atoms are selected then the clipboard is cleared."
+        if self.images.selected.any():
+            atoms = self.images.get_atoms(self.frame)
+            lena = len(atoms)
+            for i in range(len(atoms)):
+                li = lena-1-i
+                if not self.images.selected[li]:
+                    del(atoms[li])
+            for i in atoms:
+                i.position = np.dot(self.axes.T,i.position)       
+            ref = atoms[0].position
+            for i in atoms:
+                if i.position[2] < ref[2]:
+                    ref = i.position
+            self.clipboard = atoms.copy()
+            self.clipboard.reference_position = ref
+        else:
+            self.clipboard = None
+
+    def paste_atoms(self, widget):
+        "Inserts clipboard selection into the current frame using the add_atoms window."
+        if self.clipboard is not None:
+            self.add_atoms(widget, data='Paste', paste=self.clipboard)
+        
+    def add_atoms(self, widget, data=None, paste=None):
+        """
+        Presents a dialogbox to the user, that allows him to add atoms/molecule to the current slab
+        or to paste the clipboard.
+        
+        The molecule/atom is rotated using the current rotation of the coordinate system.
+        
+        The molecule/atom can be added at a specified position - if the keyword auto+Z is used,
+        the COM of the selected atoms will be used as COM for the moleculed. The COM is furthermore
+        translated Z ang towards the user.
+        
+        If no molecules are selected, the COM of all the atoms will be used for the x-y components of the
+        active coordinate system, while the z-direction will be chosen from the nearest atom position 
+        along this direction.
+        
+        Note: If this option is used, all frames except the active one are deleted.
+        """
+        
+        if data == 'load':
+            chooser = gtk.FileChooserDialog(
+                        _('Open ...'), None, gtk.FILE_CHOOSER_ACTION_OPEN,
+                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                         gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+
+            chooser.set_filename(_("<<filename>>"))
+            ok = chooser.run()
+            if ok == gtk.RESPONSE_OK:
+                filename = chooser.get_filename()
+                chooser.destroy()
+            else:
+                chooser.destroy()
+                return
+
+        if data == 'OK' or data == 'load':
+            import ase
+            if data == 'load':
+                molecule = filename
+            else:
+                molecule = self.add_entries[1].get_text()
+            tag = self.add_entries[2].get_text()
+            mom = self.add_entries[3].get_text()
+            pos = self.add_entries[4].get_text().lower()
+
+            if paste is not None: 
+                a = paste.copy()
+            else:
+                a = None
+                
+            if a is None:
+                try:
+                    a = ase.Atoms([ase.Atom(molecule)])
+                except:      
+                    try:
+                        a = ase.data.molecules.molecule(molecule)
+                    except:
+                        try:
+                            a = ase.io.read(molecule, -1)
+                        except:
+                            self.add_entries[1].set_text('?' + molecule) 
+                            return ()
+
+            directions = np.transpose(self.axes)
+            if a != None:
+                for i in a:
+                    try: 
+                        i.set('tag',int(tag))
+                    except:
+                        self.add_entries[2].set_text('?' + tag) 
+                        return ()
+                    try: 
+                        i.magmom = float(mom)
+                    except: 
+                        self.add_entries[3].set_text('?' + mom) 
+                        return ()
+                if self.origin_radio.get_active() and paste:
+                    a.translate(-paste.reference_position)
+                # apply the current rotation matrix to A
+                for i in a:
+                    i.position = np.dot(self.axes, i.position)       
+                # find the extent of the molecule in the local coordinate system
+                if self.centre_radio.get_active():
+                    a_cen_pos = np.array([0.0, 0.0, 0.0])
+                    m_cen_pos = 0.0
+                    for i in a.positions:
+                        a_cen_pos[0] += np.dot(directions[0], i)
+                        a_cen_pos[1] += np.dot(directions[1], i)
+                        a_cen_pos[2] += np.dot(directions[2], i)
+                        m_cen_pos = max(np.dot(-directions[2], i), m_cen_pos)
+                        
+                    a_cen_pos[0] /= len(a.positions)      
+                    a_cen_pos[1] /= len(a.positions)      
+                    a_cen_pos[2] /= len(a.positions)
+                    a_cen_pos[2] -= m_cen_pos
+                else:
+                    a_cen_pos = np.array([0.0, 0.0, 0.0])
+
+                # now find the position
+                cen_pos = np.array([0.0, 0.0, 0.0])
+                if sum(self.images.selected) > 0:
+                    for i in range(len(self.R)):
+                        if self.images.selected[i]:
+                            cen_pos += self.R[i]
+                    cen_pos /= sum(self.images.selected)   
+                elif len(self.R) > 0:
+                    px = 0.0
+                    py = 0.0
+                    pz = -1e6
+
+                    for i in range(len(self.R)):
+                        px += np.dot(directions[0], self.R[i])
+                        py += np.dot(directions[1], self.R[i])
+                        pz = max(np.dot(directions[2], self.R[i]), pz)
+                    px = (px/float(len(self.R)))
+                    py = (py/float(len(self.R)))
+                    cen_pos = directions[0] * px + \
+                              directions[1] * py + \
+                              directions[2] * pz
+                    
+                if 'auto' in pos:
+                    pos = pos.replace('auto', '')
+                    import re
+                    pos = re.sub('\s', '', pos)
+                    if '(' in pos:
+                        sign = eval('%s1' % pos[0])
+                        a_cen_pos -= sign * np.array(eval(pos[1:]), float)
+                    else:
+                        a_cen_pos -= float(pos) * directions[2]
+                else:
+                    cen_pos = np.array(eval(pos))
+                for i in a:
+                    i.position += cen_pos - a_cen_pos      
+
+              # and them to the molecule
+                atoms = self.images.get_atoms(self.frame)
+                atoms = atoms + a
+                self.new_atoms(atoms, init_magmom=True)
+
+              # and finally select the new molecule for easy moving and rotation
+                for i in range(len(a)):
+                    self.images.selected[len(atoms) - i - 1] = True
+
+                self.draw()    
+            self.add_entries[0].destroy()
+
+        if data == 'Cancel':
+            self.add_entries[0].destroy()
+            
+        if data == None or data == 'Paste':
+            from ase.gui.widgets import pack
+            molecule = ''
+            tag = '0'
+            mom = '0'
+            pos = 'auto+1'
+            self.add_entries = []
+            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+            self.add_entries.append(window)
+            window.set_title(_('Add atoms'))
+            if data == 'Paste':
+                molecule = paste.get_chemical_symbols(True)
+                window.set_title(_('Paste'))
+                
+            vbox = gtk.VBox(False, 0)
+            window.add(vbox)
+            vbox.show()
+            packed = False
+            for i, j in [[_('Insert atom or molecule'), molecule],
+                         [_('Tag'), tag],
+                         [_('Moment'), mom],
+                         [_('Position'), pos]]:
+
+                label = gtk.Label(i)
+                if not packed:
+                    vbox.pack_start(label, True, True, 0)
+                else: 
+                    packed = True
+                    vbox.add(label)    
+                label.show()
+  
+                entry = gtk.Entry()
+                entry.set_text(j)
+                self.add_entries.append(entry)
+                entry.set_max_length(50)
+                entry.show()
+                vbox.add(entry)
+
+            pack(vbox,[gtk.Label('atom/molecule reference:')])
+            self.centre_radio = gtk.RadioButton(None, "centre ")
+            self.origin_radio = gtk.RadioButton(self.centre_radio, "origin")
+            pack(vbox,[self.centre_radio, self.origin_radio])
+            if data == 'Paste':
+                self.origin_radio.set_active(True)
+                self.add_entries[1].set_sensitive(False)
+            if data == None:
+                button = gtk.Button(_('_Load molecule'))
+                button.connect('clicked', self.add_atoms, 'load')
+                button.show()
+                vbox.add(button)
+            button = gtk.Button(_('_OK'))
+            button.connect('clicked', self.add_atoms, 'OK', paste)
+            button.show()
+            vbox.add(button)
+            button = gtk.Button(_('_Cancel'))
+            button.connect('clicked', self.add_atoms, 'Cancel')
+            button.show()
+            vbox.add(button)
+            window.show()
+        
+    def modify_atoms(self, widget, data=None):
+        """
+        Presents a dialog box where the user is able to change the atomic type, the magnetic
+        moment and tags of the selected atoms. An item marked with X will not be changed.
+        """
+        if data:
+            if data == 'OK':
+                import ase
+                symbol = self.add_entries[1].get_text()
+                tag = self.add_entries[2].get_text()
+                mom = self.add_entries[3].get_text()
+                a = None
+                if symbol != 'X': 
+                    try:
+                        a = ase.Atoms([ase.Atom(symbol)])  
+                    except:
+                        self.add_entries[1].set_text('?' + symbol)
+                        return ()
+          
+                y = self.images.selected.copy()
+                    # and them to the molecule
+                atoms = self.images.get_atoms(self.frame)
+                for i in range(len(atoms)):
+                    if self.images.selected[i]:  
+                        if a: 
+                            atoms[i].symbol = symbol
+                        try: 
+                            if tag != 'X': 
+                                atoms[i].tag = int(tag)
+                        except:
+                            self.add_entries[2].set_text('?' + tag)
+                            return ()
+                        try: 
+                            if mom != 'X': 
+                                atoms[i].magmom = float(mom)
+                        except: 
+                            self.add_entries[3].set_text('?' + mom)
+                            return ()
+                self.new_atoms(atoms, init_magmom=True)
+
+                # and finally select the new molecule for easy moving and rotation
+                self.images.selected = y
+                self.draw()    
+          
+            self.add_entries[0].destroy()          
+        if data == None and sum(self.images.selected):
+            atoms = self.images.get_atoms(self.frame)
+            s_tag = ''
+            s_mom = ''  
+            s_symbol = ''
+          # Get the tags, moments and symbols of the selected atomsa  
+            for i in range(len(atoms)):
+                if self.images.selected[i]:
+                    if not(s_tag): 
+                        s_tag = str(atoms[i].tag)
+                    elif s_tag != str(atoms[i].tag): 
+                        s_tag = 'X'
+                    if not(s_mom): 
+                        s_mom = ("%2.2f" % (atoms[i].magmom))
+                    elif s_mom != ("%2.2f" % (atoms[i].magmom)): 
+                        s_mom = 'X'
+                    if not(s_symbol): 
+                        s_symbol = str(atoms[i].symbol)       
+                    elif s_symbol != str(atoms[i].symbol): 
+                        s_symbol = 'X'
+  
+            self.add_entries = []
+            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+            self.add_entries.append(window)
+            window.set_title(_('Modify'))
+
+            vbox = gtk.VBox(False, 0)
+            window.add(vbox)
+            vbox.show()
+            pack = False
+            for i, j in [[_('Atom'), s_symbol],
+                        [_('Tag'), s_tag],
+                        [_('Moment'), s_mom]]:
+                label = gtk.Label(i)
+                if not pack:
+                    vbox.pack_start(label, True, True, 0)
+                else: 
+                    pack = True
+                    vbox.add(label)    
+                label.show()
+
+                entry = gtk.Entry()
+                entry.set_text(j)
+                self.add_entries.append(entry)
+                entry.set_max_length(50)
+                entry.show()
+                vbox.add(entry)
+            button = gtk.Button(_('_OK'))
+            button.connect('clicked', self.modify_atoms, 'OK')
+            button.show()
+            vbox.add(button)
+            button = gtk.Button(_('_Cancel'))
+            button.connect('clicked', self.modify_atoms, 'Cancel')
+            button.show()
+            vbox.add(button)
+ 
+            window.show()
+        
+    def delete_selected_atoms(self, widget=None, data=None):
+        if data == 'OK':
+            atoms = self.images.get_atoms(self.frame)
+            lena = len(atoms)
+            for i in range(len(atoms)):
+                li = lena-1-i
+                if self.images.selected[li]:
+                    del(atoms[li])
+            self.new_atoms(atoms)
+         
+            self.draw()    
+        if data:      
+            self.delete_window.destroy()
+             
+        if not(data) and sum(self.images.selected):
+            nselected = sum(self.images.selected)
+            self.delete_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+            self.delete_window.set_title(_('Confirmation'))
+            self.delete_window.set_border_width(10)
+            self.box1 = gtk.HBox(False, 0)
+            self.delete_window.add(self.box1)
+            self.button1 = gtk.Button(ngettext('Delete selected atom?',
+                                               'Delete selected atoms?',
+                                               nselected))
+            self.button1.connect("clicked", self.delete_selected_atoms, "OK")
+            self.box1.pack_start(self.button1, True, True, 0)
+            self.button1.show()
+
+            self.button2 = gtk.Button(_("Cancel"))
+            self.button2.connect("clicked", self.delete_selected_atoms, "Cancel")
+            self.box1.pack_start(self.button2, True, True, 0)
+            self.button2.show()
+
+            self.box1.show()
+            self.delete_window.show()
+                
+    def debug(self, x):
+        from ase.gui.debug import Debug
+        Debug(self)
+
+    def execute(self, widget=None):
+        from ase.gui.execute import Execute
+        Execute(self)
+        
+    def constraints_window(self, widget=None):
+        from ase.gui.constraints import Constraints
+        Constraints(self)
+
+    def dft_window(self, widget=None):
+        from ase.gui.dft import DFT
+        DFT(self)
+
+    def select_all(self, widget):
+        self.images.selected[:] = True
+        self.draw()
+        
+    def invert_selection(self, widget):
+        self.images.selected[:] = ~self.images.selected
+        self.draw()
+        
+    def select_constrained_atoms(self, widget):
+        self.images.selected[:] = ~self.images.dynamic
+        self.draw()
+        
+    def select_immobile_atoms(self, widget):
+        if self.images.nimages > 1:
+            R0 = self.images.P[0]
+            for R in self.images.P[1:]:
+                self.images.selected[:] =~ (np.abs(R - R0) > 1.0e-10).any(1)
+        self.draw()
+
+    def movie(self, widget=None):
+        from ase.gui.movie import Movie
+        self.movie_window = Movie(self)
+        
+    def plot_graphs(self, x=None, expr=None):
+        from ase.gui.graphs import Graphs
+        g = Graphs(self)
+        if expr is not None:
+            g.plot(expr=expr)
+        self.graph_wref.append(weakref.ref(g))
+
+    def plot_graphs_newatoms(self):
+        "Notify any Graph objects that they should make new plots."
+        new_wref = []
+        found = 0
+        for wref in self.graph_wref:
+            ref = wref()
+            if ref is not None:
+                ref.plot()
+                new_wref.append(wref)  # Preserve weakrefs that still work.
+                found += 1
+        self.graph_wref = new_wref
+        return found
+        
+    def NEB(self, action):
+        from ase.gui.neb import NudgedElasticBand
+        NudgedElasticBand(self.images)
+        
+    def bulk_modulus(self, action):
+        from ase.gui.bulk_modulus import BulkModulus
+        BulkModulus(self.images)
+        
+    def open(self, button=None, filenames=None):
+        if filenames == None:
+            chooser = gtk.FileChooserDialog(
+                _('Open ...'), None, gtk.FILE_CHOOSER_ACTION_OPEN,
+                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                 gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+            chooser.set_filename(_("<<filename>>"))
+
+            # Add a file type filter
+            name_to_suffix = {}
+            types = gtk.combo_box_new_text()
+            for name, suffix in [(_('Automatic'), None),
+                                 (_('Dacapo netCDF output file'),'dacapo'),
+                                 (_('Virtual Nano Lab file'),'vnl'),
+                                 (_('ASE pickle trajectory'),'traj'),
+                                 (_('ASE bundle trajectory'),'bundle'),
+                                 (_('GPAW text output'),'gpaw-text'),
+                                 (_('CUBE file'),'cube'),
+                                 (_('XCrySDen Structure File'),'xsf'),
+                                 (_('Dacapo text output'),'dacapo-text'),
+                                 (_('XYZ-file'),'xyz'),
+                                 (_('VASP POSCAR/CONTCAR file'),'vasp'),
+                                 (_('VASP OUTCAR file'),'vasp_out'),
+                                 (_('Protein Data Bank'),'pdb'),
+                                 (_('CIF-file'),'cif'),
+                                 (_('FHI-aims geometry file'),'aims'),
+                                 (_('FHI-aims output file'),'aims_out'),
+                                 (_('TURBOMOLE coord file'),'tmol'),
+                                 (_('exciting input'),'exi'),
+                                 (_('WIEN2k structure file'),'struct'),
+                                 (_('DftbPlus input file'),'dftb'),
+                                 (_('ETSF format'),'etsf.nc'),
+                                 (_('CASTEP geom file'),'cell'),
+                                 (_('CASTEP output file'),'castep'),
+                                 (_('CASTEP trajectory file'),'geom'),
+                                 (_('DFTBPlus GEN format'),'gen')
+                                ]:
+                types.append_text(name)
+                name_to_suffix[name] = suffix
+            types.set_active(0)
+            img_vbox = gtk.VBox()
+            pack(img_vbox, [gtk.Label(_('File type:')), types])
+            img_vbox.show()
+            chooser.set_extra_widget(img_vbox)
+
+            ok = chooser.run() == gtk.RESPONSE_OK
+            if ok:
+                filenames = [chooser.get_filename()]
+                filetype = types.get_active_text()
+            chooser.destroy()
+
+            if not ok:
+                return
+
+        n_current = self.images.nimages
+        self.reset_tools_modes()     
+        self.images.read(filenames, slice(None), name_to_suffix[filetype])
+        self.set_colors()
+        self.set_coordinates(self.images.nimages - 1, focus=True)
+
+    def import_atoms (self, button=None, filenames=None):
+        if filenames == None:
+            chooser = gtk.FileChooserDialog(
+                _('Open ...'), None, gtk.FILE_CHOOSER_ACTION_OPEN,
+                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                 gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+            ok = chooser.run()
+            if ok == gtk.RESPONSE_OK:
+                filenames = [chooser.get_filename()]
+            chooser.destroy()
+
+            if not ok:
+                return
+            
+            
+        self.images.import_atoms(filenames, self.frame)
+        self.set_colors()
+        self.set_coordinates(self.images.nimages - 1, focus=True)
+
+    def save(self, action):
+        chooser = gtk.FileChooserDialog(
+            _('Save ...'), None, gtk.FILE_CHOOSER_ACTION_SAVE,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+             gtk.STOCK_SAVE, gtk.RESPONSE_OK))
+        try:
+            fname = sys.argv[1]
+        except IndexError:
+            fname = "<<filename>>"
+        chooser.set_filename(fname)
+
+        # Add a file type filter
+        types = []
+        name_to_suffix = {}
+        for name, suffix in [(_('Automatic'), None),
+                             (_('XYZ file'), 'xyz'),
+                             (_('ASE trajectory'), 'traj'),
+                             (_('PDB file'), 'pdb'),
+                             (_('Gaussian cube file'), 'cube'),
+                             (_('Python script'), 'py'),
+                             (_('VNL file'), 'vnl'),
+                             (_('Portable Network Graphics'), 'png'),
+                             (_('Persistence of Vision'), 'pov'),
+                             (_('Encapsulated PostScript'), 'eps'),
+                             (_('FHI-aims geometry input'), 'in'),
+                             (_('CASTEP geom file'),'cell'),
+                             (_('VASP geometry input'), 'POSCAR'),
+                             (_('ASE bundle trajectory'), 'bundle'),
+                             (_('cif file'), 'cif'),
+                             ]:
+            if suffix is None:
+                name = _(name)
+            else:
+                name = '%s (%s)' % (_(name), suffix)
+            filt = gtk.FileFilter()
+            filt.set_name(name)
+            if suffix is None:
+                filt.add_pattern('*')
+            elif suffix == 'POSCAR':
+                filt.add_pattern('*POSCAR*')
+            else:
+                filt.add_pattern('*.'+suffix)
+            if suffix is not None:
+                types.append(suffix)
+                name_to_suffix[name] = suffix
+                
+            chooser.add_filter(filt)
+
+        if self.images.nimages > 1:
+            img_vbox = gtk.VBox()
+            button = gtk.CheckButton(_('Save current image only (#%d)') %
+                                     self.frame)
+            pack(img_vbox, button)
+            entry = gtk.Entry(10)
+            pack(img_vbox, [gtk.Label(_('Slice: ')), entry,
+                                        help(_('Help for slice ...'))])
+            entry.set_text('0:%d' % self.images.nimages)
+            img_vbox.show()
+            chooser.set_extra_widget(img_vbox)
+
+        while True:
+            # Loop until the user selects a proper file name, or cancels.
+            response = chooser.run()
+            if response == gtk.RESPONSE_CANCEL or response == gtk.RESPONSE_DELETE_EVENT:
+                chooser.destroy()
+                return
+            elif response != gtk.RESPONSE_OK:
+                print >>sys.stderr, _("AG INTERNAL ERROR: strange response in Save,"), response
+                chooser.destroy()
+                return
+                
+            filename = chooser.get_filename()
+
+            suffix = os.path.splitext(filename)[1][1:]
+            if 'POSCAR' in filename or 'CONTCAR' in filename:
+                suffix = 'POSCAR'
+            if suffix == '':
+                # No suffix given.  If the user has chosen a special
+                # file type, use the corresponding suffix.
+                filt = chooser.get_filter().get_name()
+                suffix = name_to_suffix[filt]
+                filename = filename + '.' + suffix
+                
+            # XXX FIXME the window saying unknown output format
+            # cannot be closed
+            if suffix not in types:
+                oops(message=_('Unknown output format!'),
+                     message2=_('Use one of: %s') % ', '.join(types[1:]))
+                continue
+                
+            if self.images.nimages > 1:
+                if button.get_active():
+                    filename += '@%d' % self.frame
+                else:
+                    filename += '@' + entry.get_text()
+
+            break
+        
+        chooser.destroy()
+
+        bbox = np.empty(4)
+        size = np.array([self.width, self.height]) / self.scale
+        bbox[0:2] = np.dot(self.center, self.axes[:, :2]) - size / 2
+        bbox[2:] = bbox[:2] + size
+        suc = self.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell').get_active()
+        extra_args = {}
+        if suffix in ['eps', 'png', 'pov', 'png']:
+            extra_args['colors'] = self.get_colors(rgb=True)
+        self.images.write(filename, self.axes, show_unit_cell=suc, bbox=bbox, **extra_args)
+        
+    def quick_info_window(self, menuitem):
+        QuickInfo(self)
+
+    def bulk_window(self, menuitem):
+        SetupBulkCrystal(self)
+
+    def surface_window(self, menuitem):
+        SetupSurfaceSlab(self)
+
+    def nanoparticle_window(self, menuitem):
+        SetupNanoparticle(self)
+        
+    def graphene_window(self, menuitem):
+        SetupGraphene(self)
+
+    def nanotube_window(self, menuitem):
+        SetupNanotube(self)
+
+    def calculator_window(self, menuitem):
+        SetCalculator(self)
+        
+    def energy_window(self, menuitem):
+        EnergyForces(self)
+        
+    def energy_minimize_window(self, menuitem):
+        Minimize(self)
+
+    def scaling_window(self, menuitem):
+        HomogeneousDeformation(self)
+        
+    def new_atoms(self, atoms, init_magmom=False):
+        "Set a new atoms object."
+        self.reset_tools_modes()
+        
+        rpt = getattr(self.images, 'repeat', None)
+        self.images.repeat_images(np.ones(3, int))
+        self.images.initialize([atoms], init_magmom=init_magmom)
+        self.frame = 0   # Prevent crashes
+        self.images.repeat_images(rpt)
+        self.set_colors()
+        self.set_coordinates(frame=0, focus=True)
+        self.notify_vulnerable()
+
+    def prepare_new_atoms(self):
+        "Marks that the next call to append_atoms should clear the images."
+        self.images.prepare_new_atoms()
+        
+    def append_atoms(self, atoms):
+        "Set a new atoms object."
+        #self.notify_vulnerable()   # Do this manually after last frame.
+        frame = self.images.append_atoms(atoms)
+        self.set_coordinates(frame=frame-1, focus=True)
+
+    def notify_vulnerable(self):
+        """Notify windows that would break when new_atoms is called.
+
+        The notified windows may adapt to the new atoms.  If that is not
+        possible, they should delete themselves.
+        """
+        new_vul = []  # Keep weakrefs to objects that still exist.
+        for wref in self.vulnerable_windows:
+            ref = wref()
+            if ref is not None:
+                new_vul.append(wref)
+                ref.notify_atoms_changed()
+        self.vulnerable_windows = new_vul
+
+    def register_vulnerable(self, obj):
+        """Register windows that are vulnerable to changing the images.
+
+        Some windows will break if the atoms (and in particular the
+        number of images) are changed.  They can register themselves
+        and be closed when that happens.
+        """
+        self.vulnerable_windows.append(weakref.ref(obj))
+
+    def exit(self, button, event=None):
+        self.window.destroy()
+        gtk.main_quit()
+        return True
+
+    def xxx(self, x=None,
+            message1=_('Not implemented!'),
+            message2=_('do you really need it?')):
+        oops(message1, message2)
+        
+    def about(self, action):
+        try:
+            dialog = gtk.AboutDialog()
+        except AttributeError:
+            self.xxx()
+        else:
+            dialog.run()
+
+def webpage(widget):
+    import webbrowser
+    webbrowser.open('https://wiki.fysik.dtu.dk/ase/ase/gui.html')
+
diff --git a/ase/gui/i18n.py b/ase/gui/i18n.py
new file mode 100644
index 0000000..ed1ecfe
--- /dev/null
+++ b/ase/gui/i18n.py
@@ -0,0 +1,15 @@
+import os
+import locale
+import gettext
+
+# This module enables localization using gettext.  If this module has
+# been imported, translations will be loaded from mo-files when
+# possible.
+
+domain = 'ag'
+localedir = '%s/po/' % os.path.dirname(__file__)
+
+gettext.bindtextdomain(domain, localedir)
+gettext.textdomain(domain)
+translation = gettext.translation(domain, localedir, fallback=True)
+translation.install(unicode=True)
diff --git a/ase/gui/images.py b/ase/gui/images.py
new file mode 100644
index 0000000..d70be23
--- /dev/null
+++ b/ase/gui/images.py
@@ -0,0 +1,421 @@
+from math import sqrt
+
+import numpy as np
+
+from ase.data import covalent_radii
+from ase.atoms import Atoms
+from ase.calculators.singlepoint import SinglePointCalculator
+from ase.io import read, write, string2index
+from ase.constraints import FixAtoms
+from ase.gui.defaults import read_defaults
+from ase.quaternions import Quaternion
+
+class Images:
+    def __init__(self, images=None):
+
+        if images is not None:
+            self.initialize(images)
+    
+    def initialize(self, images, filenames=None, init_magmom=False):
+        
+        self.natoms = len(images[0])
+        self.nimages = len(images)
+        if hasattr(images[0], 'get_shapes'):
+            self.shapes = images[0].get_shapes()
+            self.Q = []
+        else:
+            self.shapes = None
+
+        if filenames is None:
+            filenames = [None] * self.nimages
+        self.filenames = filenames
+        self.P = np.empty((self.nimages, self.natoms, 3))
+        self.V = np.empty((self.nimages, self.natoms, 3))
+        self.E = np.empty(self.nimages)
+        self.K = np.empty(self.nimages)
+        self.F = np.empty((self.nimages, self.natoms, 3))
+        self.M = np.empty((self.nimages, self.natoms))
+        self.T = np.empty((self.nimages, self.natoms), int)
+        self.A = np.empty((self.nimages, 3, 3))
+        self.Z = images[0].get_atomic_numbers()
+        self.pbc = images[0].get_pbc()
+        self.covalent_radii = covalent_radii
+        config = read_defaults()
+        if config['covalent_radii'] is not None:
+            for data in config['covalent_radii']:
+                self.covalent_radii[data[0]] = data[1]
+        warning = False
+        for i, atoms in enumerate(images):
+            natomsi = len(atoms)
+            if (natomsi != self.natoms or
+                (atoms.get_atomic_numbers() != self.Z).any()):
+                raise RuntimeError('Can not handle different images with ' +
+                                   'different numbers of atoms or different ' +
+                                   'kinds of atoms!')
+            self.P[i] = atoms.get_positions()
+            self.V[i] = atoms.get_velocities()
+
+            if hasattr(self, 'Q'):
+                for q in atoms.get_quaternions():
+                     self.Q.append(Quaternion(q))
+
+            self.A[i] = atoms.get_cell()
+            if (atoms.get_pbc() != self.pbc).any():
+                warning = True
+            try:
+                self.E[i] = atoms.get_potential_energy()
+            except RuntimeError:
+                self.E[i] = np.nan
+            self.K[i] = atoms.get_kinetic_energy()
+            try:
+                self.F[i] = atoms.get_forces(apply_constraint=False)
+            except RuntimeError:
+                self.F[i] = np.nan
+            try:
+                if init_magmom:
+                    self.M[i] = atoms.get_initial_magnetic_moments()
+                else:
+                    self.M[i] = atoms.get_magnetic_moments()
+            except (RuntimeError, AttributeError):
+                self.M[i] = atoms.get_initial_magnetic_moments()
+                
+            # added support for tags
+            try:
+                self.T[i] = atoms.get_tags()
+            except RuntimeError:
+                self.T[i] = 0
+                
+
+        if warning:
+            print('WARNING: Not all images have the same bondary conditions!')
+            
+        self.selected = np.zeros(self.natoms, bool)
+        self.selected_ordered  = []
+        self.atoms_to_rotate_0 = np.zeros(self.natoms, bool)
+        self.visible = np.ones(self.natoms, bool)
+        self.nselected = 0
+        self.set_dynamic(constraints = images[0].constraints)
+        self.repeat = np.ones(3, int)
+        self.set_radii(0.89)
+        
+    def prepare_new_atoms(self):
+        "Marks that the next call to append_atoms should clear the images."
+        self.next_append_clears = True
+        
+    def append_atoms(self, atoms, filename=None):
+        "Append an atoms object to the images already stored."
+        assert len(atoms) == self.natoms
+        if self.next_append_clears:
+            i = 0
+        else:
+            i = self.nimages
+        for name in ('P', 'V', 'E', 'K', 'F', 'M', 'A', 'T'):
+            a = getattr(self, name)
+            newa = np.empty( (i+1,) + a.shape[1:], a.dtype )
+            if not self.next_append_clears:
+                newa[:-1] = a
+            setattr(self, name, newa)
+        self.next_append_clears = False
+        self.P[i] = atoms.get_positions()
+        self.V[i] = atoms.get_velocities()
+        self.A[i] = atoms.get_cell()
+        try:
+            self.E[i] = atoms.get_potential_energy()
+        except RuntimeError:
+            self.E[i] = np.nan
+        self.K[i] = atoms.get_kinetic_energy()
+        try:
+            self.F[i] = atoms.get_forces(apply_constraint=False)
+        except RuntimeError:
+            self.F[i] = np.nan
+        try:
+            self.M[i] = atoms.get_magnetic_moments()
+        except (RuntimeError, AttributeError):
+            self.M[i] = np.nan
+        try:
+            self.T[i] = atoms.get_tags()
+        except AttributeError:
+            if i == 0:
+                self.T[i] = 0
+            else:
+                self.T[i] = self.T[i-1]
+        self.nimages = i + 1
+        self.filenames.append(filename)
+        self.set_dynamic()
+        return self.nimages
+        
+    def set_radii(self, scale):
+        if self.shapes == None:
+            self.r = self.covalent_radii[self.Z] * scale
+        else:
+            self.r = np.sqrt(np.sum(self.shapes**2, axis=1)) * scale
+                
+    def read(self, filenames, index=-1, filetype=None):
+        images = []
+        names = []
+        for filename in filenames:
+            i = read(filename, index,filetype)
+            
+            if not isinstance(i, list):
+                i = [i]
+            images.extend(i)
+            names.extend([filename] * len(i))
+            
+        self.initialize(images, names)
+    
+    def import_atoms(self, filename, cur_frame):
+        if filename:
+            filename = filename[0]
+            old_a = self.get_atoms(cur_frame)
+            imp_a = read(filename, -1)
+            new_a = old_a + imp_a
+            self.initialize([new_a], [filename])
+    
+    def repeat_images(self, repeat):
+        n = self.repeat.prod()
+        repeat = np.array(repeat)
+        self.repeat = repeat
+        N = repeat.prod()
+        natoms = self.natoms // n
+        P = np.empty((self.nimages, natoms * N, 3))
+        V = np.empty((self.nimages, natoms * N, 3))
+        M = np.empty((self.nimages, natoms * N))
+        T = np.empty((self.nimages, natoms * N), int)
+        F = np.empty((self.nimages, natoms * N, 3))
+        Z = np.empty(natoms * N, int)
+        r = np.empty(natoms * N)
+        dynamic = np.empty(natoms * N, bool)
+        a0 = 0
+        for i0 in range(repeat[0]):
+            for i1 in range(repeat[1]):
+                for i2 in range(repeat[2]):
+                    a1 = a0 + natoms
+                    for i in range(self.nimages):
+                        P[i, a0:a1] = (self.P[i, :natoms] +
+                                       np.dot((i0, i1, i2), self.A[i]))
+                    V[:, a0:a1] = self.V[:, :natoms]
+                    F[:, a0:a1] = self.F[:, :natoms]
+                    M[:, a0:a1] = self.M[:, :natoms]
+                    T[:, a0:a1] = self.T[:, :natoms]
+                    Z[a0:a1] = self.Z[:natoms]
+                    r[a0:a1] = self.r[:natoms]
+                    dynamic[a0:a1] = self.dynamic[:natoms]
+                    a0 = a1
+        self.P = P
+        self.V = V
+        self.F = F
+        self.Z = Z
+        self.T = T
+        self.M = M
+        self.r = r
+        self.dynamic = dynamic
+        self.natoms = natoms * N
+        self.selected = np.zeros(natoms * N, bool)
+        self.atoms_to_rotate_0 = np.zeros(self.natoms, bool)
+        self.visible = np.ones(natoms * N, bool)
+        self.nselected = 0
+
+    def center(self):
+        """ center each image in the existing unit cell, keeping the cell constant. """
+        c = self.A.sum(axis=1) / 2.0 - self.P.mean(axis=1)
+        self.P += c[:, np.newaxis, :]
+            
+    def graph(self, expr):
+        """ routine to create the data in ag graphs, defined by the string expr.  """
+        import ase.units as units
+        code = compile(expr + ',', 'atoms.py', 'eval')
+
+        n = self.nimages
+        def d(n1, n2):
+            return sqrt(((R[n1] - R[n2])**2).sum())
+        def a(n1, n2, n3):
+            v1 = R[n1]-R[n2]
+            v2 = R[n3]-R[n2]
+            arg = np.vdot(v1,v2)/(sqrt((v1**2).sum()*(v2**2).sum()))
+            if arg > 1.0: arg = 1.0
+            if arg < -1.0: arg = -1.0
+            return 180.0*np.arccos(arg)/np.pi
+        def dih(n1, n2, n3, n4):
+            # vector 0->1, 1->2, 2->3 and their normalized cross products:
+            a    = R[n2]-R[n1]
+            b    = R[n3]-R[n2]
+            c    = R[n4]-R[n3]
+            bxa  = np.cross(b,a)
+            bxa /= np.sqrt(np.vdot(bxa,bxa))
+            cxb  = np.cross(c,b)
+            cxb /= np.sqrt(np.vdot(cxb,cxb))
+            angle = np.vdot(bxa,cxb)
+            # check for numerical trouble due to finite precision:
+            if angle < -1: angle = -1
+            if angle >  1: angle =  1
+            angle = np.arccos(angle)
+            if (np.vdot(bxa,c)) > 0: angle = 2*np.pi-angle
+            return angle*180.0/np.pi
+        # get number of mobile atoms for temperature calculation
+        ndynamic = 0
+        for dyn in self.dynamic: 
+            if dyn: ndynamic += 1
+        S = self.selected
+        D = self.dynamic[:, np.newaxis]
+        E = self.E
+        s = 0.0
+        data = []
+        for i in range(n):
+            R = self.P[i]
+            V = self.V[i]
+            F = self.F[i]
+            A = self.A[i]
+            M = self.M[i]
+            f = ((F * D)**2).sum(1)**.5
+            fmax = max(f)
+            fave = f.mean()
+            epot = E[i]
+            ekin = self.K[i]
+            e = epot + ekin
+            T = 2.0 * ekin / (3.0 * ndynamic * units.kB)
+            data = eval(code)
+            if i == 0:
+                m = len(data)
+                xy = np.empty((m, n))
+            xy[:, i] = data
+            if i + 1 < n:
+                s += sqrt(((self.P[i + 1] - R)**2).sum())
+        return xy
+
+    def set_dynamic(self, constraints = None):
+        self.dynamic = np.ones(self.natoms, bool)
+        if constraints is not None:
+            for con in constraints: 
+                if isinstance(con,FixAtoms):
+                    self.dynamic[con.index] = False
+
+    def write(self, filename, rotations='', show_unit_cell=False, bbox=None, **kwargs):
+        indices = range(self.nimages)
+        p = filename.rfind('@')
+        if p != -1:
+            try:
+                slice = string2index(filename[p + 1:])
+            except ValueError:
+                pass
+            else:
+                indices = indices[slice]
+                filename = filename[:p]
+                if isinstance(indices, int):
+                    indices = [indices]
+
+        images = [self.get_atoms(i) for i in indices]
+        if len(filename) > 4 and filename[-4:] in ['.eps', '.png', '.pov']:
+            write(filename, images, 
+                  rotation=rotations, show_unit_cell=show_unit_cell,
+                  bbox=bbox, **kwargs)
+        else:
+            write(filename, images, **kwargs)
+
+    def get_atoms(self, frame):
+        atoms = Atoms(positions=self.P[frame],
+                      numbers=self.Z,
+                      magmoms=self.M[0],
+                      tags=self.T[frame],
+                      cell=self.A[frame],
+                      pbc=self.pbc)
+
+        if not np.isnan(self.V).any():
+            atoms.set_velocities(self.V[frame])
+        
+        # check for constrained atoms and add them accordingly:
+        if not self.dynamic.all():
+            atoms.set_constraint(FixAtoms(mask=1-self.dynamic))
+        
+        atoms.set_calculator(SinglePointCalculator(self.E[frame],
+                                                   self.F[frame],
+                                                   None, None, atoms))
+        return atoms
+                           
+    def delete(self, i):
+        self.nimages -= 1
+        P = np.empty((self.nimages, self.natoms, 3))
+        V = np.empty((self.nimages, self.natoms, 3))
+        F = np.empty((self.nimages, self.natoms, 3))
+        A = np.empty((self.nimages, 3, 3))
+        E = np.empty(self.nimages)
+        P[:i] = self.P[:i]
+        P[i:] = self.P[i + 1:]
+        self.P = P
+        V[:i] = self.V[:i]
+        V[i:] = self.V[i + 1:]
+        self.V = V
+        F[:i] = self.F[:i]
+        F[i:] = self.F[i + 1:]
+        self.F = F
+        A[:i] = self.A[:i]
+        A[i:] = self.A[i + 1:]
+        self.A = A
+        E[:i] = self.E[:i]
+        E[i:] = self.E[i + 1:]
+        self.E = E
+        del self.filenames[i]
+
+    def aneb(self):
+        n = self.nimages
+        assert n % 5 == 0
+        levels = n // 5
+        n = self.nimages = 2 * levels + 3
+        P = np.empty((self.nimages, self.natoms, 3))
+        V = np.empty((self.nimages, self.natoms, 3))
+        F = np.empty((self.nimages, self.natoms, 3))
+        E = np.empty(self.nimages)
+        for L in range(levels):
+            P[L] = self.P[L * 5]
+            P[n - L - 1] = self.P[L * 5 + 4]
+            V[L] = self.V[L * 5]
+            V[n - L - 1] = self.V[L * 5 + 4]
+            F[L] = self.F[L * 5]
+            F[n - L - 1] = self.F[L * 5 + 4]
+            E[L] = self.E[L * 5]
+            E[n - L - 1] = self.E[L * 5 + 4]
+        for i in range(3):
+            P[levels + i] = self.P[levels * 5 - 4 + i]
+            V[levels + i] = self.V[levels * 5 - 4 + i]
+            F[levels + i] = self.F[levels * 5 - 4 + i]
+            E[levels + i] = self.E[levels * 5 - 4 + i]
+        self.P = P
+        self.V = V
+        self.F = F
+        self.E = E
+
+    def interpolate(self, m):
+        assert self.nimages == 2
+        self.nimages = 2 + m
+        P = np.empty((self.nimages, self.natoms, 3))
+        V = np.empty((self.nimages, self.natoms, 3))
+        F = np.empty((self.nimages, self.natoms, 3))
+        A = np.empty((self.nimages, 3, 3))
+        E = np.empty(self.nimages)
+        P[0] = self.P[0]
+        V[0] = self.V[0]
+        F[0] = self.F[0]
+        A[0] = self.A[0]
+        E[0] = self.E[0]
+        for i in range(1, m + 1):
+            x = i / (m + 1.0)
+            y = 1 - x
+            P[i] = y * self.P[0] + x * self.P[1]
+            V[i] = y * self.V[0] + x * self.V[1]
+            F[i] = y * self.F[0] + x * self.F[1]
+            A[i] = y * self.A[0] + x * self.A[1]
+            E[i] = y * self.E[0] + x * self.E[1]
+        P[-1] = self.P[1]
+        V[-1] = self.V[1]
+        F[-1] = self.F[1]
+        A[-1] = self.A[1]
+        E[-1] = self.E[1]
+        self.P = P
+        self.V = V
+        self.F = F
+        self.A = A
+        self.E = E
+        self.filenames[1:1] = [None] * m
+
+if __name__ == '__main__':
+    import os
+    os.system('python gui.py')
diff --git a/ase/gui/languages/__init__.py b/ase/gui/languages/__init__.py
new file mode 100644
index 0000000..db1978c
--- /dev/null
+++ b/ase/gui/languages/__init__.py
@@ -0,0 +1,27 @@
+import locale
+
+from ase.gui.languages.en import translation as default_translation
+
+
+def translate(text):
+    return default_translation.get(text, text)
+
+language_code = locale.getdefaultlocale()[0]
+if language_code is None:
+    language_code = 'en'
+else:
+    language_code = language_code[:2]
+
+if language_code != 'en':
+    try:
+        module = __import__(language_code, globals(), locals())
+    except ImportError:
+        pass
+    else:
+        translation = module.translation
+        def translate(text):
+            translated_text = translation.get(text)
+            if translated_text is None:
+                return default_translation.get(text, text)
+            else:
+                return translated_text
diff --git a/ase/gui/languages/da.py b/ase/gui/languages/da.py
new file mode 100644
index 0000000..6199acb
--- /dev/null
+++ b/ase/gui/languages/da.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""Danish translation."""
+
+translation = {
+'Select': u'Vælg',
+'Invert selection': 'Inverter valg',
+'Select all': u'Vælg alle',
+'View': 'Vis',
+'Focus': u'Fokusér',
+'Settings': 'Indstillinger',
+'Plugins': 'Indstik',
+'Help': u'Hjælp',
+'Edit': u'Redigér',
+'File': 'Fil',
+'Exit': 'Afslut',
+'Open': u'Åben',
+'Play': 'Afspil',
+'Adjust play time.': 'Indstil afspilningstid',
+'Image number:': 'Billede nummer:',
+
+'Tip for status box ...':
+"""Vis informationer om valgte atomer (type, position, bindingslængder
+og vinkler) ved at vælge atomer med musen (venstre knap) evt. i
+kombination med [ctrl] tasten""",
+
+'Help for plot ...':
+u"""Symboler:
+<c>e</c>:\t\ttotal energi
+<c>epot</c>:\tpotentiel energi
+<c>ekin</c>:\tkinetisk energi
+<c>fmax</c>:\tmaximum kræft
+<c>fave</c>:\tmiddel kræft
+<c>R[n,0-2]</c>:\tposition af atom nummer <c>n</c> 
+<c>d(n<sub>1</sub>,n<sub>1</sub>)</c>:\tafstand mellem to atomer
+<c>i</c>:\t\tnuværende billede nummer
+<c>E[i]</c>:\t\tenergi af billede nummer <c>i</c>
+<c>F[n,0-2]</c>:\tkræft på atom nummer <c>n</c> 
+<c>V[n,0-2]</c>:\thastighed af atom nummer <c>n</c> 
+<c>A[0-2,0-2]</c>:\tEnhedscelle vektorer
+<c>s</c>:\t\tvejlængde""",
+
+'Slice: ': 'Udsnit: '
+}
diff --git a/ase/gui/languages/en.py b/ase/gui/languages/en.py
new file mode 100644
index 0000000..309540a
--- /dev/null
+++ b/ase/gui/languages/en.py
@@ -0,0 +1,36 @@
+translation = {
+'Tip for status box ...':
+"""Show information about selected atoms (type, position, bondlengths and angles).  Use left mouse button to select atoms - possibly combined with the [ctrl] key.""",
+
+'Help for plot ...':
+"""Symbols:
+<c>e</c>:\t\t\ttotal energy
+<c>epot</c>:\t\tpotential energy
+<c>ekin</c>:\t\tkinetic energy
+<c>fmax</c>:\t\tmaximum force
+<c>fave</c>:\t\taverage force
+<c>R[n,0-2]</c>:\t\tposition of atom number <c>n</c> 
+<c>d(n<sub>1</sub>,n<sub>2</sub>)</c>:\t\tdistance between two atoms n1 and n2
+<c>i</c>:\t\t\tcurrent image number
+<c>E[i]</c>:\t\t\tenergy of image number <c>i</c>
+<c>F[n,0-2]</c>:\t\tforce on atom number <c>n</c>
+<c>V[n,0-2]</c>:\t\tvelocity of atom number <c>n</c>
+<c>M[n]</c>:\t\tmagnetic moment of atom number <c>n</c> 
+<c>A[0-2,0-2]</c>:\tunit-cell basis vectors 
+<c>s</c>:\t\t\tpath length
+<c>a(n1,n2,n3)</c>:\tangle between atoms n1, n2 and n3, centered on n2
+<c>dih(n1,n2,n3,n4)</c>:\tdihedral angle between n1, n2, n3, and n4
+<c>T</c>:\t\t\tTemperature (K)""",
+
+'Help for slice ...':
+"""Use Python slice syntax: "start:stop:step" or "start:stop":
+
+<c>2:5</c>\tsecond, third and fourth
+<c>:3</c>\tfirst three
+<c>-3:</c>\tlast three
+<c>::3</c>\tevery third
+<c>0</c>\tfirst
+<c>6</c>\tseventh
+<c>-1</c>\tlast
+"""
+}
diff --git a/ase/gui/minimize.py b/ase/gui/minimize.py
new file mode 100644
index 0000000..336da53
--- /dev/null
+++ b/ase/gui/minimize.py
@@ -0,0 +1,142 @@
+# encoding: utf-8
+
+"Module for performing energy minimization."
+
+import gtk
+from gettext import gettext as _
+from ase.gui.simulation import Simulation
+from ase.gui.widgets import oops, pack, AseGuiCancelException
+import ase
+import numpy as np
+
+class MinimizeMixin:
+    minimizers = ('BFGS', 'BFGSLineSearch', 'LBFGS', 'LBFGSLineSearch', 'MDMin', 'FIRE')
+    def make_minimize_gui(self, box):
+        self.algo = gtk.combo_box_new_text()
+        for m in self.minimizers:
+            self.algo.append_text(m)
+        self.algo.set_active(0)
+        self.algo.connect('changed', self.min_algo_specific)
+        pack(box, [gtk.Label(_("Algorithm: ")), self.algo])
+        
+        self.fmax = gtk.Adjustment(0.05, 0.00, 10.0, 0.01)
+        self.fmax_spin = gtk.SpinButton(self.fmax, 0, 3)
+        lbl = gtk.Label()
+        lbl.set_markup(_("Convergence criterion: F<sub>max</sub> = "))
+        pack(box, [lbl, self.fmax_spin])
+
+        self.steps = gtk.Adjustment(100, 1, 1000000, 1)
+        self.steps_spin = gtk.SpinButton(self.steps, 0, 0)
+        pack(box, [gtk.Label(_("Max. number of steps: ")), self.steps_spin])
+
+        # Special stuff for MDMin
+        lbl = gtk.Label(_("Pseudo time step: "))
+        self.mdmin_dt = gtk.Adjustment(0.05, 0.0, 10.0, 0.01)
+        spin = gtk.SpinButton(self.mdmin_dt, 0, 3)
+        self.mdmin_widgets = [lbl, spin]
+        pack(box, self.mdmin_widgets)
+        self.min_algo_specific()
+        
+    def min_algo_specific(self, *args):
+        "SHow or hide algorithm-specific widgets."
+        minimizer = self.minimizers[self.algo.get_active()]
+        for w in self.mdmin_widgets:
+            if minimizer == 'MDMin':
+                w.show()
+            else:
+                w.hide()
+        
+class Minimize(Simulation, MinimizeMixin):
+    "Window for performing energy minimization."
+    
+    def __init__(self, gui):
+        Simulation.__init__(self, gui)
+        self.set_title(_("Energy minimization"))
+        
+        vbox = gtk.VBox()
+        self.packtext(vbox,
+                      _("Minimize the energy with respect to the positions."))
+        self.packimageselection(vbox)
+        pack(vbox, gtk.Label(""))
+
+        self.make_minimize_gui(vbox)
+        
+        pack(vbox, gtk.Label(""))
+        self.status_label = gtk.Label("")
+        pack(vbox, [self.status_label])
+        self.makebutbox(vbox)
+        vbox.show()
+        self.add(vbox)
+        self.show()
+        self.gui.register_vulnerable(self)
+
+    def run(self, *args):
+        "User has pressed [Run]: run the minimization."
+        if not self.setup_atoms():
+            return
+        fmax = self.fmax.value
+        steps = self.steps.value
+        mininame = self.minimizers[self.algo.get_active()]
+        self.begin(mode="min", algo=mininame, fmax=fmax, steps=steps)
+        algo = getattr(ase.optimize, mininame)
+        try:
+            logger_func = self.gui.simulation['progress'].get_logger_stream
+        except (KeyError, AttributeError):
+            logger = None
+        else:
+            logger = logger_func()  # Don't catch errors in the function.
+
+        # Display status message
+        self.status_label.set_text(_("Running ..."))
+        self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                    gtk.gdk.color_parse('#AA0000'))
+        while gtk.events_pending():
+            gtk.main_iteration()
+
+        self.prepare_store_atoms()
+        if mininame == "MDMin":
+            minimizer = algo(self.atoms, logfile=logger,
+                             dt=self.mdmin_dt.value)
+        else:
+            minimizer = algo(self.atoms, logfile=logger)
+        minimizer.attach(self.store_atoms)
+        try:
+            minimizer.run(fmax=fmax, steps=steps)
+        except AseGuiCancelException:
+            # Update display to reflect cancellation of simulation.
+            self.status_label.set_text(_("Minimization CANCELLED after "
+                                         "%i steps.")
+                                       % (self.count_steps,))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#AA4000'))
+        except MemoryError:
+            self.status_label.set_text(_("Out of memory, consider using "
+                                         "LBFGS instead"))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#AA4000'))
+            
+        else:
+            # Update display to reflect succesful end of simulation.
+            self.status_label.set_text(_("Minimization completed in %i steps.")
+                                       % (self.count_steps,))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#007700'))
+            
+        self.end()
+        if self.count_steps:
+            # Notify other windows that atoms have changed.
+            # This also notifies this window!
+            self.gui.notify_vulnerable()
+
+        # Open movie window and energy graph
+        if self.gui.images.nimages > 1:
+            self.gui.movie()
+            assert not np.isnan(self.gui.images.E[0])
+            if not self.gui.plot_graphs_newatoms():
+                expr = 'i, e - E[-1]'            
+                self.gui.plot_graphs(expr=expr)
+
+    def notify_atoms_changed(self):
+        "When atoms have changed, check for the number of images."
+        self.setupimageselection()
+        
diff --git a/ase/gui/movie.py b/ase/gui/movie.py
new file mode 100644
index 0000000..e1c1e12
--- /dev/null
+++ b/ase/gui/movie.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+import gtk
+from gettext import gettext as _
+import gobject
+
+from ase.gui.widgets import pack
+
+class Movie(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_position(gtk.WIN_POS_NONE)
+        self.connect('destroy', self.close)
+        #self.connect('delete_event', self.exit2)
+        self.set_title(_('Movie'))
+        vbox = gtk.VBox()
+        pack(vbox, gtk.Label(_('Image number:')))
+        self.frame_number = gtk.Adjustment(gui.frame, 0,
+                                           gui.images.nimages - 1,
+                                           1.0, 5.0)
+        self.frame_number.connect('value-changed', self.new_frame)
+
+        hscale = pack(vbox, gtk.HScale(self.frame_number))
+        hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
+        hscale.set_digits(0)
+
+        buttons = [gtk.Button(stock=gtk.STOCK_GOTO_FIRST),
+                   gtk.Button(stock=gtk.STOCK_GO_BACK),
+                   gtk.Button(stock=gtk.STOCK_GO_FORWARD),
+                   gtk.Button(stock=gtk.STOCK_GOTO_LAST)]
+
+        buttons[0].connect('clicked', self.click, -1, True)
+        buttons[1].connect('clicked', self.click, -1)
+        buttons[2].connect('clicked', self.click, 1)
+        buttons[3].connect('clicked', self.click, 1, True)
+
+        pack(vbox, buttons)
+
+        play = gtk.Button(_('Play'))
+        play.connect('clicked', self.play)
+        stop = gtk.Button(_('Stop'))
+        stop.connect('clicked', self.stop)
+        # TRANSLATORS: This function plays an animation forwards and backwards
+        # alternatingly, e.g. for displaying vibrational movement
+        self.rock = gtk.CheckButton(_('Rock'))
+
+        pack(vbox, [play, stop, gtk.Label('  '), self.rock])
+
+        if gui.images.nimages > 150:
+            skipdefault = gui.images.nimages/150
+            tdefault = min(max(gui.images.nimages/(skipdefault*5.0), 1.0), 30)
+        else:
+            skipdefault = 0
+            tdefault = min(max(gui.images.nimages/5.0, 1.0), 30)
+        self.time = gtk.Adjustment(tdefault, 1.0, 99, 0.1)
+        self.time_spin = gtk.SpinButton(self.time, 0, 0)
+        self.time_spin.set_digits(1)
+        self.time.connect("value-changed",self.frame_rate_changed)
+        self.skip = gtk.Adjustment(skipdefault, 0, 99, 1)
+        self.skip_spin = gtk.SpinButton(self.skip, 0, 0)
+        pack(vbox, [gtk.Label(_(' Frame rate: ')), self.time_spin,
+                    gtk.Label(_(' Skip frames: ')), self.skip_spin,
+                    gtk.Label('   ')])
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+        #gui.m=self
+        self.direction = 1
+        self.id = None
+        gui.register_vulnerable(self)
+
+    def notify_atoms_changed(self):
+        "Called by gui object when the atoms have changed."
+        self.destroy()
+        
+    def close(self, event):
+        self.stop()
+
+    def click(self, button, step, firstlast=False):
+        if firstlast and step < 0:
+            i = 0
+        elif firstlast:
+            i = self.gui.images.nimages - 1
+        else:
+            i = max(0, min(self.gui.images.nimages - 1, self.gui.frame + step))
+        self.gui.set_frame(i)
+        self.frame_number.value = i
+        if firstlast:
+            self.direction = cmp(-step, 0)
+        else:
+            self.direction = cmp(step, 0)
+            
+    def new_frame(self, widget):
+        self.gui.set_frame(int(self.frame_number.value))
+
+    def play(self, widget=None):
+        if self.id is not None:
+            gobject.source_remove(self.id)
+        t = int(1000.0 / float(self.time.value))
+        self.id = gobject.timeout_add(t, self.step)
+
+    def stop(self, widget=None):
+        if self.id is not None:
+            gobject.source_remove(self.id)
+            self.id = None
+
+    def frame_rate_changed(self,widget=None):
+        if self.id is not None:
+            self.play()
+
+    def step(self):
+        i = self.gui.frame
+        nimages = self.gui.images.nimages
+        delta = int(self.skip.value + 1)
+        
+        if self.rock.get_active():
+            if i <= self.skip.value:
+                self.direction = 1
+            elif i >= nimages - delta:
+                self.direction = -1
+            i += self.direction * delta
+        else:
+            i = (i + self.direction * delta + nimages) % nimages
+            
+        self.gui.set_frame(i)
+        self.frame_number.value = i
+        return True
+
+    def new_time(self, widget):
+        if self.id is not None:
+            self.play()
+
+if __name__ == '__main__':
+    import os
+    os.system('python gui.py')
diff --git a/ase/gui/nanoparticle.py b/ase/gui/nanoparticle.py
new file mode 100644
index 0000000..25775c7
--- /dev/null
+++ b/ase/gui/nanoparticle.py
@@ -0,0 +1,621 @@
+# encoding: utf-8
+"""nanoparticle.py - Window for setting up crystalline nanoparticles.
+"""
+
+import gtk
+from gettext import gettext as _
+from copy import copy
+from ase.gui.widgets import pack, cancel_apply_ok, oops, help
+from ase.gui.setupwindow import SetupWindow
+from ase.gui.pybutton import PyButton
+import ase
+import numpy as np
+# Delayed imports:
+# ase.cluster.data
+from ase.cluster.cubic import FaceCenteredCubic, BodyCenteredCubic, SimpleCubic
+from ase.cluster.hexagonal import HexagonalClosedPacked, Graphite
+from ase.cluster import wulff_construction
+
+introtext = _("""\
+Create a nanoparticle either by specifying the number of layers, or using the
+Wulff construction.  Please press the [Help] button for instructions on how to
+specify the directions.
+WARNING: The Wulff construction currently only works with cubic crystals!
+""")
+
+helptext = _("""
+The nanoparticle module sets up a nano-particle or a cluster with a given
+crystal structure.
+
+1) Select the element, the crystal structure and the lattice constant(s).
+   The [Get structure] button will find the data for a given element.
+
+2) Choose if you want to specify the number of layers in each direction, or if
+   you want to use the Wulff construction.  In the latter case, you must specify
+   surface energies in each direction, and the size of the cluster.
+
+How to specify the directions:
+------------------------------
+
+First time a direction appears, it is interpreted as the entire family of
+directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of these
+directions is specified again, the second specification overrules that specific
+direction.  For this reason, the order matters and you can rearrange the
+directions with the [Up] and [Down] keys.  You can also add a new direction,
+remember to press [Add] or it will not be included.
+
+Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of directions,
+the {111} family and then the (001) direction, overruling the value given for
+the whole family of directions.
+""")
+
+py_template_layers = """
+import ase
+%(import)s
+
+surfaces = %(surfaces)s
+layers = %(layers)s
+lc = %(latconst)s
+atoms = %(factory)s('%(element)s', surfaces, layers, latticeconstant=lc)
+
+# OPTIONAL: Cast to ase.Atoms object, discarding extra information:
+# atoms = ase.Atoms(atoms)
+"""
+
+py_template_wulff = """
+import ase
+from ase.cluster import wulff_construction
+
+surfaces = %(surfaces)s
+esurf = %(energies)s
+lc = %(latconst)s
+size = %(natoms)s  # Number of atoms
+atoms = wulff_construction('%(element)s', surfaces, esurf, size, '%(structure)s',
+                           rounding='%(rounding)s', latticeconstant=lc)
+
+# OPTIONAL: Cast to ase.Atoms object, discarding extra information:
+# atoms = ase.Atoms(atoms)
+"""
+
+class SetupNanoparticle(SetupWindow):
+    "Window for setting up a nanoparticle."
+    # Structures:  Abbreviation, name, 4-index (boolean), two lattice const (bool), factory
+    structure_data = (('fcc', _('Face centered cubic (fcc)'), False, False, FaceCenteredCubic),
+                      ('bcc', _('Body centered cubic (bcc)'), False, False, BodyCenteredCubic),
+                      ('sc',  _('Simple cubic (sc)'), False, False, SimpleCubic),
+                      ('hcp', _('Hexagonal closed-packed (hcp)'), True, True, HexagonalClosedPacked),
+                      ('graphite', _('Graphite'), True, True, Graphite),
+                      )
+    #NB:  HCP is broken!
+    
+    # A list of import statements for the Python window.
+    import_names = {'fcc': 'from ase.cluster.cubic import FaceCenteredCubic',
+                    'bcc': 'from ase.cluster.cubic import BodyCenteredCubic',
+                    'sc': 'from ase.cluster.cubic import SimpleCubic',
+                    'hcp': 'from ase.cluster.hexagonal import HexagonalClosedPacked',
+                    'graphite': 'from ase.cluster.hexagonal import Graphite',
+                    }
+    # Default layer specifications for the different structures.
+    default_layers = {'fcc': [( (1,0,0), 6),
+                              ( (1,1,0), 9),
+                              ( (1,1,1), 5)],
+                      'bcc': [( (1,0,0), 6),
+                              ( (1,1,0), 9),
+                              ( (1,1,1), 5)],
+                      'sc':  [( (1,0,0), 6),
+                              ( (1,1,0), 9),
+                              ( (1,1,1), 5)],
+                      'hcp': [( (0,0,0,1), 5),
+                              ( (1,0,-1,0), 5)],
+                      'graphite': [( (0,0,0,1), 5),
+                                   ( (1,0,-1,0), 5)]
+                      }
+    
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Nanoparticle"))
+        self.atoms = None
+        self.no_update = True
+        
+        vbox = gtk.VBox()
+
+        # Intoductory text
+        self.packtext(vbox, introtext)
+           
+        # Choose the element
+        label = gtk.Label(_("Element: "))
+        label.set_alignment(0.0, 0.2)
+        element = gtk.Entry(max=3)
+        self.element = element
+        lattice_button = gtk.Button(_("Get structure"))
+        lattice_button.connect('clicked', self.set_structure_data)
+        self.elementinfo = gtk.Label(" ")
+        pack(vbox, [label, element, self.elementinfo, lattice_button], end=True)
+        self.element.connect('activate', self.update)
+        self.legal_element = False
+
+        # The structure and lattice constant
+        label = gtk.Label(_("Structure: "))
+        self.structure = gtk.combo_box_new_text()
+        self.list_of_structures = []
+        self.needs_4index = {}
+        self.needs_2lat = {}
+        self.factory = {}
+        for abbrev, name, n4, c, factory in self.structure_data:
+            self.structure.append_text(name)
+            self.list_of_structures.append(abbrev)
+            self.needs_4index[abbrev] = n4
+            self.needs_2lat[abbrev] = c
+            self.factory[abbrev] = factory
+        self.structure.set_active(0)
+        self.fourindex = self.needs_4index[self.list_of_structures[0]]
+        self.structure.connect('changed', self.update_structure)
+        
+        label2 = gtk.Label(_("Lattice constant:  a ="))
+        self.lattice_const_a = gtk.Adjustment(3.0, 0.0, 1000.0, 0.01)
+        self.lattice_const_c = gtk.Adjustment(5.0, 0.0, 1000.0, 0.01)
+        self.lattice_box_a = gtk.SpinButton(self.lattice_const_a, 10.0, 3)
+        self.lattice_box_c = gtk.SpinButton(self.lattice_const_c, 10.0, 3)
+        self.lattice_box_a.numeric = True
+        self.lattice_box_c.numeric = True
+        self.lattice_label_c = gtk.Label(" c =")
+        pack(vbox, [label, self.structure])
+        pack(vbox, [label2, self.lattice_box_a,
+                    self.lattice_label_c, self.lattice_box_c])
+        self.lattice_label_c.hide()
+        self.lattice_box_c.hide()
+        self.lattice_const_a.connect('value-changed', self.update)
+        self.lattice_const_c.connect('value-changed', self.update)
+
+        # Choose specification method
+        label = gtk.Label(_("Method: "))
+        self.method = gtk.combo_box_new_text()
+        for meth in (_("Layer specification"), _("Wulff construction")):
+            self.method.append_text(meth)
+        self.method.set_active(0)
+        self.method.connect('changed', self.update_gui_method)
+        pack(vbox, [label, self.method])
+        pack(vbox, gtk.Label(""))
+        self.old_structure = None
+
+        frame = gtk.Frame()
+        pack(vbox, frame)
+        framebox = gtk.VBox()
+        frame.add(framebox)
+        framebox.show()
+        self.layerlabel = gtk.Label("Missing text")  # Filled in later
+        pack(framebox, [self.layerlabel])
+        # This box will contain a single table that is replaced when
+        # the list of directions is changed.
+        self.direction_table_box = gtk.VBox()
+        pack(framebox, self.direction_table_box)
+        pack(self.direction_table_box, 
+             gtk.Label(_("Dummy placeholder object")))
+        pack(framebox, gtk.Label(""))
+        pack(framebox, [gtk.Label(_("Add new direction:"))])
+        self.newdir_label = []
+        self.newdir_box = []
+        self.newdir_index = []
+        packlist = []
+        for txt in ('(', ', ', ', ', ', '):
+            self.newdir_label.append(gtk.Label(txt))
+            adj = gtk.Adjustment(0, -100, 100, 1)
+            self.newdir_box.append(gtk.SpinButton(adj, 1, 0))
+            self.newdir_index.append(adj)
+            packlist.append(self.newdir_label[-1])
+            packlist.append(self.newdir_box[-1])
+        self.newdir_layers = gtk.Adjustment(5, 0, 100, 1)
+        self.newdir_layers_box = gtk.SpinButton(self.newdir_layers, 1, 0)
+        self.newdir_esurf = gtk.Adjustment(1.0, 0, 1000.0, 0.1)
+        self.newdir_esurf_box = gtk.SpinButton(self.newdir_esurf, 10, 3)
+        addbutton = gtk.Button(_("Add"))
+        addbutton.connect('clicked', self.row_add)
+        packlist.extend([gtk.Label("): "),
+                         self.newdir_layers_box,
+                         self.newdir_esurf_box,
+                         gtk.Label("  "),
+                         addbutton])
+        pack(framebox, packlist)
+        self.defaultbutton = gtk.Button(_("Set all directions to default "
+                                          "values"))
+        self.defaultbutton.connect('clicked', self.default_direction_table)
+        self.default_direction_table()
+
+        # Extra widgets for the Wulff construction
+        self.wulffbox = gtk.VBox()
+        pack(vbox, self.wulffbox)
+        label = gtk.Label(_("Particle size: "))
+        self.size_n_radio = gtk.RadioButton(None, _("Number of atoms: "))
+        self.size_n_radio.set_active(True)
+        self.size_n_adj = gtk.Adjustment(100, 1, 100000, 1)
+        self.size_n_spin = gtk.SpinButton(self.size_n_adj, 0, 0)
+        self.size_dia_radio = gtk.RadioButton(self.size_n_radio,
+                                              _("Volume: "))
+        self.size_dia_adj = gtk.Adjustment(1.0, 0, 100.0, 0.1)
+        self.size_dia_spin = gtk.SpinButton(self.size_dia_adj, 10.0, 2)
+        pack(self.wulffbox, [label, self.size_n_radio, self.size_n_spin,
+                    gtk.Label("   "), self.size_dia_radio, self.size_dia_spin,
+                    gtk.Label(_("ų"))])
+        self.size_n_radio.connect("toggled", self.update_gui_size)
+        self.size_dia_radio.connect("toggled", self.update_gui_size)
+        self.size_n_adj.connect("value-changed", self.update_size_n)
+        self.size_dia_adj.connect("value-changed", self.update_size_dia)
+        label = gtk.Label(_("Rounding: If exact size is not possible, "
+                            "choose the size"))
+        pack(self.wulffbox, [label])
+        self.round_above = gtk.RadioButton(None, _("above  "))
+        self.round_below = gtk.RadioButton(self.round_above, _("below  "))
+        self.round_closest = gtk.RadioButton(self.round_above, _("closest  "))
+        self.round_closest.set_active(True)
+        butbox = gtk.HButtonBox()
+        self.smaller_button = gtk.Button(_("Smaller"))
+        self.larger_button = gtk.Button(_("Larger"))
+        self.smaller_button.connect('clicked', self.wulff_smaller)
+        self.larger_button.connect('clicked', self.wulff_larger)
+        pack(butbox, [self.smaller_button, self.larger_button])
+        buts = [self.round_above, self.round_below, self.round_closest]
+        for b in buts:
+            b.connect("toggled", self.update)
+        buts.append(butbox)
+        pack(self.wulffbox, buts, end=True)
+
+        # Information
+        pack(vbox, gtk.Label(""))
+        infobox = gtk.VBox()
+        label1 = gtk.Label(_("Number of atoms: "))
+        self.natoms_label = gtk.Label("-")
+        label2 = gtk.Label(_("   Approx. diameter: "))
+        self.dia1_label = gtk.Label("-")
+        pack(infobox, [label1, self.natoms_label, label2, self.dia1_label])
+        pack(infobox, gtk.Label(""))
+        infoframe = gtk.Frame(_("Information about the created cluster:"))
+        infoframe.add(infobox)
+        infobox.show()
+        pack(vbox, infoframe)
+        
+        # Buttons
+        self.pybut = PyButton(_("Creating a nanoparticle."))
+        self.pybut.connect('clicked', self.makeatoms)
+        helpbut = help(helptext)
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [self.pybut, helpbut, buts], end=True, bottom=True)
+        self.auto = gtk.CheckButton(_("Automatic Apply"))
+        fr = gtk.Frame()
+        fr.add(self.auto)
+        fr.show_all()
+        pack(vbox, [fr], end=True, bottom=True)
+        
+        # Finalize setup
+        self.update_structure()
+        self.update_gui_method()
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+        self.no_update = False
+
+    def default_direction_table(self, widget=None):
+        "Set default directions and values for the current crystal structure."
+        self.direction_table = []
+        struct = self.get_structure()
+        for direction, layers in self.default_layers[struct]:
+            adj1 = gtk.Adjustment(layers, -100, 100, 1)
+            adj2 = gtk.Adjustment(1.0, -1000.0, 1000.0, 0.1)
+            adj1.connect("value-changed", self.update)
+            adj2.connect("value-changed", self.update)
+            self.direction_table.append([direction, adj1, adj2])
+        self.update_direction_table()
+
+    def update_direction_table(self):
+        "Update the part of the GUI containing the table of directions."
+        #Discard old table
+        oldwidgets = self.direction_table_box.get_children()
+        assert len(oldwidgets) == 1
+        oldwidgets[0].hide()
+        self.direction_table_box.remove(oldwidgets[0])
+        del oldwidgets  # It should now be gone
+        tbl = gtk.Table(len(self.direction_table)+1, 7)
+        pack(self.direction_table_box, [tbl])
+        for i, data in enumerate(self.direction_table):
+            tbl.attach(gtk.Label("%s: " % (str(data[0]),)),
+                       0, 1, i, i+1)
+            if self.method.get_active():
+                # Wulff construction
+                spin = gtk.SpinButton(data[2], 1.0, 3)
+            else:
+                # Layers
+                spin = gtk.SpinButton(data[1], 1, 0)
+            tbl.attach(spin, 1, 2, i, i+1)
+            tbl.attach(gtk.Label("   "), 2, 3, i, i+1)
+            but = gtk.Button(_("Up"))
+            but.connect("clicked", self.row_swap_next, i-1)
+            if i == 0:
+                but.set_sensitive(False)
+            tbl.attach(but, 3, 4, i, i+1)
+            but = gtk.Button(_("Down"))
+            but.connect("clicked", self.row_swap_next, i)
+            if i == len(self.direction_table)-1:
+                but.set_sensitive(False)
+            tbl.attach(but, 4, 5, i, i+1)
+            but = gtk.Button(_("Delete"))
+            but.connect("clicked", self.row_delete, i)
+            if len(self.direction_table) == 1:
+                but.set_sensitive(False)
+            tbl.attach(but, 5, 6, i, i+1)
+        tbl.show_all()
+        self.update()
+
+    def get_structure(self):
+        "Returns the crystal structure chosen by the user."
+        return self.list_of_structures[self.structure.get_active()]
+
+    def update_structure(self, widget=None):
+        "Called when the user changes the structure."
+        s = self.get_structure()
+        if s != self.old_structure:
+            old4 = self.fourindex
+            self.fourindex = self.needs_4index[s]
+            if self.fourindex != old4:
+                # The table of directions is invalid.
+                self.default_direction_table()
+            self.old_structure = s
+            if self.needs_2lat[s]:
+                self.lattice_label_c.show()
+                self.lattice_box_c.show()
+            else:
+                self.lattice_label_c.hide()
+                self.lattice_box_c.hide()
+            if self.fourindex:
+                self.newdir_label[3].show()
+                self.newdir_box[3].show()
+            else:
+                self.newdir_label[3].hide()
+                self.newdir_box[3].hide()
+        self.update()
+
+    def update_gui_method(self, widget=None):
+        "Switch between layer specification and Wulff construction."
+        self.update_direction_table()
+        if self.method.get_active():
+            self.wulffbox.show()
+            self.layerlabel.set_text(_("Surface energies (as energy/area, "
+                                       "NOT per atom):"))
+            self.newdir_layers_box.hide()
+            self.newdir_esurf_box.show()
+        else:
+            self.wulffbox.hide()
+            self.layerlabel.set_text(_("Number of layers:"))
+            self.newdir_layers_box.show()
+            self.newdir_esurf_box.hide()
+        self.update()
+
+    def wulff_smaller(self, widget=None):
+        "Make a smaller Wulff construction."
+        n = len(self.atoms)
+        self.size_n_radio.set_active(True)
+        self.size_n_adj.value = n-1
+        self.round_below.set_active(True)
+        self.apply()
+
+    def wulff_larger(self, widget=None):
+        "Make a larger Wulff construction."
+        n = len(self.atoms)
+        self.size_n_radio.set_active(True)
+        self.size_n_adj.value = n+1
+        self.round_above.set_active(True)
+        self.apply()
+    
+    def row_add(self, widget=None):
+        "Add a row to the list of directions."
+        if self.fourindex:
+            n = 4
+        else:
+            n = 3
+        idx = tuple( [int(a.value) for a in self.newdir_index[:n]] )
+        if not np.array(idx).any():
+            oops(_("At least one index must be non-zero"))
+            return
+        if n == 4 and np.array(idx)[:3].sum() != 0:
+            oops(_("Invalid hexagonal indices",
+                 "The sum of the first three numbers must be zero"))
+            return
+        adj1 = gtk.Adjustment(self.newdir_layers.value, -100, 100, 1)
+        adj2 = gtk.Adjustment(self.newdir_esurf.value, -1000.0, 1000.0, 0.1)
+        adj1.connect("value-changed", self.update)
+        adj2.connect("value-changed", self.update)
+        self.direction_table.append([idx, adj1, adj2])
+        self.update_direction_table()
+
+    def row_delete(self, widget, row):
+        del self.direction_table[row]
+        self.update_direction_table()
+
+    def row_swap_next(self, widget, row):
+        dt = self.direction_table
+        dt[row], dt[row+1] = dt[row+1], dt[row]
+        self.update_direction_table()
+        
+    def update_gui_size(self, widget=None):
+        "Update gui when the cluster size specification changes."
+        self.size_n_spin.set_sensitive(self.size_n_radio.get_active())
+        self.size_dia_spin.set_sensitive(self.size_dia_radio.get_active())
+
+    def update_size_n(self, widget=None):
+        if not self.size_n_radio.get_active():
+            return
+        at_vol = self.get_atomic_volume()
+        dia = 2.0 * (3 * self.size_n_adj.value * at_vol / (4 * np.pi))**(1.0/3)
+        self.size_dia_adj.value = dia
+        self.update()
+
+    def update_size_dia(self, widget=None):
+        if not self.size_dia_radio.get_active():
+            return
+        at_vol = self.get_atomic_volume()
+        n = round(np.pi / 6 * self.size_dia_adj.value**3 / at_vol)
+        self.size_n_adj.value = n
+        self.update()
+                
+    def update(self, *args):
+        if self.no_update:
+            return
+        self.update_element()
+        if self.auto.get_active():
+            self.makeatoms()
+            if self.atoms is not None:
+                self.gui.new_atoms(self.atoms)
+        else:
+            self.clearatoms()
+        self.makeinfo()
+
+    def set_structure_data(self, *args):
+        "Called when the user presses [Get structure]."
+        if not self.update_element():
+            oops(_("Invalid element."))
+            return
+        z = ase.atomic_numbers[self.legal_element]
+        ref = ase.data.reference_states[z]
+        if ref is None:
+            structure = None
+        else:
+            structure = ref['symmetry']
+                
+        if ref is None or not structure in self.list_of_structures:
+            oops(_("Unsupported or unknown structure",
+                   "Element = %s,  structure = %s" % (self.legal_element,
+                                                      structure)))
+            return
+        for i, s in enumerate(self.list_of_structures):
+            if structure == s:
+                self.structure.set_active(i)
+        a = ref['a']
+        self.lattice_const_a.set_value(a)
+        self.fourindex = self.needs_4index[structure]
+        if self.fourindex:
+            try:
+                c = ref['c']
+            except KeyError:
+                c = ref['c/a'] * a
+            self.lattice_const_c.set_value(c)
+            self.lattice_label_c.show()
+            self.lattice_box_c.show()
+        else:
+            self.lattice_label_c.hide()
+            self.lattice_box_c.hide()
+
+    def makeatoms(self, *args):
+        "Make the atoms according to the current specification."
+        if not self.update_element():
+            self.clearatoms()
+            self.makeinfo()
+            return False
+        assert self.legal_element is not None
+        struct = self.list_of_structures[self.structure.get_active()]
+        if self.needs_2lat[struct]:
+            # a and c lattice constants
+            lc = {'a': self.lattice_const_a.value,
+                  'c': self.lattice_const_c.value}
+            lc_str = str(lc)
+        else:
+            lc = self.lattice_const_a.value
+            lc_str = "%.5f" % (lc,)
+        if self.method.get_active() == 0:
+            # Layer-by-layer specification
+            surfaces = [x[0] for x in self.direction_table]
+            layers = [int(x[1].value) for x in self.direction_table]
+            self.atoms = self.factory[struct](self.legal_element, copy(surfaces),
+                                              layers, latticeconstant=lc)
+            imp = self.import_names[struct]
+            self.pybut.python = py_template_layers % {'import': imp,
+                                                      'element': self.legal_element,
+                                                      'surfaces': str(surfaces),
+                                                      'layers': str(layers),
+                                                      'latconst': lc_str,
+                                                      'factory': imp.split()[-1]
+                                                      }
+        else:
+            # Wulff construction
+            assert self.method.get_active() == 1
+            surfaces = [x[0] for x in self.direction_table]
+            surfaceenergies = [x[2].value for x in self.direction_table]            
+            self.update_size_dia()
+            if self.round_above.get_active():
+                rounding = "above"
+            elif self.round_below.get_active():
+                rounding = "below"
+            elif self.round_closest.get_active():
+                rounding = "closest"
+            else:
+                raise RuntimeError("No rounding!")
+            self.atoms = wulff_construction(self.legal_element, surfaces,
+                                            surfaceenergies,
+                                            self.size_n_adj.value,
+                                            self.factory[struct],
+                                            rounding, lc)
+            self.pybut.python = py_template_wulff % {'element': self.legal_element,
+                                                     'surfaces': str(surfaces),
+                                                     'energies': str(surfaceenergies),
+                                                     'latconst': lc_str,
+                                                     'natoms': self.size_n_adj.value,
+                                                     'structure': struct,
+                                                     'rounding': rounding
+                                                      }
+        self.makeinfo()
+
+    def clearatoms(self):
+        self.atoms = None
+        self.pybut.python = None
+
+    def get_atomic_volume(self):
+        s = self.list_of_structures[self.structure.get_active()]
+        a = self.lattice_const_a.value
+        c = self.lattice_const_c.value
+        if s == 'fcc':
+            return a**3 / 4
+        elif s == 'bcc':
+            return a**3 / 2
+        elif s == 'sc':
+            return a**3
+        elif s == 'hcp':
+            return np.sqrt(3.0)/2 * a * a * c / 2
+        elif s == 'graphite':
+            return np.sqrt(3.0)/2 * a * a * c / 4
+        else:
+            raise RuntimeError("Unknown structure: "+s)
+
+    def makeinfo(self):
+        """Fill in information field about the atoms.
+
+        Also turns the Wulff construction buttons [Larger] and
+        [Smaller] on and off.
+        """
+        if self.atoms is None:
+            self.natoms_label.set_label("-")
+            self.dia1_label.set_label("-")
+            self.smaller_button.set_sensitive(False)
+            self.larger_button.set_sensitive(False)
+        else:
+            self.natoms_label.set_label(str(len(self.atoms)))
+            at_vol = self.get_atomic_volume()
+            dia = 2 * (3 * len(self.atoms) * at_vol / (4 * np.pi))**(1.0/3.0)
+            self.dia1_label.set_label(_("%.1f Å") % (dia,))
+            self.smaller_button.set_sensitive(True)
+            self.larger_button.set_sensitive(True)
+            
+    def apply(self, *args):
+        self.makeatoms()
+        if self.atoms is not None:
+            self.gui.new_atoms(self.atoms)
+            return True
+        else:
+            oops(_("No valid atoms."),
+                 _("You have not (yet) specified a consistent set of "
+                   "parameters."))
+            return False
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+            
diff --git a/ase/gui/nanotube.py b/ase/gui/nanotube.py
new file mode 100644
index 0000000..b9c449c
--- /dev/null
+++ b/ase/gui/nanotube.py
@@ -0,0 +1,148 @@
+# encoding: utf-8
+"""nanotube.py - Window for setting up Carbon nanotubes and similar tubes.
+"""
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops
+from ase.gui.setupwindow import SetupWindow
+from ase.gui.pybutton import PyButton
+from ase.structure import nanotube
+import ase
+import numpy as np
+
+introtext = _("""\
+Set up a Carbon nanotube by specifying the (n,m) roll-up vector.
+Please note that m <= n.
+
+Nanotubes of other elements can be made by specifying the element
+and bond length.\
+""")
+
+py_template = """
+from ase.structure import nanotube
+
+atoms = nanotube(%(n)i, %(m)i, length=%(length)i, bond=%(bl).3f, symbol='%(symb)s')
+"""
+
+
+class SetupNanotube(SetupWindow):
+    "Window for setting up a (Carbon) nanotube."
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Nanotube"))
+        vbox = gtk.VBox()
+
+        # Intoductory text
+        self.packtext(vbox, introtext)
+           
+        # Choose the element and bond length
+        label1 = gtk.Label(_("Element: "))
+        #label.set_alignment(0.0, 0.2)
+        self.element = gtk.Entry(max=3)
+        self.element.set_text("C")
+        self.element.connect('activate', self.update_element)
+        self.bondlength = gtk.Adjustment(1.42, 0.0, 1000.0, 0.01)
+        label2 = gtk.Label(_("  Bond length: "))
+        label3 = gtk.Label(_("Å"))
+        bond_box = gtk.SpinButton(self.bondlength, 10.0, 3)
+        pack(vbox, [label1, self.element, label2, bond_box, label3])
+        self.elementinfo = gtk.Label("")
+        self.elementinfo.modify_fg(gtk.STATE_NORMAL,
+                                   gtk.gdk.color_parse('#FF0000'))
+        pack(vbox, [self.elementinfo])
+        pack(vbox, gtk.Label(""))
+
+        # Choose the structure.
+        pack(vbox, [gtk.Label(_("Select roll-up vector (n,m) "
+                                "and tube length:"))])
+        label1 = gtk.Label("n: ")
+        label2 = gtk.Label("  m: ")
+        self.n = gtk.Adjustment(5, 1, 100, 1)
+        self.m = gtk.Adjustment(5, 0, 100, 1)
+        spinn = gtk.SpinButton(self.n, 0, 0)
+        spinm = gtk.SpinButton(self.m, 0, 0)
+        label3 = gtk.Label(_("  Length: "))
+        self.length = gtk.Adjustment(1, 1, 100, 1)
+        spinl = gtk.SpinButton(self.length, 0, 0)
+        pack(vbox, [label1, spinn, label2, spinm, label3, spinl])
+        self.err = gtk.Label("")
+        self.err.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FF0000'))
+        pack(vbox, [self.err])
+        pack(vbox, gtk.Label(""))
+
+        # Buttons
+        self.pybut = PyButton(_("Creating a nanoparticle."))
+        self.pybut.connect('clicked', self.makeatoms)
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [self.pybut, buts], end=True, bottom=True)
+
+        # Finalize setup
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def update_element(self, *args):
+        "Called when a new element may have been entered."
+        # Assumes the element widget is self.element and that a label
+        # for errors is self.elementinfo.  The chemical symbol is
+        # placed in self.legalelement - or None if the element is
+        # invalid.
+        elem = self.element.get_text()
+        if not elem:
+            self.invalid_element(_("  No element specified!"))
+            return False
+        try:
+            z = int(elem)
+        except ValueError:
+            # Probably a symbol
+            try:
+                z = ase.data.atomic_numbers[elem]
+            except KeyError:
+                self.invalid_element()
+                return False
+        try:
+            symb = ase.data.chemical_symbols[z]
+        except KeyError:
+            self.invalid_element()
+            return False
+        self.elementinfo.set_text("")
+        self.legal_element = symb
+        return True
+        
+    def makeatoms(self, *args):
+        self.update_element()
+        if self.legal_element is None:
+            self.atoms = None
+            self.pybut.python = None
+        else:
+            n = int(self.n.value)
+            m = int(self.m.value)
+            symb = self.legal_element
+            length = int(self.length.value)
+            bl = self.bondlength.value
+            self.atoms = nanotube(n, m, length=length, bond=bl, symbol=symb)
+            # XXX can this be translated?
+            self.pybut.python = py_template % {'n': n, 'm':m, 'length':length,
+                                               'symb':symb, 'bl':bl}
+        
+
+    def apply(self, *args):
+        self.makeatoms()
+        if self.atoms is not None:
+            self.gui.new_atoms(self.atoms)
+            return True
+        else:
+            oops(_("No valid atoms."),
+                 _("You have not (yet) specified a consistent "
+                   "set of parameters."))
+            return False
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+            
+
diff --git a/ase/gui/neb.py b/ase/gui/neb.py
new file mode 100644
index 0000000..1c577c7
--- /dev/null
+++ b/ase/gui/neb.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from math import sqrt
+
+import numpy as np
+
+from ase.neb import fit0
+
+def NudgedElasticBand(images):
+    N = images.repeat.prod()
+    natoms = images.natoms // N
+
+    R = images.P[:, :natoms]
+    E = images.E
+    F = images.F[:, :natoms]
+
+    s, E, Sfit, Efit, lines = fit0(E, F, R)
+    import pylab
+    import matplotlib
+    #matplotlib.use('GTK')
+    pylab.ion()
+    x = 2.95
+    pylab.figure(figsize=(x * 2.5**0.5, x))
+    pylab.plot(s, E, 'o')
+    for x, y in lines:
+        pylab.plot(x, y, '-g')
+    pylab.plot(Sfit, Efit, 'k-')
+    pylab.xlabel(u'path [Å]')
+    pylab.ylabel(u'energy [eV]')
+    pylab.title('Maximum: %.3f eV' % max(Efit))
+    pylab.show()
diff --git a/ase/gui/po/ag.pot b/ase/gui/po/ag.pot
new file mode 100644
index 0000000..3121c8f
--- /dev/null
+++ b/ase/gui/po/ag.pot
@@ -0,0 +1,2810 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-02-14 13:35+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: ../ag.py:130
+msgid ""
+"\n"
+"An exception occurred!  Please report the issue to\n"
+"ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if\n"
+"it was a user error, so that a better error message can be provided\n"
+"next time."
+msgstr ""
+
+#. Asap and GPAW may be imported if selected.
+#: ../calculator.py:18
+msgid ""
+"To make most calculations on the atoms, a Calculator object must first\n"
+"be associated with it.  ASE supports a number of calculators, supporting\n"
+"different elements, and implementing different physical models for the\n"
+"interatomic interactions."
+msgstr ""
+
+#. Informational text about the calculators
+#: ../calculator.py:26
+msgid ""
+"The Lennard-Jones pair potential is one of the simplest\n"
+"possible models for interatomic interactions, mostly\n"
+"suitable for noble gasses and model systems.\n"
+"\n"
+"Interactions are described by an interaction length and an\n"
+"interaction strength."
+msgstr ""
+
+#: ../calculator.py:35
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au, the Al potential is however not suitable for materials\n"
+"science application, as the stacking fault energy is wrong.\n"
+"\n"
+"A number of parameter sets are provided.\n"
+"\n"
+"<b>Default parameters:</b>\n"
+"\n"
+"The default EMT parameters, as published in K. W. Jacobsen,\n"
+"P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternative Cu, Ag and Au:</b>\n"
+"\n"
+"An alternative set of parameters for Cu, Ag and Au,\n"
+"reoptimized to experimental data including the stacking\n"
+"fault energies by Torben Rasmussen (partly unpublished).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parameters for Ruthenium, as published in J. Gavnholt and\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metallic glasses:</b>\n"
+"\n"
+"Parameters for MgCu and CuZr metallic glasses. MgCu\n"
+"parameters are in N. P. Bailey, J. Schiøtz and\n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).\n"
+"CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+msgstr ""
+
+#: ../calculator.py:70
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au.  In addition, this implementation allows for the use of\n"
+"H, N, O and C adatoms, although the description of these is\n"
+"most likely not very good.\n"
+"\n"
+"<b>This is the ASE implementation of EMT.</b> For large\n"
+"simulations the ASAP implementation is more suitable; this\n"
+"implementation is mainly to make EMT available when ASAP is\n"
+"not installed.\n"
+msgstr ""
+
+#: ../calculator.py:85
+msgid ""
+"The Brenner potential is a reactive bond-order potential for\n"
+"carbon and hydrocarbons.  As a bond-order potential, it takes\n"
+"into account that carbon orbitals can hybridize in different\n"
+"ways, and that carbon can form single, double and triple\n"
+"bonds.  That the potential is reactive means that it can\n"
+"handle gradual changes in the bond order as chemical bonds\n"
+"are formed or broken.\n"
+"\n"
+"The Brenner potential is implemented in Asap, based on a\n"
+"C implentation published at http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"The potential is documented here:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+msgstr ""
+
+#: ../calculator.py:107
+msgid ""
+"GPAW implements Density Functional Theory using a\n"
+"<b>G</b>rid-based real-space representation of the wave\n"
+"functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave\n"
+"method for handling the core regions.  \n"
+msgstr ""
+
+#: ../calculator.py:114
+msgid ""
+"FHI-aims is an external package implementing density \n"
+"functional theory and quantum chemical methods using \n"
+"all-electron methods and a numeric local orbital basis set. \n"
+"For full details, see http://www.fhi-berlin.mpg.de/aims/ \n"
+"or Comp. Phys. Comm. v180 2175 (2009). The ASE \n"
+"documentation contains information on the keywords and \n"
+"functionalities available within this interface. \n"
+msgstr ""
+
+#: ../calculator.py:124
+msgid ""
+"WARNING:\n"
+"Your system seems to have more than zero but less than \n"
+"three periodic dimensions. Please check that this is \n"
+"really what you want to compute. Assuming full \n"
+"3D periodicity for this calculator."
+msgstr ""
+
+#: ../calculator.py:131
+msgid ""
+"VASP is an external package implementing density \n"
+"functional functional theory using pseudopotentials \n"
+"or the projector-augmented wave method together \n"
+"with a plane wave basis set. For full details, see\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+msgstr ""
+
+#: ../calculator.py:140
+msgid "Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+msgstr ""
+
+#: ../calculator.py:141
+msgid "Alternative Cu, Ag and Au"
+msgstr ""
+
+#: ../calculator.py:142
+msgid "Ruthenium"
+msgstr ""
+
+#: ../calculator.py:143
+msgid "CuMg and CuZr metallic glass"
+msgstr ""
+
+#: ../calculator.py:158
+msgid "Select calculator"
+msgstr ""
+
+#: ../calculator.py:164
+msgid "Calculator:"
+msgstr ""
+
+#. No calculator (the default)
+#: ../calculator.py:167
+msgid "None"
+msgstr ""
+
+#: ../calculator.py:172
+msgid "Lennard-Jones (ASAP)"
+msgstr ""
+
+#: ../calculator.py:173 ../calculator.py:206 ../calculator.py:215
+#: ../calculator.py:224
+msgid "Setup"
+msgstr ""
+
+#: ../calculator.py:180
+msgid "EMT - Effective Medium Theory (ASAP)"
+msgstr ""
+
+#: ../calculator.py:192
+msgid "EMT - Effective Medium Theory (ASE)"
+msgstr ""
+
+#: ../calculator.py:198
+msgid "Brenner Potential (ASAP)"
+msgstr ""
+
+#: ../calculator.py:204
+msgid "Density Functional Theory (GPAW)"
+msgstr ""
+
+#: ../calculator.py:213
+msgid "Density Functional Theory (FHI-aims)"
+msgstr ""
+
+#: ../calculator.py:222
+msgid "Density Functional Theory (VASP)"
+msgstr ""
+
+#: ../calculator.py:235
+msgid "Check that the calculator is reasonable."
+msgstr ""
+
+#: ../calculator.py:298 ../simulation.py:114
+msgid "No atoms present"
+msgstr ""
+
+#: ../calculator.py:388 ../calculator.py:422 ../calculator.py:456
+msgid "ASAP is not installed. (Failed to import asap3)"
+msgstr ""
+
+#: ../calculator.py:391
+msgid "You must set up the Lennard-Jones parameters"
+msgstr ""
+
+#: ../calculator.py:396
+msgid "Could not create useful Lennard-Jones calculator."
+msgstr ""
+
+#: ../calculator.py:430
+msgid "Could not attach EMT calculator to the atoms."
+msgstr ""
+
+#: ../calculator.py:472 ../calculator.py:484
+msgid "GPAW is not installed. (Failed to import gpaw)"
+msgstr ""
+
+#: ../calculator.py:475
+msgid "You must set up the GPAW parameters"
+msgstr ""
+
+#: ../calculator.py:516
+msgid "You must set up the FHI-aims parameters"
+msgstr ""
+
+#: ../calculator.py:530
+msgid "You must set up the VASP parameters"
+msgstr ""
+
+#: ../calculator.py:554
+#, python-format
+msgid "Element %(sym)s not allowed by the '%(name)s' calculator"
+msgstr ""
+
+#: ../calculator.py:561
+msgid "Info"
+msgstr ""
+
+#: ../calculator.py:577
+msgid "Lennard-Jones parameters"
+msgstr ""
+
+#: ../calculator.py:589
+msgid "Specify the Lennard-Jones parameters here"
+msgstr ""
+
+#: ../calculator.py:592
+msgid "Epsilon (eV):"
+msgstr ""
+
+#: ../calculator.py:596
+msgid "Sigma (Å):"
+msgstr ""
+
+#. TRANSLATORS: Shift roughly means adjust (about a potential)
+#: ../calculator.py:600
+msgid "Shift to make smooth at cutoff"
+msgstr ""
+
+#: ../calculator.py:681
+msgid "GPAW parameters"
+msgstr ""
+
+#. label = gtk.Label("Specify the GPAW parameters here")
+#. pack(vbox, [label])
+#. Print some info
+#: ../calculator.py:696 ../calculator.py:985 ../calculator.py:1473
+#, python-format
+msgid "%i atoms.\n"
+msgstr ""
+
+#: ../calculator.py:698
+#, python-format
+msgid "Orthogonal unit cell: %.2f x %.2f x %.2f Å."
+msgstr ""
+
+#: ../calculator.py:700
+msgid "Non-orthogonal unit cell:\n"
+msgstr ""
+
+#: ../calculator.py:710 ../calculator.py:1001 ../calculator.py:1488
+msgid "Exchange-correlation functional: "
+msgstr ""
+
+#. Grid spacing
+#: ../calculator.py:714
+msgid "Grid spacing"
+msgstr ""
+
+#: ../calculator.py:718 ../graphene.py:67 ../graphene.py:79 ../graphene.py:102
+#: ../nanotube.py:47 ../surfaceslab.py:97
+msgid "Å"
+msgstr ""
+
+#: ../calculator.py:719
+msgid "Grid points"
+msgstr ""
+
+#: ../calculator.py:728
+#, python-format
+msgid "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+msgstr ""
+
+#: ../calculator.py:754 ../calculator.py:1016 ../calculator.py:1524
+msgid "k-points  k = ("
+msgstr ""
+
+#: ../calculator.py:758 ../calculator.py:1020
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å"
+msgstr ""
+
+#. Spin polarized
+#: ../calculator.py:763 ../calculator.py:1486
+msgid "Spin polarized"
+msgstr ""
+
+#: ../calculator.py:769
+msgid "FD - Finite Difference (grid) mode"
+msgstr ""
+
+#: ../calculator.py:770
+msgid "LCAO - Linear Combination of Atomic Orbitals"
+msgstr ""
+
+#: ../calculator.py:773
+msgid "Mode: "
+msgstr ""
+
+#: ../calculator.py:775
+msgid "sz - Single Zeta"
+msgstr ""
+
+#: ../calculator.py:776
+msgid "szp - Single Zeta polarized"
+msgstr ""
+
+#: ../calculator.py:777
+msgid "dzp - Double Zeta polarized"
+msgstr ""
+
+#. dzp
+#: ../calculator.py:779
+msgid "Basis functions: "
+msgstr ""
+
+#. Mixer
+#: ../calculator.py:785
+msgid "Non-standard mixer parameters"
+msgstr ""
+
+#: ../calculator.py:981
+msgid "FHI-aims parameters"
+msgstr ""
+
+#: ../calculator.py:988
+msgid "Periodic geometry, unit cell is:\n"
+msgstr ""
+
+#: ../calculator.py:993
+msgid "Non-periodic geometry.\n"
+msgstr ""
+
+#: ../calculator.py:1000
+msgid "Hirshfeld-based dispersion correction"
+msgstr ""
+
+#. Spin polarized, charge, relativity
+#: ../calculator.py:1026
+msgid "Spin / initial moment "
+msgstr ""
+
+#: ../calculator.py:1044
+msgid "   Charge"
+msgstr ""
+
+#: ../calculator.py:1046
+msgid "   Relativity"
+msgstr ""
+
+#: ../calculator.py:1048
+msgid " Threshold"
+msgstr ""
+
+#. self-consistency criteria
+#: ../calculator.py:1053
+msgid "Self-consistency convergence:"
+msgstr ""
+
+#: ../calculator.py:1066
+msgid "Compute forces"
+msgstr ""
+
+#. XXX: use gtk table for layout.  Spaces will not work well otherwise
+#. (depend on fonts, widget style, ...)
+#. TRANSLATORS: Don't care too much about these, just get approximately
+#. the same string lengths
+#: ../calculator.py:1077
+msgid "Energy:                 "
+msgstr ""
+
+#: ../calculator.py:1079
+msgid " eV   Sum of eigenvalues:  "
+msgstr ""
+
+#: ../calculator.py:1081 ../calculator.py:1559
+msgid " eV"
+msgstr ""
+
+#: ../calculator.py:1082
+msgid "Electron density: "
+msgstr ""
+
+#: ../calculator.py:1084
+msgid "        Force convergence:  "
+msgstr ""
+
+#: ../calculator.py:1086
+msgid " eV/Ang  "
+msgstr ""
+
+#: ../calculator.py:1099 ../calculator.py:1570
+msgid "Additional keywords: "
+msgstr ""
+
+#. run command and species defaults:
+#: ../calculator.py:1113
+msgid "FHI-aims execution command: "
+msgstr ""
+
+#: ../calculator.py:1115 ../calculator.py:1587
+msgid "Directory for species defaults: "
+msgstr ""
+
+#: ../calculator.py:1127 ../calculator.py:1595
+msgid "Set Defaults"
+msgstr ""
+
+#: ../calculator.py:1129
+msgid "Import control.in"
+msgstr ""
+
+#: ../calculator.py:1131
+msgid "Export control.in"
+msgstr ""
+
+#: ../calculator.py:1317
+msgid "Export parameters ... "
+msgstr ""
+
+#: ../calculator.py:1337
+msgid "Import control.in file ... "
+msgstr ""
+
+#: ../calculator.py:1393 ../calculator.py:1907
+#, python-format
+msgid ""
+"Please use the facilities provided in this window to manipulate the keyword: "
+"%s!"
+msgstr ""
+
+#: ../calculator.py:1396
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/aims.py."
+msgstr ""
+
+#: ../calculator.py:1469
+msgid "VASP parameters"
+msgstr ""
+
+#: ../calculator.py:1475
+msgid "Periodic geometry, unit cell is: \n"
+msgstr ""
+
+#: ../calculator.py:1527
+msgid ")    Cutoff: "
+msgstr ""
+
+#: ../calculator.py:1528
+msgid "    Precision: "
+msgstr ""
+
+#: ../calculator.py:1530
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å       "
+msgstr ""
+
+#: ../calculator.py:1546
+msgid "Smearing: "
+msgstr ""
+
+#: ../calculator.py:1548
+msgid " order: "
+msgstr ""
+
+#: ../calculator.py:1550
+msgid " width: "
+msgstr ""
+
+#: ../calculator.py:1557
+msgid "Self-consistency convergence: "
+msgstr ""
+
+#. run command and location of POTCAR files:
+#: ../calculator.py:1583
+msgid "VASP execution command: "
+msgstr ""
+
+#: ../calculator.py:1597
+msgid "Import VASP files"
+msgstr ""
+
+#: ../calculator.py:1599
+msgid "Export VASP files"
+msgstr ""
+
+#: ../calculator.py:1810
+msgid "<b>WARNING:</b> cutoff energy is lower than recommended minimum!"
+msgstr ""
+
+#: ../calculator.py:1862
+msgid "Import VASP input files: choose directory ... "
+msgstr ""
+
+#: ../calculator.py:1877
+msgid "Export VASP input files: choose directory ... "
+msgstr ""
+
+#: ../calculator.py:1910
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/vasp.py."
+msgstr ""
+
+#: ../colors.py:24
+msgid "Colors"
+msgstr ""
+
+#. Upper left: Choose how the atoms are colored.
+#: ../colors.py:41
+msgid "Choose how the atoms are colored:"
+msgstr ""
+
+#: ../colors.py:43
+msgid "By atomic number, default \"jmol\" colors"
+msgstr ""
+
+#: ../colors.py:45
+msgid "By atomic number, user specified"
+msgstr ""
+
+#: ../colors.py:46
+msgid "By tag"
+msgstr ""
+
+#: ../colors.py:47
+msgid "By force"
+msgstr ""
+
+#: ../colors.py:48
+msgid "By velocity"
+msgstr ""
+
+#: ../colors.py:49
+msgid "Manually specified"
+msgstr ""
+
+#: ../colors.py:50
+msgid "All the same color"
+msgstr ""
+
+#. Now fill in the box for additional information in case the force is used.
+#: ../colors.py:60
+msgid "This should not be displayed!"
+msgstr ""
+
+#: ../colors.py:65 ../colors.py:82 ../rotate.py:25
+msgid "Update"
+msgstr ""
+
+#: ../colors.py:67 ../colors.py:84
+msgid "Min: "
+msgstr ""
+
+#: ../colors.py:69 ../colors.py:86
+msgid "  Max: "
+msgstr ""
+
+#: ../colors.py:71 ../colors.py:88
+msgid "  Steps: "
+msgstr ""
+
+#: ../colors.py:95
+msgid "Create a color scale:"
+msgstr ""
+
+#: ../colors.py:98
+msgid "Black - white"
+msgstr ""
+
+#: ../colors.py:99
+msgid "Black - red - yellow - white"
+msgstr ""
+
+#: ../colors.py:100
+msgid "Black - green - white"
+msgstr ""
+
+#: ../colors.py:101
+msgid "Black - blue - cyan"
+msgstr ""
+
+#: ../colors.py:102
+msgid "Hue"
+msgstr ""
+
+#: ../colors.py:103
+msgid "Named colors"
+msgstr ""
+
+#: ../colors.py:109
+msgid "Create"
+msgstr ""
+
+#: ../colors.py:367
+#, python-format
+msgid "Max force: %.2f (this frame), %.2f (all frames)"
+msgstr ""
+
+#: ../colors.py:369
+#, python-format
+msgid "Max force: %.2f."
+msgstr ""
+
+#: ../colors.py:383
+#, python-format
+msgid "Max velocity: %.2f (this frame), %.2f (all frames)"
+msgstr ""
+
+#: ../colors.py:385
+#, python-format
+msgid "Max velocity: %.2f."
+msgstr ""
+
+#: ../colors.py:426
+msgid "ERROR"
+msgstr ""
+
+#: ../colors.py:455
+msgid "ERR"
+msgstr ""
+
+#: ../colors.py:542
+msgid "Incorrect color specification"
+msgstr ""
+
+#: ../constraints.py:13 ../widgets.py:89
+msgid "Constraints"
+msgstr ""
+
+#: ../constraints.py:15 ../constraints.py:18 ../settings.py:17
+#: ../widgets.py:91 ../widgets.py:94
+msgid "Constrain"
+msgstr ""
+
+#: ../constraints.py:16 ../settings.py:20 ../settings.py:35 ../widgets.py:92
+msgid " selected atoms"
+msgstr ""
+
+#: ../constraints.py:19 ../widgets.py:95
+msgid " immobile atoms:"
+msgstr ""
+
+#: ../constraints.py:21
+msgid "Unconstrain"
+msgstr ""
+
+#: ../constraints.py:22
+msgid " selected atoms:"
+msgstr ""
+
+#: ../constraints.py:24
+msgid "Clear constraints"
+msgstr ""
+
+#: ../constraints.py:26 ../dft.py:29 ../settings.py:53 ../widgets.py:60
+#: ../widgets.py:99
+msgid "Close"
+msgstr ""
+
+#: ../crystal.py:16
+msgid ""
+"  Use this dialog to create crystal lattices. First select the structure,\n"
+"  either from a set of common crystal structures, or by space group "
+"description.\n"
+"  Then add all other lattice parameters.\n"
+"\n"
+"  If an experimental crystal structure is available for an atom, you can\n"
+"  look up the crystal type and lattice constant, otherwise you have to "
+"specify it\n"
+"  yourself.  "
+msgstr ""
+
+#: ../crystal.py:33
+#, python-format
+msgid " %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>"
+msgstr ""
+
+#: ../crystal.py:58
+msgid "Create Bulk Crystal by Spacegroup"
+msgstr ""
+
+#: ../crystal.py:72
+msgid "Number: 1"
+msgstr ""
+
+#: ../crystal.py:73
+msgid "Lattice: "
+msgstr ""
+
+#: ../crystal.py:73
+msgid "\tSpace group: "
+msgstr ""
+
+#: ../crystal.py:77
+msgid "Size: x: "
+msgstr ""
+
+#: ../crystal.py:78 ../crystal.py:138
+msgid "  y: "
+msgstr ""
+
+#: ../crystal.py:79 ../crystal.py:139
+msgid "  z: "
+msgstr ""
+
+#: ../crystal.py:80 ../surfaceslab.py:127 ../surfaceslab.py:129
+msgid " unit cells"
+msgstr ""
+
+#: ../crystal.py:92 ../crystal.py:96 ../crystal.py:100 ../crystal.py:104
+#: ../crystal.py:108 ../crystal.py:112
+msgid "free"
+msgstr ""
+
+#: ../crystal.py:93 ../crystal.py:102
+msgid "equals b"
+msgstr ""
+
+#: ../crystal.py:94 ../crystal.py:98
+msgid "equals c"
+msgstr ""
+
+#: ../crystal.py:95 ../crystal.py:99 ../crystal.py:103 ../crystal.py:107
+#: ../crystal.py:111 ../crystal.py:115
+msgid "fixed"
+msgstr ""
+
+#: ../crystal.py:97 ../crystal.py:101
+msgid "equals a"
+msgstr ""
+
+#: ../crystal.py:105 ../crystal.py:114
+msgid "equals beta"
+msgstr ""
+
+#: ../crystal.py:106 ../crystal.py:110
+msgid "equals gamma"
+msgstr ""
+
+#: ../crystal.py:109 ../crystal.py:113
+msgid "equals alpha"
+msgstr ""
+
+#: ../crystal.py:119
+msgid "Lattice parameters"
+msgstr ""
+
+#: ../crystal.py:120
+msgid "\t\ta:\t"
+msgstr ""
+
+#: ../crystal.py:121
+msgid "\talpha:\t"
+msgstr ""
+
+#: ../crystal.py:122
+msgid "\t\tb:\t"
+msgstr ""
+
+#: ../crystal.py:123
+msgid "\tbeta:\t"
+msgstr ""
+
+#: ../crystal.py:124
+msgid "\t\tc:\t"
+msgstr ""
+
+#: ../crystal.py:125
+msgid "\tgamma:\t"
+msgstr ""
+
+#: ../crystal.py:126 ../surfaceslab.py:99
+msgid "Get from database"
+msgstr ""
+
+#: ../crystal.py:131
+msgid "Basis: "
+msgstr ""
+
+#: ../crystal.py:137
+msgid "  Element:\t"
+msgstr ""
+
+#: ../crystal.py:137
+msgid "\tx: "
+msgstr ""
+
+#: ../crystal.py:157
+msgid "Creating a crystal."
+msgstr ""
+
+#: ../crystal.py:202
+#, python-format
+msgid "Symbol: %s"
+msgstr ""
+
+#: ../crystal.py:207
+#, python-format
+msgid "Number: %s"
+msgstr ""
+
+#: ../crystal.py:210
+msgid "Invalid Spacegroup!"
+msgstr ""
+
+#: ../crystal.py:336 ../crystal.py:339
+msgid "Please specify a consistent set of atoms."
+msgstr ""
+
+#: ../crystal.py:348 ../graphene.py:230 ../nanoparticle.py:613
+#: ../nanotube.py:139 ../surfaceslab.py:248
+msgid "No valid atoms."
+msgstr ""
+
+#: ../crystal.py:465
+msgid "Can't find lattice definition!"
+msgstr ""
+
+#: ../debug.py:11
+msgid "Debug"
+msgstr ""
+
+#: ../dft.py:13
+msgid "DFT"
+msgstr ""
+
+#: ../dft.py:19
+msgid "XC-functional: "
+msgstr ""
+
+#: ../dft.py:23 ../repeat.py:16
+msgid "Repeat atoms:"
+msgstr ""
+
+#: ../energyforces.py:11
+msgid "Output:"
+msgstr ""
+
+#: ../energyforces.py:41
+msgid "Save output"
+msgstr ""
+
+#: ../energyforces.py:57
+msgid "Potential energy and forces"
+msgstr ""
+
+#: ../energyforces.py:61
+msgid "Calculate potential energy and the force on all atoms"
+msgstr ""
+
+#: ../energyforces.py:65
+msgid "Write forces on the atoms"
+msgstr ""
+
+#: ../energyforces.py:82
+msgid "Potential Energy:\n"
+msgstr ""
+
+#: ../energyforces.py:83
+#, python-format
+msgid "  %8.2f eV\n"
+msgstr ""
+
+#: ../energyforces.py:84
+#, python-format
+msgid ""
+"  %8.4f eV/atom\n"
+"\n"
+msgstr ""
+
+#: ../energyforces.py:86
+msgid "Forces:\n"
+msgstr ""
+
+#: ../execute.py:23
+msgid ""
+"\n"
+"    Global commands work on all frames or only on the current frame\n"
+"    - Assignment of a global variable may not reference a local one\n"
+"    - use 'Current frame' switch to switch off application to all frames\n"
+"    <c>e</c>:\t\ttotal energy of one frame\n"
+"    <c>fmax</c>:\tmaximal force in one frame\n"
+"    <c>A</c>:\tunit cell\n"
+"    <c>E</c>:\t\ttotal energy array of all frames\n"
+"    <c>F</c>:\t\tall forces in one frame\n"
+"    <c>M</c>:\tall magnetic moments\n"
+"    <c>R</c>:\t\tall atomic positions\n"
+"    <c>S</c>:\tall selected atoms (boolean array)\n"
+"    <c>D</c>:\tall dynamic atoms (boolean array)\n"
+"    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atom commands work on each atom (or a selection) individually\n"
+"    - these can use global commands on the RHS of an equation\n"
+"    - use 'selected atoms only' to restrict application of command\n"
+"    <c>x,y,z</c>:\tatomic coordinates\n"
+"    <c>r,g,b</c>:\tatom display color, range is [0..1]\n"
+"    <c>rad</c>:\tatomic radius for display\n"
+"    <c>s</c>:\t\tatom is selected\n"
+"    <c>d</c>:\t\tatom is movable\n"
+"    <c>f</c>:\t\tforce\n"
+"    <c>Z</c>:\tatomic number\n"
+"    <c>m</c>:\tmagnetic moment\n"
+"    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Special commands and objects:\n"
+"    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame\n"
+"    <c>frame</c>:\tframe number\n"
+"    <c>center</c>:\tcenters the system in its existing unit cell\n"
+"    <c>del S</c>:\tdelete selection\n"
+"    <c>CM</c>:\tcenter of mass\n"
+"    <c>ans[-i]</c>:\tith last calculated result\n"
+"    <c>exec file</c>: executes commands listed in file\n"
+"    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z\n"
+"    <c>gui</c>:\tadvanced: ag window python object\n"
+"    <c>img</c>:\tadvanced: ag images object\n"
+"    "
+msgstr ""
+
+#: ../execute.py:67
+msgid "Expert user mode"
+msgstr ""
+
+#: ../execute.py:80
+msgid "Welcome to the ASE Expert user mode"
+msgstr ""
+
+#: ../execute.py:87
+msgid "Only selected atoms (sa)   "
+msgstr ""
+
+#: ../execute.py:89
+msgid "Only current frame (cf)  "
+msgstr ""
+
+#: ../execute.py:99
+msgid ""
+"Global: Use A, D, E, M, N, R, S, n, frame; Atoms: Use a, f, m, s, x, y, z, "
+"Z     "
+msgstr ""
+
+#: ../execute.py:198
+#, python-format
+msgid "*** WARNING: file does not exist - %s"
+msgstr ""
+
+#: ../execute.py:203
+msgid "*** WARNING: No atoms selected to work with"
+msgstr ""
+
+#: ../execute.py:277
+msgid "*** Only working on selected atoms"
+msgstr ""
+
+#: ../execute.py:279
+msgid "*** Working on all atoms"
+msgstr ""
+
+#: ../execute.py:283
+msgid "*** Only working on current image"
+msgstr ""
+
+#: ../execute.py:285
+msgid "*** Working on all images"
+msgstr ""
+
+#: ../execute.py:301
+msgid "Save Terminal text ..."
+msgstr ""
+
+#: ../graphene.py:15
+msgid ""
+"Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may\n"
+"optionally be saturated with hydrogen (or another element)."
+msgstr ""
+
+#: ../graphene.py:30 ../gui.py:298
+msgid "Graphene"
+msgstr ""
+
+#. Choose structure
+#. The structure and lattice constant
+#. Choose the surface structure
+#: ../graphene.py:37 ../nanoparticle.py:138 ../surfaceslab.py:78
+msgid "Structure: "
+msgstr ""
+
+#: ../graphene.py:39
+msgid "Infinite sheet"
+msgstr ""
+
+#: ../graphene.py:39
+msgid "Unsaturated ribbon"
+msgstr ""
+
+#: ../graphene.py:40
+msgid "Saturated ribbon"
+msgstr ""
+
+#. Orientation
+#: ../graphene.py:47
+msgid "Orientation: "
+msgstr ""
+
+#: ../graphene.py:50
+msgid "zigzag"
+msgstr ""
+
+#: ../graphene.py:50
+msgid "armchair"
+msgstr ""
+
+#: ../graphene.py:66 ../graphene.py:78 ../nanotube.py:46
+msgid "  Bond length: "
+msgstr ""
+
+#. Choose the saturation element and bond length
+#: ../graphene.py:72
+msgid "Saturation: "
+msgstr ""
+
+#: ../graphene.py:75
+msgid "H"
+msgstr ""
+
+#. Size
+#: ../graphene.py:91
+msgid "Width: "
+msgstr ""
+
+#: ../graphene.py:92 ../nanotube.py:65
+msgid "  Length: "
+msgstr ""
+
+#. Vacuum
+#: ../graphene.py:100
+msgid "Vacuum: "
+msgstr ""
+
+#: ../graphene.py:138 ../nanotube.py:96 ../setupwindow.py:32
+msgid "  No element specified!"
+msgstr ""
+
+#: ../graphene.py:231 ../nanoparticle.py:614 ../nanotube.py:140
+#: ../pybutton.py:49 ../surfaceslab.py:249
+msgid "You have not (yet) specified a consistent set of parameters."
+msgstr ""
+
+#: ../graphs.py:19
+msgid "Help for plot ..."
+msgstr ""
+
+#: ../graphs.py:30 ../graphs.py:33
+msgid "Plot"
+msgstr ""
+
+#: ../graphs.py:38
+msgid "clear"
+msgstr ""
+
+#: ../graphs.py:92
+msgid "Save data to file ... "
+msgstr ""
+
+#: ../gtkexcepthook.py:117
+msgid "Bug Detected"
+msgstr ""
+
+#: ../gtkexcepthook.py:121
+msgid "A programming error has been detected."
+msgstr ""
+
+#: ../gtkexcepthook.py:124
+msgid ""
+"It probably isn't fatal, but the details should be reported to the "
+"developers nonetheless."
+msgstr ""
+
+#: ../gtkexcepthook.py:140
+msgid "Report..."
+msgstr ""
+
+#: ../gtkexcepthook.py:144
+msgid "Details..."
+msgstr ""
+
+#: ../gtkexcepthook.py:160
+#, python-format
+msgid ""
+"From: buggy_application\"\n"
+"To: bad_programmer\n"
+"Subject: Exception feedback\n"
+"\n"
+"%s"
+msgstr ""
+
+#. Show details...
+#: ../gtkexcepthook.py:173
+msgid "Bug Details"
+msgstr ""
+
+#: ../gui.py:164
+msgid "_File"
+msgstr ""
+
+#: ../gui.py:165
+msgid "_Edit"
+msgstr ""
+
+#: ../gui.py:166
+msgid "_View"
+msgstr ""
+
+#: ../gui.py:167
+msgid "_Tools"
+msgstr ""
+
+#. TRANSLATORS: Set up (i.e. build) surfaces, nanoparticles, ...
+#: ../gui.py:169
+msgid "_Setup"
+msgstr ""
+
+#: ../gui.py:170
+msgid "_Calculate"
+msgstr ""
+
+#: ../gui.py:171
+msgid "_Help"
+msgstr ""
+
+#: ../gui.py:172
+msgid "_Open"
+msgstr ""
+
+#: ../gui.py:173
+msgid "Create a new file"
+msgstr ""
+
+#: ../gui.py:175
+msgid "_New"
+msgstr ""
+
+#: ../gui.py:176
+msgid "New ase.gui window"
+msgstr ""
+
+#: ../gui.py:178
+msgid "_Save"
+msgstr ""
+
+#: ../gui.py:179
+msgid "Save current file"
+msgstr ""
+
+#: ../gui.py:181
+msgid "_Quit"
+msgstr ""
+
+#: ../gui.py:182
+msgid "Quit"
+msgstr ""
+
+#: ../gui.py:184
+msgid "Select _all"
+msgstr ""
+
+#: ../gui.py:187
+msgid "_Invert selection"
+msgstr ""
+
+#: ../gui.py:190
+msgid "Select _constrained atoms"
+msgstr ""
+
+#: ../gui.py:193
+msgid "Select _immobile atoms"
+msgstr ""
+
+#: ../gui.py:196
+msgid "_Copy"
+msgstr ""
+
+#: ../gui.py:197
+msgid "Copy current selection and its orientation to clipboard"
+msgstr ""
+
+#: ../gui.py:199
+msgid "_Paste"
+msgstr ""
+
+#: ../gui.py:200
+msgid "Insert current clipboard selection"
+msgstr ""
+
+#: ../gui.py:202
+msgid "_Modify"
+msgstr ""
+
+#: ../gui.py:203
+msgid "Change tags, moments and atom types of the selected atoms"
+msgstr ""
+
+#: ../gui.py:205
+msgid "_Add atoms"
+msgstr ""
+
+#: ../gui.py:206
+msgid "Insert or import atoms and molecules"
+msgstr ""
+
+#: ../gui.py:208
+msgid "_Delete selected atoms"
+msgstr ""
+
+#: ../gui.py:209
+msgid "Delete the selected atoms"
+msgstr ""
+
+#: ../gui.py:211
+msgid "_First image"
+msgstr ""
+
+#: ../gui.py:214
+msgid "_Previous image"
+msgstr ""
+
+#: ../gui.py:217
+msgid "_Next image"
+msgstr ""
+
+#: ../gui.py:220
+msgid "_Last image"
+msgstr ""
+
+#: ../gui.py:223
+msgid "Quick Info ..."
+msgstr ""
+
+#: ../gui.py:226
+msgid "Repeat ..."
+msgstr ""
+
+#: ../gui.py:229
+msgid "Rotate ..."
+msgstr ""
+
+#: ../gui.py:232
+msgid "Colors ..."
+msgstr ""
+
+#. TRANSLATORS: verb
+#: ../gui.py:235
+msgid "Focus"
+msgstr ""
+
+#: ../gui.py:238
+msgid "Zoom in"
+msgstr ""
+
+#: ../gui.py:241
+msgid "Zoom out"
+msgstr ""
+
+#: ../gui.py:244
+msgid "Reset View"
+msgstr ""
+
+#: ../gui.py:247
+msgid "Settings ..."
+msgstr ""
+
+#: ../gui.py:250
+msgid "VMD"
+msgstr ""
+
+#: ../gui.py:253
+msgid "RasMol"
+msgstr ""
+
+#: ../gui.py:256
+msgid "xmakemol"
+msgstr ""
+
+#: ../gui.py:259
+msgid "avogadro"
+msgstr ""
+
+#: ../gui.py:262
+msgid "Graphs ..."
+msgstr ""
+
+#: ../gui.py:265
+msgid "Movie ..."
+msgstr ""
+
+#: ../gui.py:268
+msgid "Expert mode ..."
+msgstr ""
+
+#: ../gui.py:271
+msgid "Constraints ..."
+msgstr ""
+
+#: ../gui.py:274
+msgid "Render scene ..."
+msgstr ""
+
+#: ../gui.py:277
+msgid "DFT ..."
+msgstr ""
+
+#: ../gui.py:280
+msgid "NE_B"
+msgstr ""
+
+#: ../gui.py:283
+msgid "B_ulk Modulus"
+msgstr ""
+
+#: ../gui.py:286
+msgid "_Bulk Crystal"
+msgstr ""
+
+#: ../gui.py:287
+msgid "Create a bulk crystal with arbitrary orientation"
+msgstr ""
+
+#: ../gui.py:289
+msgid "_Surface slab"
+msgstr ""
+
+#: ../gui.py:290
+msgid "Create the most common surfaces"
+msgstr ""
+
+#: ../gui.py:292
+msgid "_Nanoparticle"
+msgstr ""
+
+#: ../gui.py:293
+msgid "Create a crystalline nanoparticle"
+msgstr ""
+
+#: ../gui.py:295
+msgid "Nano_tube"
+msgstr ""
+
+#: ../gui.py:296
+msgid "Create a nanotube"
+msgstr ""
+
+#: ../gui.py:299
+msgid "Create a graphene sheet or nanoribbon"
+msgstr ""
+
+#: ../gui.py:301
+msgid "Set _Calculator"
+msgstr ""
+
+#: ../gui.py:302
+msgid "Set a calculator used in all calculation modules"
+msgstr ""
+
+#: ../gui.py:304
+msgid "_Energy and Forces"
+msgstr ""
+
+#: ../gui.py:305
+msgid "Calculate energy and forces"
+msgstr ""
+
+#: ../gui.py:307
+msgid "Energy _Minimization"
+msgstr ""
+
+#: ../gui.py:308
+msgid "Minimize the energy"
+msgstr ""
+
+#: ../gui.py:310
+msgid "Scale system"
+msgstr ""
+
+#: ../gui.py:311
+msgid "Deform system by scaling it"
+msgstr ""
+
+#: ../gui.py:313
+msgid "_About"
+msgstr ""
+
+#: ../gui.py:316
+msgid "Webpage ..."
+msgstr ""
+
+#: ../gui.py:317
+msgid "Debug ..."
+msgstr ""
+
+#: ../gui.py:319
+msgid "Show _unit cell"
+msgstr ""
+
+#: ../gui.py:323
+msgid "Show _axes"
+msgstr ""
+
+#: ../gui.py:327
+msgid "Show _bonds"
+msgstr ""
+
+#: ../gui.py:331
+msgid "_Move atoms"
+msgstr ""
+
+#: ../gui.py:335
+msgid "_Rotate atoms"
+msgstr ""
+
+#: ../gui.py:339
+msgid "Orien_t atoms"
+msgstr ""
+
+#: ../gui.py:351
+#, python-format
+msgid "building menus failed: %s"
+msgstr ""
+
+#: ../gui.py:620 ../gui.py:1016 ../gui.py:1076
+msgid "Open ..."
+msgstr ""
+
+#: ../gui.py:624 ../gui.py:1019
+msgid "<<filename>>"
+msgstr ""
+
+#: ../gui.py:756
+msgid "Add atoms"
+msgstr ""
+
+#: ../gui.py:759
+msgid "Paste"
+msgstr ""
+
+#: ../gui.py:765
+msgid "Insert atom or molecule"
+msgstr ""
+
+#: ../gui.py:766 ../gui.py:883
+msgid "Tag"
+msgstr ""
+
+#: ../gui.py:767 ../gui.py:884
+msgid "Moment"
+msgstr ""
+
+#: ../gui.py:768
+msgid "Position"
+msgstr ""
+
+#: ../gui.py:793
+msgid "_Load molecule"
+msgstr ""
+
+#: ../gui.py:797 ../gui.py:899
+msgid "_OK"
+msgstr ""
+
+#: ../gui.py:801 ../gui.py:903
+msgid "_Cancel"
+msgstr ""
+
+#: ../gui.py:876
+msgid "Modify"
+msgstr ""
+
+#: ../gui.py:882
+msgid "Atom"
+msgstr ""
+
+#: ../gui.py:927
+msgid "Confirmation"
+msgstr ""
+
+#: ../gui.py:931
+msgid "Delete selected atom?"
+msgid_plural "Delete selected atoms?"
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../gui.py:938
+msgid "Cancel"
+msgstr ""
+
+#: ../gui.py:1024 ../gui.py:1106
+msgid "Automatic"
+msgstr ""
+
+#: ../gui.py:1025
+msgid "Dacapo netCDF output file"
+msgstr ""
+
+#: ../gui.py:1026
+msgid "Virtual Nano Lab file"
+msgstr ""
+
+#: ../gui.py:1027
+msgid "ASE pickle trajectory"
+msgstr ""
+
+#: ../gui.py:1028 ../gui.py:1119
+msgid "ASE bundle trajectory"
+msgstr ""
+
+#: ../gui.py:1029
+msgid "GPAW text output"
+msgstr ""
+
+#: ../gui.py:1030
+msgid "CUBE file"
+msgstr ""
+
+#: ../gui.py:1031
+msgid "XCrySDen Structure File"
+msgstr ""
+
+#: ../gui.py:1032
+msgid "Dacapo text output"
+msgstr ""
+
+#: ../gui.py:1033
+msgid "XYZ-file"
+msgstr ""
+
+#: ../gui.py:1034
+msgid "VASP POSCAR/CONTCAR file"
+msgstr ""
+
+#: ../gui.py:1035
+msgid "VASP OUTCAR file"
+msgstr ""
+
+#: ../gui.py:1036
+msgid "Protein Data Bank"
+msgstr ""
+
+#: ../gui.py:1037
+msgid "CIF-file"
+msgstr ""
+
+#: ../gui.py:1038
+msgid "FHI-aims geometry file"
+msgstr ""
+
+#: ../gui.py:1039
+msgid "FHI-aims output file"
+msgstr ""
+
+#: ../gui.py:1040
+msgid "TURBOMOLE coord file"
+msgstr ""
+
+#: ../gui.py:1041
+msgid "exciting input"
+msgstr ""
+
+#: ../gui.py:1042
+msgid "WIEN2k structure file"
+msgstr ""
+
+#: ../gui.py:1043
+msgid "DftbPlus input file"
+msgstr ""
+
+#: ../gui.py:1044
+msgid "ETSF format"
+msgstr ""
+
+#: ../gui.py:1045 ../gui.py:1117
+msgid "CASTEP geom file"
+msgstr ""
+
+#: ../gui.py:1046
+msgid "CASTEP output file"
+msgstr ""
+
+#: ../gui.py:1047
+msgid "CASTEP trajectory file"
+msgstr ""
+
+#: ../gui.py:1048
+msgid "DFTBPlus GEN format"
+msgstr ""
+
+#: ../gui.py:1054
+msgid "File type:"
+msgstr ""
+
+#: ../gui.py:1094
+msgid "Save ..."
+msgstr ""
+
+#: ../gui.py:1107
+msgid "XYZ file"
+msgstr ""
+
+#: ../gui.py:1108
+msgid "ASE trajectory"
+msgstr ""
+
+#: ../gui.py:1109
+msgid "PDB file"
+msgstr ""
+
+#: ../gui.py:1110
+msgid "Gaussian cube file"
+msgstr ""
+
+#: ../gui.py:1111
+msgid "Python script"
+msgstr ""
+
+#: ../gui.py:1112
+msgid "VNL file"
+msgstr ""
+
+#: ../gui.py:1113
+msgid "Portable Network Graphics"
+msgstr ""
+
+#: ../gui.py:1114
+msgid "Persistence of Vision"
+msgstr ""
+
+#: ../gui.py:1115
+msgid "Encapsulated PostScript"
+msgstr ""
+
+#: ../gui.py:1116
+msgid "FHI-aims geometry input"
+msgstr ""
+
+#: ../gui.py:1118
+msgid "VASP geometry input"
+msgstr ""
+
+#: ../gui.py:1120
+msgid "cif file"
+msgstr ""
+
+#: ../gui.py:1142
+#, python-format
+msgid "Save current image only (#%d)"
+msgstr ""
+
+#: ../gui.py:1146
+msgid "Slice: "
+msgstr ""
+
+#: ../gui.py:1147
+msgid "Help for slice ..."
+msgstr ""
+
+#: ../gui.py:1159
+msgid "AG INTERNAL ERROR: strange response in Save,"
+msgstr ""
+
+#: ../gui.py:1178
+msgid "Unknown output format!"
+msgstr ""
+
+#: ../gui.py:1179
+#, python-format
+msgid "Use one of: %s"
+msgstr ""
+
+#: ../gui.py:1284
+msgid "Not implemented!"
+msgstr ""
+
+#: ../gui.py:1285
+msgid "do you really need it?"
+msgstr ""
+
+#: ../minimize.py:20
+msgid "Algorithm: "
+msgstr ""
+
+#: ../minimize.py:25 ../progress.py:67
+msgid "Convergence criterion: F<sub>max</sub> = "
+msgstr ""
+
+#: ../minimize.py:30 ../progress.py:70
+msgid "Max. number of steps: "
+msgstr ""
+
+#. Special stuff for MDMin
+#: ../minimize.py:33
+msgid "Pseudo time step: "
+msgstr ""
+
+#: ../minimize.py:54
+msgid "Energy minimization"
+msgstr ""
+
+#: ../minimize.py:58
+msgid "Minimize the energy with respect to the positions."
+msgstr ""
+
+#. Don't catch errors in the function.
+#. Display status message
+#: ../minimize.py:90 ../scaling.py:299
+msgid "Running ..."
+msgstr ""
+
+#. Update display to reflect cancellation of simulation.
+#: ../minimize.py:107
+#, python-format
+msgid "Minimization CANCELLED after %i steps."
+msgstr ""
+
+#: ../minimize.py:113 ../scaling.py:350
+msgid "Out of memory, consider using LBFGS instead"
+msgstr ""
+
+#. Update display to reflect succesful end of simulation.
+#: ../minimize.py:120
+#, python-format
+msgid "Minimization completed in %i steps."
+msgstr ""
+
+#. self.connect('delete_event', self.exit2)
+#: ../movie.py:14
+msgid "Movie"
+msgstr ""
+
+#: ../movie.py:16
+msgid "Image number:"
+msgstr ""
+
+#: ../movie.py:38
+msgid "Play"
+msgstr ""
+
+#: ../movie.py:40
+msgid "Stop"
+msgstr ""
+
+#. TRANSLATORS: This function plays an animation forwards and backwards
+#. alternatingly, e.g. for displaying vibrational movement
+#: ../movie.py:44
+msgid "Rock"
+msgstr ""
+
+#: ../movie.py:60
+msgid " Frame rate: "
+msgstr ""
+
+#: ../movie.py:61
+msgid " Skip frames: "
+msgstr ""
+
+#: ../nanoparticle.py:19
+msgid ""
+"Create a nanoparticle either by specifying the number of layers, or using "
+"the\n"
+"Wulff construction.  Please press the [Help] button for instructions on how "
+"to\n"
+"specify the directions.\n"
+"WARNING: The Wulff construction currently only works with cubic crystals!\n"
+msgstr ""
+
+#: ../nanoparticle.py:26
+msgid ""
+"\n"
+"The nanoparticle module sets up a nano-particle or a cluster with a given\n"
+"crystal structure.\n"
+"\n"
+"1) Select the element, the crystal structure and the lattice constant(s).\n"
+"   The [Get structure] button will find the data for a given element.\n"
+"\n"
+"2) Choose if you want to specify the number of layers in each direction, or "
+"if\n"
+"   you want to use the Wulff construction.  In the latter case, you must "
+"specify\n"
+"   surface energies in each direction, and the size of the cluster.\n"
+"\n"
+"How to specify the directions:\n"
+"------------------------------\n"
+"\n"
+"First time a direction appears, it is interpreted as the entire family of\n"
+"directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of "
+"these\n"
+"directions is specified again, the second specification overrules that "
+"specific\n"
+"direction.  For this reason, the order matters and you can rearrange the\n"
+"directions with the [Up] and [Down] keys.  You can also add a new "
+"direction,\n"
+"remember to press [Add] or it will not be included.\n"
+"\n"
+"Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of "
+"directions,\n"
+"the {111} family and then the (001) direction, overruling the value given "
+"for\n"
+"the whole family of directions.\n"
+msgstr ""
+
+#. Structures:  Abbreviation, name, 4-index (boolean), two lattice const (bool), factory
+#: ../nanoparticle.py:83
+msgid "Face centered cubic (fcc)"
+msgstr ""
+
+#: ../nanoparticle.py:84
+msgid "Body centered cubic (bcc)"
+msgstr ""
+
+#: ../nanoparticle.py:85
+msgid "Simple cubic (sc)"
+msgstr ""
+
+#: ../nanoparticle.py:86
+msgid "Hexagonal closed-packed (hcp)"
+msgstr ""
+
+#: ../nanoparticle.py:87
+msgid "Graphite"
+msgstr ""
+
+#: ../nanoparticle.py:116
+msgid "Nanoparticle"
+msgstr ""
+
+#. Choose the element
+#. Choose the element and bond length
+#. Choose the element
+#: ../nanoparticle.py:126 ../nanotube.py:40 ../surfaceslab.py:69
+msgid "Element: "
+msgstr ""
+
+#: ../nanoparticle.py:130
+msgid "Get structure"
+msgstr ""
+
+#: ../nanoparticle.py:154
+msgid "Lattice constant:  a ="
+msgstr ""
+
+#. Choose specification method
+#: ../nanoparticle.py:171
+msgid "Method: "
+msgstr ""
+
+#: ../nanoparticle.py:173
+msgid "Layer specification"
+msgstr ""
+
+#: ../nanoparticle.py:173
+msgid "Wulff construction"
+msgstr ""
+
+#: ../nanoparticle.py:193
+msgid "Dummy placeholder object"
+msgstr ""
+
+#: ../nanoparticle.py:195
+msgid "Add new direction:"
+msgstr ""
+
+#: ../nanoparticle.py:211
+msgid "Add"
+msgstr ""
+
+#: ../nanoparticle.py:219
+msgid "Set all directions to default values"
+msgstr ""
+
+#: ../nanoparticle.py:227
+msgid "Particle size: "
+msgstr ""
+
+#: ../nanoparticle.py:228 ../nanoparticle.py:265 ../progress.py:196
+msgid "Number of atoms: "
+msgstr ""
+
+#: ../nanoparticle.py:233
+msgid "Volume: "
+msgstr ""
+
+#: ../nanoparticle.py:238
+msgid "ų"
+msgstr ""
+
+#: ../nanoparticle.py:243
+msgid "Rounding: If exact size is not possible, choose the size"
+msgstr ""
+
+#: ../nanoparticle.py:246
+msgid "above  "
+msgstr ""
+
+#: ../nanoparticle.py:247
+msgid "below  "
+msgstr ""
+
+#: ../nanoparticle.py:248
+msgid "closest  "
+msgstr ""
+
+#: ../nanoparticle.py:251
+msgid "Smaller"
+msgstr ""
+
+#: ../nanoparticle.py:252
+msgid "Larger"
+msgstr ""
+
+#: ../nanoparticle.py:267
+msgid "   Approx. diameter: "
+msgstr ""
+
+#: ../nanoparticle.py:271
+msgid "Information about the created cluster:"
+msgstr ""
+
+#. Buttons
+#: ../nanoparticle.py:277 ../nanotube.py:75
+msgid "Creating a nanoparticle."
+msgstr ""
+
+#: ../nanoparticle.py:284
+msgid "Automatic Apply"
+msgstr ""
+
+#: ../nanoparticle.py:332
+msgid "Up"
+msgstr ""
+
+#: ../nanoparticle.py:337
+msgid "Down"
+msgstr ""
+
+#: ../nanoparticle.py:342
+msgid "Delete"
+msgstr ""
+
+#: ../nanoparticle.py:383
+msgid "Surface energies (as energy/area, NOT per atom):"
+msgstr ""
+
+#: ../nanoparticle.py:389
+msgid "Number of layers:"
+msgstr ""
+
+#: ../nanoparticle.py:418
+msgid "At least one index must be non-zero"
+msgstr ""
+
+#: ../nanoparticle.py:421
+msgid "Invalid hexagonal indices"
+msgstr ""
+
+#: ../nanoparticle.py:476 ../surfaceslab.py:218
+msgid "Invalid element."
+msgstr ""
+
+#: ../nanoparticle.py:486
+msgid "Unsupported or unknown structure"
+msgstr ""
+
+#: ../nanoparticle.py:603
+#, python-format
+msgid "%.1f Å"
+msgstr ""
+
+#: ../nanotube.py:14
+msgid ""
+"Set up a Carbon nanotube by specifying the (n,m) roll-up vector.\n"
+"Please note that m <= n.\n"
+"\n"
+"Nanotubes of other elements can be made by specifying the element\n"
+"and bond length."
+msgstr ""
+
+#: ../nanotube.py:33
+msgid "Nanotube"
+msgstr ""
+
+#. Choose the structure.
+#: ../nanotube.py:57
+msgid "Select roll-up vector (n,m) and tube length:"
+msgstr ""
+
+#: ../progress.py:25
+msgid "Progress"
+msgstr ""
+
+#: ../progress.py:32
+msgid "Scaling deformation:"
+msgstr ""
+
+#: ../progress.py:38
+#, python-format
+msgid "Step number %s of %s."
+msgstr ""
+
+#. Minimization progress frame
+#. Box containing frame and spacing
+#: ../progress.py:53
+msgid "Energy minimization:"
+msgstr ""
+
+#: ../progress.py:60
+msgid "Step number: "
+msgstr ""
+
+#: ../progress.py:62
+msgid "F<sub>max</sub>: "
+msgstr ""
+
+#: ../progress.py:102
+msgid "unknown"
+msgstr ""
+
+#: ../progress.py:179
+msgid "Status: "
+msgstr ""
+
+#: ../progress.py:181
+msgid "Iteration: "
+msgstr ""
+
+#: ../progress.py:184
+msgid "log<sub>10</sub>(change):"
+msgstr ""
+
+#: ../progress.py:187
+msgid "Wave functions: "
+msgstr ""
+
+#: ../progress.py:189
+msgid "Density: "
+msgstr ""
+
+#: ../progress.py:191
+msgid "Energy: "
+msgstr ""
+
+#: ../progress.py:194
+msgid "GPAW version: "
+msgstr ""
+
+#: ../progress.py:197
+msgid "N/A"
+msgstr ""
+
+#: ../progress.py:198
+msgid "Memory estimate: "
+msgstr ""
+
+#: ../progress.py:233
+msgid "No info"
+msgstr ""
+
+#: ../progress.py:243
+msgid "Initializing"
+msgstr ""
+
+#: ../progress.py:244
+msgid "Positions:"
+msgstr ""
+
+#: ../progress.py:248
+msgid "Starting calculation"
+msgstr ""
+
+#: ../progress.py:285
+msgid "unchanged"
+msgstr ""
+
+#: ../progress.py:295
+msgid "Self-consistency loop"
+msgstr ""
+
+#: ../progress.py:300
+msgid "Calculating forces"
+msgstr ""
+
+#: ../progress.py:301
+msgid " (converged)"
+msgstr ""
+
+#: ../pybutton.py:37
+msgid "Python"
+msgstr ""
+
+#: ../pybutton.py:48
+msgid "No Python code"
+msgstr ""
+
+#: ../pybutton.py:52
+#, python-format
+msgid ""
+"\n"
+"Title: %(title)s\n"
+"Time: %(time)s\n"
+msgstr ""
+
+#: ../pybutton.py:61
+msgid "ag: Python code"
+msgstr ""
+
+#: ../pybutton.py:65
+msgid "Information:"
+msgstr ""
+
+#: ../pybutton.py:72
+msgid "Python code:"
+msgstr ""
+
+#: ../quickinfo.py:9
+msgid "Single image loaded."
+msgstr ""
+
+#: ../quickinfo.py:10
+#, python-format
+msgid "Image %d loaded (0 - %d)."
+msgstr ""
+
+#: ../quickinfo.py:11
+msgid "Unit cell is fixed."
+msgstr ""
+
+#: ../quickinfo.py:12
+msgid "Unit cell varies."
+msgstr ""
+
+#: ../quickinfo.py:14
+#, python-format
+msgid ""
+"%s\n"
+"\n"
+"Number of atoms: %d.\n"
+"\n"
+"Unit cell:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+msgstr ""
+
+#: ../quickinfo.py:29
+msgid "Quick Info"
+msgstr ""
+
+#: ../quickinfo.py:33
+msgid "No atoms loaded."
+msgstr ""
+
+#: ../render.py:14
+msgid ""
+"    Textures can be used to highlight different parts of\n"
+"    an atomic structure. This window applies the default\n"
+"    texture to the entire structure and optionally\n"
+"    applies a different texture to subsets of atoms that\n"
+"    can be selected using the mouse.\n"
+"    An alternative selection method is based on a boolean\n"
+"    expression in the entry box provided, using the\n"
+"    variables x, y, z, or Z. For example, the expression\n"
+"    Z == 11 and x > 10 and y > 10 \n"
+"    will mark all sodium atoms with x or coordinates \n"
+"    larger than 10. In either case, the button labeled\n"
+"    `Create new texture from selection` will enable\n"
+"    to change the attributes of the current selection. \n"
+"    "
+msgstr ""
+
+#: ../render.py:32
+msgid "Render current view in povray ... "
+msgstr ""
+
+#: ../render.py:36
+#, python-format
+msgid "Rendering %d atoms."
+msgstr ""
+
+#: ../render.py:41
+msgid "Render constraints"
+msgstr ""
+
+#: ../render.py:44
+msgid "Width"
+msgstr ""
+
+#: ../render.py:45
+msgid "     Height"
+msgstr ""
+
+#: ../render.py:52
+msgid "Render unit cell"
+msgstr ""
+
+#: ../render.py:61
+msgid "Line width"
+msgstr ""
+
+#: ../render.py:63
+msgid "Angstrom           "
+msgstr ""
+
+#: ../render.py:73
+msgid "Set"
+msgstr ""
+
+#: ../render.py:75
+msgid "Output basename: "
+msgstr ""
+
+#: ../render.py:77
+msgid "               Filename: "
+msgstr ""
+
+#: ../render.py:88
+msgid " Default texture for atoms: "
+msgstr ""
+
+#: ../render.py:89
+msgid "    transparency: "
+msgstr ""
+
+#: ../render.py:90
+msgid "Define atom selection for new texture:"
+msgstr ""
+
+#: ../render.py:92
+msgid "Select"
+msgstr ""
+
+#: ../render.py:96
+msgid "Create new texture from selection"
+msgstr ""
+
+#: ../render.py:98
+msgid "Help on textures"
+msgstr ""
+
+#: ../render.py:111
+msgid "Camera type: "
+msgstr ""
+
+#: ../render.py:112
+msgid "     Camera distance"
+msgstr ""
+
+#: ../render.py:113
+msgid "Render current frame"
+msgstr ""
+
+#: ../render.py:117
+#, python-format
+msgid "Render all %d frames"
+msgstr ""
+
+#: ../render.py:122
+msgid "Transparent background"
+msgstr ""
+
+#: ../render.py:125
+msgid "Run povray       "
+msgstr ""
+
+#: ../render.py:128
+msgid "Keep povray files       "
+msgstr ""
+
+#: ../render.py:131
+msgid "Show output window"
+msgstr ""
+
+#: ../render.py:212
+msgid "  transparency: "
+msgstr ""
+
+#: ../render.py:218
+msgid ""
+"Can not create new texture! Must have some atoms selected to create a new "
+"material!"
+msgstr ""
+
+#: ../repeat.py:14
+msgid "Repeat"
+msgstr ""
+
+#: ../repeat.py:21
+msgid "Set unit cell"
+msgstr ""
+
+#: ../rotate.py:15
+msgid "Rotate"
+msgstr ""
+
+#: ../rotate.py:17
+msgid "Rotation angles:"
+msgstr ""
+
+#: ../rotate.py:27
+msgid ""
+"Note:\n"
+"You can rotate freely\n"
+"with the mouse, by holding\n"
+"down mouse button 2."
+msgstr ""
+
+#: ../scaling.py:49
+msgid "Homogeneous scaling"
+msgstr ""
+
+#: ../scaling.py:59
+msgid "3D deformation   "
+msgstr ""
+
+#: ../scaling.py:60
+msgid "2D deformation   "
+msgstr ""
+
+#: ../scaling.py:61
+msgid "1D deformation   "
+msgstr ""
+
+#: ../scaling.py:64
+msgid "Bulk"
+msgstr ""
+
+#: ../scaling.py:66
+msgid "xy-plane"
+msgstr ""
+
+#: ../scaling.py:68
+msgid "xz-plane"
+msgstr ""
+
+#: ../scaling.py:70
+msgid "yz-plane"
+msgstr ""
+
+#: ../scaling.py:72
+msgid "x-axis"
+msgstr ""
+
+#: ../scaling.py:74
+msgid "y-axis"
+msgstr ""
+
+#: ../scaling.py:76
+msgid "z-axis"
+msgstr ""
+
+#: ../scaling.py:89
+msgid "Allow deformation along non-periodic directions."
+msgstr ""
+
+#. Parameters for the deformation
+#: ../scaling.py:94
+msgid "Deformation:"
+msgstr ""
+
+#: ../scaling.py:100
+msgid "Maximal scale factor: "
+msgstr ""
+
+#: ../scaling.py:103
+msgid "Scale offset: "
+msgstr ""
+
+#: ../scaling.py:106
+msgid "Number of steps: "
+msgstr ""
+
+#: ../scaling.py:107
+msgid "Only positive deformation"
+msgstr ""
+
+#. Atomic relaxations
+#: ../scaling.py:112
+msgid "Atomic relaxations:"
+msgstr ""
+
+#: ../scaling.py:116
+msgid "On   "
+msgstr ""
+
+#: ../scaling.py:117
+msgid "Off"
+msgstr ""
+
+#. Results
+#: ../scaling.py:128
+msgid "Results:"
+msgstr ""
+
+#: ../scaling.py:130
+msgid "Keep original configuration"
+msgstr ""
+
+#: ../scaling.py:132
+msgid "Load optimal configuration"
+msgstr ""
+
+#: ../scaling.py:134
+msgid "Load all configurations"
+msgstr ""
+
+#: ../scaling.py:143
+msgid "Strain\t\tEnergy [eV]"
+msgstr ""
+
+#: ../scaling.py:144
+msgid "Fit:"
+msgstr ""
+
+#: ../scaling.py:148
+msgid "2nd"
+msgstr ""
+
+#: ../scaling.py:149
+msgid "3rd"
+msgstr ""
+
+#: ../scaling.py:153
+msgid "Order of fit: "
+msgstr ""
+
+#. Update display to reflect cancellation of simulation.
+#: ../scaling.py:346
+msgid "Calculation CANCELLED."
+msgstr ""
+
+#. Update display to reflect succesful end of simulation.
+#: ../scaling.py:357
+msgid "Calculation completed."
+msgstr ""
+
+#: ../scaling.py:380
+msgid "No trustworthy minimum: Old configuration kept."
+msgstr ""
+
+#: ../scaling.py:420
+#, python-format
+msgid ""
+"Insufficent data for a fit\n"
+"(only %i data points)\n"
+msgstr ""
+
+#: ../scaling.py:424
+msgid ""
+"REVERTING TO 2ND ORDER FIT\n"
+"(only 3 data points)\n"
+"\n"
+msgstr ""
+
+#: ../scaling.py:440
+msgid "No minimum found!"
+msgstr ""
+
+#: ../scaling.py:454
+msgid ""
+"\n"
+"WARNING: Minimum is outside interval\n"
+msgstr ""
+
+#: ../scaling.py:455
+msgid "It is UNRELIABLE!\n"
+msgstr ""
+
+#: ../settings.py:16
+msgid "Constraints:"
+msgstr ""
+
+#: ../settings.py:19
+msgid "release"
+msgstr ""
+
+#: ../settings.py:23
+msgid "Constrain immobile atoms"
+msgstr ""
+
+#: ../settings.py:25
+msgid "Clear all constraints"
+msgstr ""
+
+#: ../settings.py:31
+msgid "Visibility:"
+msgstr ""
+
+#: ../settings.py:32
+msgid "Hide"
+msgstr ""
+
+#: ../settings.py:34
+msgid "show"
+msgstr ""
+
+#: ../settings.py:38
+msgid "View all atoms"
+msgstr ""
+
+#: ../settings.py:44
+msgid "Miscellaneous:"
+msgstr ""
+
+#: ../settings.py:47
+msgid "Scale atomic radii:"
+msgstr ""
+
+#. A close button
+#: ../settings.py:52
+msgid "\n"
+msgstr ""
+
+#: ../setupwindow.py:51
+msgid "No crystal structure data"
+msgstr ""
+
+#: ../setupwindow.py:62
+msgid "  ERROR: Invalid element!"
+msgstr ""
+
+#: ../simulation.py:24
+msgid " (rerun simulation)"
+msgstr ""
+
+#: ../simulation.py:25
+msgid " (continue simulation)"
+msgstr ""
+
+#: ../simulation.py:27
+msgid "Select starting configuration:"
+msgstr ""
+
+#: ../simulation.py:32
+#, python-format
+msgid "There are currently %i configurations loaded."
+msgstr ""
+
+#: ../simulation.py:36
+msgid "Choose which one to use as the initial configuration"
+msgstr ""
+
+#: ../simulation.py:40
+#, python-format
+msgid "The first configuration %s."
+msgstr ""
+
+#: ../simulation.py:43
+msgid "Configuration number "
+msgstr ""
+
+#: ../simulation.py:49
+#, python-format
+msgid "The last configuration %s."
+msgstr ""
+
+#: ../simulation.py:85
+msgid "Run"
+msgstr ""
+
+#: ../simulation.py:105
+msgid "No calculator: Use Calculate/Set Calculator on the menu."
+msgstr ""
+
+#: ../status.py:37 ../status.py:39
+msgid "Tip for status box ..."
+msgstr ""
+
+#. TRANSLATORS: mom refers to magnetic moment
+#: ../status.py:63
+#, python-format
+msgid " tag=%(tag)s mom=%(mom)1.2f"
+msgstr ""
+
+#: ../status.py:104
+msgid "dihedral"
+msgstr ""
+
+#: ../surfaceslab.py:14
+msgid ""
+"  Use this dialog to create surface slabs.  Select the element by\n"
+"writing the chemical symbol or the atomic number in the box.  Then\n"
+"select the desired surface structure.  Note that some structures can\n"
+"be created with an othogonal or a non-orthogonal unit cell, in these\n"
+"cases the non-orthogonal unit cell will contain fewer atoms.\n"
+"\n"
+"  If the structure matches the experimental crystal structure, you can\n"
+"look up the lattice constant, otherwise you have to specify it\n"
+"yourself."
+msgstr ""
+
+#. Name, structure, orthogonal, support-nonorthogonal, function
+#: ../surfaceslab.py:26
+msgid "FCC(100)"
+msgstr ""
+
+#: ../surfaceslab.py:26 ../surfaceslab.py:27 ../surfaceslab.py:28
+#: ../surfaceslab.py:30
+msgid "fcc"
+msgstr ""
+
+#: ../surfaceslab.py:27
+msgid "FCC(110)"
+msgstr ""
+
+#: ../surfaceslab.py:28
+msgid "FCC(111) non-orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:30
+msgid "FCC(111) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:31
+msgid "BCC(100)"
+msgstr ""
+
+#: ../surfaceslab.py:31 ../surfaceslab.py:32 ../surfaceslab.py:34
+#: ../surfaceslab.py:35 ../surfaceslab.py:37
+msgid "bcc"
+msgstr ""
+
+#: ../surfaceslab.py:32
+msgid "BCC(110) non-orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:34
+msgid "BCC(110) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:35
+msgid "BCC(111) non-orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:37
+msgid "BCC(111) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:38
+msgid "HCP(0001) non-orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:38 ../surfaceslab.py:40 ../surfaceslab.py:41
+msgid "hcp"
+msgstr ""
+
+#: ../surfaceslab.py:40
+msgid "HCP(0001) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:41
+msgid "HCP(10-10) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:43
+msgid "DIAMOND(100) orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:43 ../surfaceslab.py:45
+msgid "diamond"
+msgstr ""
+
+#: ../surfaceslab.py:45
+msgid "DIAMOND(111) non-orthogonal"
+msgstr ""
+
+#: ../surfaceslab.py:60
+msgid "Surface"
+msgstr ""
+
+#: ../surfaceslab.py:90
+msgid "Lattice constant: "
+msgstr ""
+
+#: ../surfaceslab.py:97
+msgid "a:"
+msgstr ""
+
+#: ../surfaceslab.py:109
+#, python-format
+msgid "(%.1f %% of ideal)"
+msgstr ""
+
+#: ../surfaceslab.py:126
+msgid "Size: \tx: "
+msgstr ""
+
+#: ../surfaceslab.py:128
+msgid "\t\ty: "
+msgstr ""
+
+#: ../surfaceslab.py:130
+msgid "      \t\tz: "
+msgstr ""
+
+#: ../surfaceslab.py:131
+msgid " layers,  "
+msgstr ""
+
+#: ../surfaceslab.py:132
+msgid " Å vacuum"
+msgstr ""
+
+#: ../surfaceslab.py:133
+msgid "\t\tNo size information yet."
+msgstr ""
+
+#. Buttons
+#: ../surfaceslab.py:142
+msgid "Creating a surface slab."
+msgstr ""
+
+#: ../surfaceslab.py:212
+#, python-format
+msgid "%i atoms."
+msgstr ""
+
+#: ../surfaceslab.py:224
+msgid "No structure specified!"
+msgstr ""
+
+#: ../surfaceslab.py:233
+#, python-format
+msgid "%(struct)s lattice constant unknown for %(element)s."
+msgstr ""
+
+#: ../widgets.py:53 ../widgets.py:80
+msgid "Help"
+msgstr ""
+
+#: ../widgets.py:97
+msgid "Clear constraint"
+msgstr ""
diff --git a/ase/gui/po/da_DK/LC_MESSAGES/ag.po b/ase/gui/po/da_DK/LC_MESSAGES/ag.po
new file mode 100644
index 0000000..b53a4d7
--- /dev/null
+++ b/ase/gui/po/da_DK/LC_MESSAGES/ag.po
@@ -0,0 +1,3167 @@
+# Danish translations for ASE package
+# Danske oversættelser for pakken ASE.
+# Copyright (C) 2011 CAMD
+# This file is distributed under the same license as the ASE package.
+#
+# Ask Hjorth Larsen <askhl at fysik.dtu.dk>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ase-3.5.2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-02-01 16:08+0100\n"
+"PO-Revision-Date: 2012-02-15 00:54+0100\n"
+"Last-Translator: Ask Hjorth Larsen <askhl at fysik.dtu.dk>\n"
+"Language-Team: Danish <dansk at dansk-gruppen.dk>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: da\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../ag.py:130
+msgid ""
+"\n"
+"An exception occurred!  Please report the issue to\n"
+"ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if\n"
+"it was a user error, so that a better error message can be provided\n"
+"next time."
+msgstr ""
+"\n"
+"Der opstod en undtagelse!  Rapportér venligst dette problem til \n"
+"ase-developers at listserv.fysik.dtu.dk - mange tak!  Rapportér også gerne "
+"dette\n"
+"hvis det var en brugerfejl, så der kan gives en bedre fejlmeddelelse næste\n"
+"gang."
+
+#. Asap and GPAW may be imported if selected.
+#: ../calculator.py:18
+msgid ""
+"To make most calculations on the atoms, a Calculator object must first\n"
+"be associated with it.  ASE supports a number of calculators, supporting\n"
+"different elements, and implementing different physical models for the\n"
+"interatomic interactions."
+msgstr ""
+"For at kunne foretage de fleste typer atomare beregninger, skal der\n"
+"først tilknyttes et beregnerobject (Calculator).  ASE tilbyder\n"
+"adskillige beregnere, som understøtter forskellige grundstoffer, og\n"
+"implementerer forskellige fysiske modeller for atomernes vekselvirkning."
+
+#. Informational text about the calculators
+#: ../calculator.py:26
+msgid ""
+"The Lennard-Jones pair potential is one of the simplest\n"
+"possible models for interatomic interactions, mostly\n"
+"suitable for noble gasses and model systems.\n"
+"\n"
+"Interactions are described by an interaction length and an\n"
+"interaction strength."
+msgstr ""
+"Lennard-Jones-parpotentialet er en af de simpleste mulige modeller for\n"
+"atomare interaktioner, og er især nyttigt til ædelgasser og\n"
+"modelsystemer.\n"
+"\n"
+"Interaktionerne beskrives ved en interaktionslængde og en\n"
+"interaktionsstyrke."
+
+#: ../calculator.py:35
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au, the Al potential is however not suitable for materials\n"
+"science application, as the stacking fault energy is wrong.\n"
+"\n"
+"A number of parameter sets are provided.\n"
+"\n"
+"<b>Default parameters:</b>\n"
+"\n"
+"The default EMT parameters, as published in K. W. Jacobsen,\n"
+"P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternative Cu, Ag and Au:</b>\n"
+"\n"
+"An alternative set of parameters for Cu, Ag and Au,\n"
+"reoptimized to experimental data including the stacking\n"
+"fault energies by Torben Rasmussen (partly unpublished).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parameters for Ruthenium, as published in J. Gavnholt and\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metallic glasses:</b>\n"
+"\n"
+"Parameters for MgCu and CuZr metallic glasses. MgCu\n"
+"parameters are in N. P. Bailey, J. Schiøtz and\n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).\n"
+"CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+msgstr ""
+"EMT-potentialet er et mangepartikelpotential, som giver en god\n"
+"beskrivelse af de sene overgangsmetaller som danner FCC-strukturer.\n"
+"Grundstofferne som beskrives af hoveddelen af EMT-parametrene er Al,\n"
+"Ni, Cu, Pd, Ag, Pt og Au.  Dog er Al-potentialet ikke egnet til\n"
+"anvendelse i materialevidenskab, da energien for fejl i krystalstrukturen\n"
+"er forkert.\n"
+"\n"
+"Der medfølger en række standardparametre.\n"
+"\n"
+"<b>Standardparametre:</b>\n"
+"\n"
+"Standardparametrene som udgivet i K. W. Jacobsen,\n"
+"P. Stoltze og J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternativ Cu, Ag og Au:</b>\n"
+"\n"
+"Et alternativt sæt parametre for Cu, Ag og Au, genoptimeret til\n"
+"eksperimentelle data inklusive energier for krystalfejl af Torben\n"
+"Rasmussen (delvis upubliceret).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parametre for ruthenium som udgivet i  J. Gavnholt og\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metalglas:</b>\n"
+"\n"
+"Parametre for MgCu- og CuZr-metalglas. MgCu-parametrene findes i\n"
+"N. P. Bailey, J. Schiøtz anog K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</"
+"b>, \n"
+"144205 (2004).\n"
+"CuZr findes i A. Paduraru, A. Kenoufi, N. P. Bailey og\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+
+#: ../calculator.py:70
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au.  In addition, this implementation allows for the use of\n"
+"H, N, O and C adatoms, although the description of these is\n"
+"most likely not very good.\n"
+"\n"
+"<b>This is the ASE implementation of EMT.</b> For large\n"
+"simulations the ASAP implementation is more suitable; this\n"
+"implementation is mainly to make EMT available when ASAP is\n"
+"not installed.\n"
+msgstr ""
+"EMT-potentialet er et mangepartikelpotential, som giver en god\n"
+"beskrivelse af de sene overgangsmetaller som danner FCC-strukturer.\n"
+"Grundstofferne som beskrives af hoveddelen af EMT-parametrene er Al,\n"
+"Ni, Cu, Pd, Ag, Pt og Au.  Yderligere tillader denne implementation\n"
+"bruben af H-, N-, O- og C-adatomer, selvom beskrivelsen af disse\n"
+"sandsynligvis er dårlig.\n"
+"\n"
+"<b>Dette er ASE's implementation af EMT.</b> For støre simulationer er\n"
+"ASAP-implementationen bedre; denne implementation bruges hovedsageligt\n"
+"for at tilbyde en EMT-beskrivelse når ASAP ikke er installeret.\n"
+
+#: ../calculator.py:85
+msgid ""
+"The Brenner potential is a reactive bond-order potential for\n"
+"carbon and hydrocarbons.  As a bond-order potential, it takes\n"
+"into account that carbon orbitals can hybridize in different\n"
+"ways, and that carbon can form single, double and triple\n"
+"bonds.  That the potential is reactive means that it can\n"
+"handle gradual changes in the bond order as chemical bonds\n"
+"are formed or broken.\n"
+"\n"
+"The Brenner potential is implemented in Asap, based on a\n"
+"C implentation published at http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"The potential is documented here:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+msgstr ""
+"Brennerpotentialet er et reaktivt bindingsordenspotential til kulstof og "
+"kulbrinter.  Som et bindingsordenspotential tager det højde for at "
+"kulstoforbitaler kan hybridisere på forskellige måder, og at kulstof kan "
+"danne enkelt- dobbelt- og tripelbindinger.  At potentialet er reaktivt "
+"betyder, at det kan beskrive gradvise ændringer i bindingsorden efterhånden "
+"som kemiske bindinger dannes eller brydes.\n"
+"\n"
+"Brennerpotentialet er implementeret i ASAP baseret på en C-implementation "
+"publiceret på siden http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"Potentialet dokumenteres her:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+
+#: ../calculator.py:107
+msgid ""
+"GPAW implements Density Functional Theory using a\n"
+"<b>G</b>rid-based real-space representation of the wave\n"
+"functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave\n"
+"method for handling the core regions.  \n"
+msgstr ""
+"GPAW implementerer tæthedsfunktionalteori med en <b>G</b>itterbaseret\n"
+"repræsentation af bølgefunktioner i det reelle rum, samt\n"
+"<b>P</b>rojector <b>A</b>ugmented <b>W</b>ave-metoden til behandling\n"
+"af regionen omkring atomkerner.  \n"
+
+#: ../calculator.py:114
+msgid ""
+"FHI-aims is an external package implementing density \n"
+"functional theory and quantum chemical methods using \n"
+"all-electron methods and a numeric local orbital basis set. \n"
+"For full details, see http://www.fhi-berlin.mpg.de/aims/ \n"
+"or Comp. Phys. Comm. v180 2175 (2009). The ASE \n"
+"documentation contains information on the keywords and \n"
+"functionalities available within this interface. \n"
+msgstr ""
+"FHI-aims er en ekstern pakke, der implementerer tæthedsfunktionalteori\n"
+"og kvantekemiske metoder ved brug af \"all-electron\"-metoder og et\n"
+"numerisk lokaliseret atomart basissæt. De fulde detaljer kan findes på\n"
+"http://www.fhi-berlin.mpg.de/aims/ eller i Comp. Phys. Comm. v180 2175\n"
+"(2009). ASE-dokumentationen indeholder oplysninger om nøgleord og\n"
+"funktioner, som er tilgængelige i denne grænseflade. \n"
+
+#: ../calculator.py:124
+msgid ""
+"WARNING:\n"
+"Your system seems to have more than zero but less than \n"
+"three periodic dimensions. Please check that this is \n"
+"really what you want to compute. Assuming full \n"
+"3D periodicity for this calculator."
+msgstr ""
+"ADVARSEL:\n"
+"Dit system skal have flere end nul men mindre end tre periodiske\n"
+"dimensioner.  Kontrollér venligst at dette virkelig er hvad du godt\n"
+"vil beregne. Antager fuld 3D-periodicitet for denne beregner."
+
+#: ../calculator.py:131
+msgid ""
+"VASP is an external package implementing density \n"
+"functional functional theory using pseudopotentials \n"
+"or the projector-augmented wave method together \n"
+"with a plane wave basis set. For full details, see\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+msgstr ""
+"VASP er en ekstern pakke, der implementerer tæthedsfunktionalteori med\n"
+"pseudopotentialer eller PAW-metoden (projector augmented wave method)\n"
+"sammen med en planbølgebasis. De fulde detaljer kan findes på\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+
+#: ../calculator.py:140
+msgid "Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+msgstr "Standard (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+
+#: ../calculator.py:141
+msgid "Alternative Cu, Ag and Au"
+msgstr "Alternativ Cu, Ag og Au"
+
+#: ../calculator.py:142
+msgid "Ruthenium"
+msgstr "Ruthenium"
+
+#: ../calculator.py:143
+msgid "CuMg and CuZr metallic glass"
+msgstr "Metallisk glas med CuMg og CuZr"
+
+#: ../calculator.py:158
+msgid "Select calculator"
+msgstr "Vælg beregner"
+
+#: ../calculator.py:164
+msgid "Calculator:"
+msgstr "Beregner:"
+
+#. No calculator (the default)
+#: ../calculator.py:167
+msgid "None"
+msgstr "Ingen"
+
+#: ../calculator.py:172
+msgid "Lennard-Jones (ASAP)"
+msgstr "Lennard-Jones (ASAP)"
+
+#: ../calculator.py:173 ../calculator.py:206 ../calculator.py:215
+#: ../calculator.py:224
+msgid "Setup"
+msgstr "Opsætning"
+
+#: ../calculator.py:180
+msgid "EMT - Effective Medium Theory (ASAP)"
+msgstr "EMT - Effective Medium Theory (ASAP)"
+
+#: ../calculator.py:192
+msgid "EMT - Effective Medium Theory (ASE)"
+msgstr "EMT - Effective Medium Theory (ASE)"
+
+#: ../calculator.py:198
+msgid "Brenner Potential (ASAP)"
+msgstr "Brenner-potentialet (ASAP)"
+
+#: ../calculator.py:204
+msgid "Density Functional Theory (GPAW)"
+msgstr "Tæthedsfunktionalteori (GPAW)"
+
+#: ../calculator.py:213
+msgid "Density Functional Theory (FHI-aims)"
+msgstr "Tæthedsfunktionalteori (FHI-aims)"
+
+#: ../calculator.py:222
+msgid "Density Functional Theory (VASP)"
+msgstr "Tæthedsfunktionalteori (VASP)"
+
+#: ../calculator.py:235
+msgid "Check that the calculator is reasonable."
+msgstr "Kontrollér at beregneren er rimelig."
+
+#: ../calculator.py:298 ../simulation.py:114
+msgid "No atoms present"
+msgstr "Ingen atomer til stede"
+
+#: ../calculator.py:388 ../calculator.py:422 ../calculator.py:456
+msgid "ASAP is not installed. (Failed to import asap3)"
+msgstr "ASAP er ikke installeret. (Kunne ikke importere asap3)"
+
+#: ../calculator.py:391
+msgid "You must set up the Lennard-Jones parameters"
+msgstr "Du skal indstille Lennard-Jones-parametrene"
+
+#: ../calculator.py:396
+msgid "Could not create useful Lennard-Jones calculator."
+msgstr "Kunne ikke oprette en nyttig Lennard-Jones-beregner."
+
+#: ../calculator.py:430
+msgid "Could not attach EMT calculator to the atoms."
+msgstr "Kunne ikke knytte EMT-beregner til atomerne."
+
+#: ../calculator.py:472 ../calculator.py:484
+msgid "GPAW is not installed. (Failed to import gpaw)"
+msgstr "GPAW er ikke installeret. (Kunne ikke importere gpaw)"
+
+#: ../calculator.py:475
+msgid "You must set up the GPAW parameters"
+msgstr "Du skal angive GPAW-parametrene"
+
+#: ../calculator.py:516
+msgid "You must set up the FHI-aims parameters"
+msgstr "Du skal angive FHI-aims-parametrene"
+
+#: ../calculator.py:530
+msgid "You must set up the VASP parameters"
+msgstr "Du skal angive VASP-parametrene"
+
+#: ../calculator.py:554
+#, python-format
+msgid "Element %(sym)s not allowed by the '%(name)s' calculator"
+msgstr "Grundstoffet %(sym)s tillades ikke af \"%(name)s\"-beregneren"
+
+#: ../calculator.py:561
+msgid "Info"
+msgstr "Info"
+
+#: ../calculator.py:577
+msgid "Lennard-Jones parameters"
+msgstr "Lennard-Jones-parametre"
+
+#: ../calculator.py:589
+msgid "Specify the Lennard-Jones parameters here"
+msgstr "Angiv Lennard-Jones-parametrene her"
+
+#: ../calculator.py:592
+msgid "Epsilon (eV):"
+msgstr "Epsilon (eV):"
+
+#: ../calculator.py:596
+msgid "Sigma (Å):"
+msgstr "Sigma (Å):"
+
+#. TRANSLATORS: Shift roughly means adjust (about a potential)
+#: ../calculator.py:600
+msgid "Shift to make smooth at cutoff"
+msgstr "Skift for at blødgøre ved afskæring"
+
+#: ../calculator.py:681
+msgid "GPAW parameters"
+msgstr "GPAW-parametre"
+
+#. label = gtk.Label("Specify the GPAW parameters here")
+#. pack(vbox, [label])
+#. Print some info
+#: ../calculator.py:696 ../calculator.py:985 ../calculator.py:1473
+#, python-format
+msgid "%i atoms.\n"
+msgstr "%i atomer.\n"
+
+#: ../calculator.py:698
+#, python-format
+msgid "Orthogonal unit cell: %.2f x %.2f x %.2f Å."
+msgstr "Ortogonal enhedscelle: %.2f x %.2f x %.2f Å."
+
+#: ../calculator.py:700
+msgid "Non-orthogonal unit cell:\n"
+msgstr "Ikke-ortogonal enhedscelle:\n"
+
+#: ../calculator.py:710 ../calculator.py:1001 ../calculator.py:1488
+msgid "Exchange-correlation functional: "
+msgstr "Udvekslings- og korrelationsfunktional: "
+
+#. Grid spacing
+#: ../calculator.py:714
+msgid "Grid spacing"
+msgstr "Gitterafstand"
+
+#: ../calculator.py:718 ../graphene.py:67 ../graphene.py:79 ../graphene.py:102
+#: ../nanotube.py:47 ../surfaceslab.py:97
+msgid "Å"
+msgstr "Å"
+
+#: ../calculator.py:719
+msgid "Grid points"
+msgstr "Gitterpunkter"
+
+#: ../calculator.py:728
+#, python-format
+msgid "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+msgstr "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+
+#: ../calculator.py:754 ../calculator.py:1016 ../calculator.py:1524
+msgid "k-points  k = ("
+msgstr "k-punkter  k = ("
+
+#: ../calculator.py:758 ../calculator.py:1020
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å"
+msgstr "k-punkter x størrelse:  (%.1f, %.1f, %.1f) Å"
+
+#. Spin polarized
+#: ../calculator.py:763 ../calculator.py:1486
+msgid "Spin polarized"
+msgstr "Spinpolariseret"
+
+#: ../calculator.py:769
+msgid "FD - Finite Difference (grid) mode"
+msgstr "FD - Finite Difference-tilstand (gitter)"
+
+#: ../calculator.py:770
+msgid "LCAO - Linear Combination of Atomic Orbitals"
+msgstr "LCAO - linearkombination af atomare orbitaler"
+
+#: ../calculator.py:773
+msgid "Mode: "
+msgstr "Tilstand: "
+
+#: ../calculator.py:775
+msgid "sz - Single Zeta"
+msgstr "sz - Enkelt-zeta"
+
+#: ../calculator.py:776
+msgid "szp - Single Zeta polarized"
+msgstr "szp - Enkelt-zeta polariseret"
+
+#: ../calculator.py:777
+msgid "dzp - Double Zeta polarized"
+msgstr "dzp - dobbelt-zeta polariseret"
+
+#. dzp
+#: ../calculator.py:779
+msgid "Basis functions: "
+msgstr "Basisfunktioner: "
+
+#. Mixer
+#: ../calculator.py:785
+msgid "Non-standard mixer parameters"
+msgstr "Særlige mikserparametre"
+
+#: ../calculator.py:981
+msgid "FHI-aims parameters"
+msgstr "FHI-aims-parametre"
+
+#: ../calculator.py:988
+msgid "Periodic geometry, unit cell is:\n"
+msgstr "Periodisk geometri; enhedscellen er:\n"
+
+#: ../calculator.py:993
+msgid "Non-periodic geometry.\n"
+msgstr "Ikke-periodisk geometri.\n"
+
+# XXX ikke Hirschfeld?
+#: ../calculator.py:1000
+msgid "Hirshfeld-based dispersion correction"
+msgstr "Hirshfeld-baseret dispersionskorrektion"
+
+#. Spin polarized, charge, relativity
+#: ../calculator.py:1026
+msgid "Spin / initial moment "
+msgstr "Spin / startmoment "
+
+#: ../calculator.py:1044
+msgid "   Charge"
+msgstr "   Ladning"
+
+#: ../calculator.py:1046
+msgid "   Relativity"
+msgstr "   Relativitet"
+
+#: ../calculator.py:1048
+msgid " Threshold"
+msgstr " Tærskel"
+
+#. self-consistency criteria
+#: ../calculator.py:1053
+msgid "Self-consistency convergence:"
+msgstr "Selfkonsistenskonvergens:"
+
+#: ../calculator.py:1066
+msgid "Compute forces"
+msgstr "Beregn kræfter"
+
+#. XXX: use gtk table for layout.  Spaces will not work well otherwise
+#. (depend on fonts, widget style, ...)
+#. TRANSLATORS: Don't care too much about these, just get approximately
+#. the same string lengths
+#: ../calculator.py:1077
+msgid "Energy:                 "
+msgstr "Energi:                 "
+
+#: ../calculator.py:1079
+msgid " eV   Sum of eigenvalues:  "
+msgstr " eV   Sum af egenværdier:  "
+
+#: ../calculator.py:1081 ../calculator.py:1559
+msgid " eV"
+msgstr " eV"
+
+#: ../calculator.py:1082
+msgid "Electron density: "
+msgstr "Elektrontæthed: "
+
+#: ../calculator.py:1084
+msgid "        Force convergence:  "
+msgstr "        Kraftkonvergens:  "
+
+#: ../calculator.py:1086
+msgid " eV/Ang  "
+msgstr " eV/Å  "
+
+#: ../calculator.py:1099 ../calculator.py:1570
+msgid "Additional keywords: "
+msgstr "Yderligere nøgleord: "
+
+#. run command and species defaults:
+#: ../calculator.py:1113
+msgid "FHI-aims execution command: "
+msgstr "Kørselskommando til FHI-aims: "
+
+# ??
+#: ../calculator.py:1115 ../calculator.py:1587
+msgid "Directory for species defaults: "
+msgstr "Mappe for grundstofstandarder: "
+
+#: ../calculator.py:1127 ../calculator.py:1595
+msgid "Set Defaults"
+msgstr "Brug standardværdier"
+
+#: ../calculator.py:1129
+msgid "Import control.in"
+msgstr "Importér control.in"
+
+#: ../calculator.py:1131
+msgid "Export control.in"
+msgstr "Eksportér control.in"
+
+#: ../calculator.py:1317
+msgid "Export parameters ... "
+msgstr "Eksportér parametre ... "
+
+#: ../calculator.py:1337
+msgid "Import control.in file ... "
+msgstr "Importér control.in-fil ... "
+
+#: ../calculator.py:1393 ../calculator.py:1907
+#, python-format
+msgid ""
+"Please use the facilities provided in this window to manipulate the keyword: "
+"%s!"
+msgstr ""
+"Brug venligst faciliteterne i dette vindue til at manipulere nøgleordet: %s!"
+
+#: ../calculator.py:1396
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/aims.py."
+msgstr ""
+"Kender ikke dette nøgleord: %s\n"
+"\n"
+"Kontrollér venligst!\n"
+"\n"
+"Hvis du virkelig mener det børe være tilgængeligt, så tilføj det venligst "
+"øverst i ase/calculators/aims.py."
+
+#: ../calculator.py:1469
+msgid "VASP parameters"
+msgstr "VASP-parametre"
+
+#: ../calculator.py:1475
+msgid "Periodic geometry, unit cell is: \n"
+msgstr "Periodisk geometri; enhedscelle er: \n"
+
+#: ../calculator.py:1527
+msgid ")    Cutoff: "
+msgstr ")    Afskæring: "
+
+#: ../calculator.py:1528
+msgid "    Precision: "
+msgstr "    Præcision: "
+
+#: ../calculator.py:1530
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å       "
+msgstr "k-punkter x størrelse:  (%.1f, %.1f, %.1f) Å       "
+
+#: ../calculator.py:1546
+msgid "Smearing: "
+msgstr "Udjævning: "
+
+#: ../calculator.py:1548
+msgid " order: "
+msgstr " orden: "
+
+#: ../calculator.py:1550
+msgid " width: "
+msgstr " bredde: "
+
+#: ../calculator.py:1557
+msgid "Self-consistency convergence: "
+msgstr "Selfkonsistenskonvergens: "
+
+#. run command and location of POTCAR files:
+#: ../calculator.py:1583
+msgid "VASP execution command: "
+msgstr "Kørselskommando til VASP: "
+
+#: ../calculator.py:1597
+msgid "Import VASP files"
+msgstr "Importér VASP-filer"
+
+#: ../calculator.py:1599
+msgid "Export VASP files"
+msgstr "Eksportér VASP-filer"
+
+#: ../calculator.py:1810
+msgid "<b>WARNING:</b> cutoff energy is lower than recommended minimum!"
+msgstr ""
+"<b>ADVARSEL:</b> afskæringsenergi er lavere end det anbefalede minimum!"
+
+#: ../calculator.py:1862
+msgid "Import VASP input files: choose directory ... "
+msgstr "Importér VASP-inputfiler: vælg mappe ... "
+
+#: ../calculator.py:1877
+msgid "Export VASP input files: choose directory ... "
+msgstr "Eksportér VASP-inputfiler: vælg mappe ... "
+
+#: ../calculator.py:1910
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/vasp.py."
+msgstr ""
+"Kender ikke dette nøgleord:: %s\n"
+"Kontrollér venligst!\n"
+"\n"
+"Hvis du virkelig tror det bør være tilgængeligt, så tilføj det venligst "
+"øverst i ase/calculators/vasp.py."
+
+#: ../colors.py:24
+msgid "Colors"
+msgstr "Farver"
+
+#. Upper left: Choose how the atoms are colored.
+#: ../colors.py:41
+msgid "Choose how the atoms are colored:"
+msgstr "Vælg hvordan atomerne farves:"
+
+#: ../colors.py:43
+msgid "By atomic number, default \"jmol\" colors"
+msgstr "Efter atomnummer; \"jmol\"-farver som standard"
+
+#: ../colors.py:45
+msgid "By atomic number, user specified"
+msgstr "Efter atomnummer, brugerdefineret"
+
+#: ../colors.py:46
+msgid "By tag"
+msgstr "Efter mærke"
+
+#: ../colors.py:47
+msgid "By force"
+msgstr "Efter kraft"
+
+#: ../colors.py:48
+msgid "By velocity"
+msgstr "Efter hastighed"
+
+#: ../colors.py:49
+msgid "Manually specified"
+msgstr "Manuelt angivet"
+
+#: ../colors.py:50
+msgid "All the same color"
+msgstr "Alle med samme farve"
+
+#. Now fill in the box for additional information in case the force is used.
+#: ../colors.py:60
+msgid "This should not be displayed!"
+msgstr "Dette bør ikke blive vist!"
+
+#: ../colors.py:65 ../colors.py:82 ../rotate.py:25
+msgid "Update"
+msgstr "Opdatér"
+
+#: ../colors.py:67 ../colors.py:84
+msgid "Min: "
+msgstr "Min: "
+
+#: ../colors.py:69 ../colors.py:86
+msgid "  Max: "
+msgstr "  Maks: "
+
+#: ../colors.py:71 ../colors.py:88
+msgid "  Steps: "
+msgstr "  Trin: "
+
+#: ../colors.py:95
+msgid "Create a color scale:"
+msgstr "Opret en farveskala:"
+
+#: ../colors.py:98
+msgid "Black - white"
+msgstr "Sort - hvid"
+
+#: ../colors.py:99
+msgid "Black - red - yellow - white"
+msgstr "Sort - rød - gul - hvid"
+
+#: ../colors.py:100
+msgid "Black - green - white"
+msgstr "Sort - grøn - hvid"
+
+#: ../colors.py:101
+msgid "Black - blue - cyan"
+msgstr "Sort - blå - cyan"
+
+#: ../colors.py:102
+msgid "Hue"
+msgstr "Farvetone"
+
+#: ../colors.py:103
+msgid "Named colors"
+msgstr "Navngivne farver"
+
+#: ../colors.py:109
+msgid "Create"
+msgstr "Opret"
+
+#: ../colors.py:367
+#, python-format
+msgid "Max force: %.2f (this frame), %.2f (all frames)"
+msgstr "Maks kraft: %.2f (dette billede), %.2f (alle billeder)"
+
+#: ../colors.py:369
+#, python-format
+msgid "Max force: %.2f."
+msgstr "Maks kraft: %.2f."
+
+#: ../colors.py:383
+#, python-format
+msgid "Max velocity: %.2f (this frame), %.2f (all frames)"
+msgstr "Maks. hastighed: %.2f (dette billede), %.2f (alle billeder)"
+
+#: ../colors.py:385
+#, python-format
+msgid "Max velocity: %.2f."
+msgstr "Maks. hastighed: %.2f."
+
+#: ../colors.py:426
+msgid "ERROR"
+msgstr "FEJL"
+
+#: ../colors.py:455
+msgid "ERR"
+msgstr "FEJL"
+
+#: ../colors.py:542
+msgid "Incorrect color specification"
+msgstr "Forkert farveangivelse"
+
+#: ../constraints.py:13 ../widgets.py:89
+msgid "Constraints"
+msgstr "Begrænsninger"
+
+#: ../constraints.py:15 ../constraints.py:18 ../settings.py:17
+#: ../widgets.py:91 ../widgets.py:94
+msgid "Constrain"
+msgstr "Begræns"
+
+#: ../constraints.py:16 ../settings.py:20 ../settings.py:35 ../widgets.py:92
+msgid " selected atoms"
+msgstr " markerede atomer"
+
+#: ../constraints.py:19 ../widgets.py:95
+msgid " immobile atoms:"
+msgstr " immobile atomer:"
+
+#: ../constraints.py:21
+msgid "Unconstrain"
+msgstr "Fjern begrænsninger"
+
+#: ../constraints.py:22
+msgid " selected atoms:"
+msgstr " markerede atomer:"
+
+#: ../constraints.py:24
+msgid "Clear constraints"
+msgstr "Ryd begrænsninger"
+
+#: ../constraints.py:26 ../dft.py:29 ../settings.py:53 ../widgets.py:60
+#: ../widgets.py:99
+msgid "Close"
+msgstr "Luk"
+
+#: ../crystal.py:16
+msgid ""
+"  Use this dialog to create crystal lattices. First select the structure,\n"
+"  either from a set of common crystal structures, or by space group "
+"description.\n"
+"  Then add all other lattice parameters.\n"
+"\n"
+"  If an experimental crystal structure is available for an atom, you can\n"
+"  look up the crystal type and lattice constant, otherwise you have to "
+"specify it\n"
+"  yourself.  "
+msgstr ""
+"  Brug denne dialog til at oprette krystalstrukturer. Vælg først "
+"strukturen,\n"
+"  enten fra en samling almindelige krystalstrukturer eller ud fra en\n"
+"  rumgruppebeskrivelse. Tilføj så alle andre gitterparametre.\n"
+"\n"
+"  Hvis der er en eksperimentel krystalstruktur tilgængelig for at\n"
+"  atom, kan du slå krystaltypen samt gitterkonstanten op - ellers skal\n"
+"  du angive den selv.  "
+
+#: ../crystal.py:33
+#, python-format
+msgid " %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>"
+msgstr " %(natoms)i atomer: %(symbols)s, Volumen: %(volume).3f A<sup>3</sup>"
+
+#: ../crystal.py:58
+msgid "Create Bulk Crystal by Spacegroup"
+msgstr "Opret krystalstruktur fra rumgruppe"
+
+#: ../crystal.py:72
+msgid "Number: 1"
+msgstr "Nummer: 1"
+
+# slice ~ opdel
+#: ../crystal.py:73
+msgid "Lattice: "
+msgstr "Gitter: "
+
+#: ../crystal.py:73
+msgid "\tSpace group: "
+msgstr "\tRumgruppe: "
+
+#: ../crystal.py:77
+msgid "Size: x: "
+msgstr "Størrelse: x: "
+
+#: ../crystal.py:78 ../crystal.py:138
+msgid "  y: "
+msgstr "  y: "
+
+#: ../crystal.py:79 ../crystal.py:139
+msgid "  z: "
+msgstr "  z: "
+
+#: ../crystal.py:80 ../surfaceslab.py:127 ../surfaceslab.py:129
+msgid " unit cells"
+msgstr " enhedsceller"
+
+#: ../crystal.py:92 ../crystal.py:96 ../crystal.py:100 ../crystal.py:104
+#: ../crystal.py:108 ../crystal.py:112
+msgid "free"
+msgstr "fri"
+
+#: ../crystal.py:93 ../crystal.py:102
+msgid "equals b"
+msgstr "lig med b"
+
+#: ../crystal.py:94 ../crystal.py:98
+msgid "equals c"
+msgstr "lig med c"
+
+#: ../crystal.py:95 ../crystal.py:99 ../crystal.py:103 ../crystal.py:107
+#: ../crystal.py:111 ../crystal.py:115
+msgid "fixed"
+msgstr "fast"
+
+#: ../crystal.py:97 ../crystal.py:101
+msgid "equals a"
+msgstr "lig med a"
+
+#: ../crystal.py:105 ../crystal.py:114
+msgid "equals beta"
+msgstr "lig med beta"
+
+#: ../crystal.py:106 ../crystal.py:110
+msgid "equals gamma"
+msgstr "lig med gamma"
+
+#: ../crystal.py:109 ../crystal.py:113
+msgid "equals alpha"
+msgstr "lig med alfa"
+
+#: ../crystal.py:119
+msgid "Lattice parameters"
+msgstr "Gitterparametre"
+
+#: ../crystal.py:120
+msgid "\t\ta:\t"
+msgstr "\t\ta:\t"
+
+#: ../crystal.py:121
+msgid "\talpha:\t"
+msgstr "\talfa:\t"
+
+#: ../crystal.py:122
+msgid "\t\tb:\t"
+msgstr "\t\tb:\t"
+
+#: ../crystal.py:123
+msgid "\tbeta:\t"
+msgstr "\tbeta:\t"
+
+#: ../crystal.py:124
+msgid "\t\tc:\t"
+msgstr "\t\tc:\t"
+
+#: ../crystal.py:125
+msgid "\tgamma:\t"
+msgstr "\tgamma:\t"
+
+#: ../crystal.py:126 ../surfaceslab.py:99
+msgid "Get from database"
+msgstr "Hent fra database"
+
+#: ../crystal.py:131
+msgid "Basis: "
+msgstr "Basis: "
+
+#: ../crystal.py:137
+msgid "  Element:\t"
+msgstr "  Grundstof:\t"
+
+#: ../crystal.py:137
+msgid "\tx: "
+msgstr "\tx: "
+
+#: ../crystal.py:157
+msgid "Creating a crystal."
+msgstr "Oprettelse af krystal."
+
+#: ../crystal.py:202
+#, python-format
+msgid "Symbol: %s"
+msgstr "Symbol: %s"
+
+#: ../crystal.py:207
+#, python-format
+msgid "Number: %s"
+msgstr "Nummer: %s"
+
+#: ../crystal.py:210
+msgid "Invalid Spacegroup!"
+msgstr "Ugyldig rumgruppe!"
+
+#: ../crystal.py:336 ../crystal.py:339
+msgid "Please specify a consistent set of atoms."
+msgstr "Angiv venligst en konsistent samling atomer."
+
+#: ../crystal.py:348 ../graphene.py:230 ../nanoparticle.py:613
+#: ../nanotube.py:139 ../surfaceslab.py:248
+msgid "No valid atoms."
+msgstr "Ingen gyldige atomer."
+
+#: ../crystal.py:465
+msgid "Can't find lattice definition!"
+msgstr "Kan ikke finde gitterdefinition!"
+
+#: ../debug.py:11
+msgid "Debug"
+msgstr "Fejlsøgning"
+
+#: ../dft.py:13
+msgid "DFT"
+msgstr "DFT"
+
+#: ../dft.py:19
+msgid "XC-functional: "
+msgstr "XC-funktional: "
+
+#: ../dft.py:23 ../repeat.py:16
+msgid "Repeat atoms:"
+msgstr "Gentag atomer:"
+
+#: ../energyforces.py:11
+msgid "Output:"
+msgstr "Uddata:"
+
+#: ../energyforces.py:41
+msgid "Save output"
+msgstr "Gem uddata"
+
+#: ../energyforces.py:57
+msgid "Potential energy and forces"
+msgstr "Potentiel energi og kræfter"
+
+#: ../energyforces.py:61
+msgid "Calculate potential energy and the force on all atoms"
+msgstr "Beregn potentiel energi og kræfter på alle atomer"
+
+#: ../energyforces.py:65
+msgid "Write forces on the atoms"
+msgstr "Skriv kræfter på atomerne"
+
+#: ../energyforces.py:82
+msgid "Potential Energy:\n"
+msgstr "Potentiel energi:\n"
+
+#: ../energyforces.py:83
+#, python-format
+msgid "  %8.2f eV\n"
+msgstr "  %8.2f eV\n"
+
+#: ../energyforces.py:84
+#, python-format
+msgid ""
+"  %8.4f eV/atom\n"
+"\n"
+msgstr ""
+"  %8.4f eV/atom\n"
+"\n"
+
+#: ../energyforces.py:86
+msgid "Forces:\n"
+msgstr "Kræfter:\n"
+
+#: ../execute.py:23
+msgid ""
+"\n"
+"    Global commands work on all frames or only on the current frame\n"
+"    - Assignment of a global variable may not reference a local one\n"
+"    - use 'Current frame' switch to switch off application to all frames\n"
+"    <c>e</c>:\t\ttotal energy of one frame\n"
+"    <c>fmax</c>:\tmaximal force in one frame\n"
+"    <c>A</c>:\tunit cell\n"
+"    <c>E</c>:\t\ttotal energy array of all frames\n"
+"    <c>F</c>:\t\tall forces in one frame\n"
+"    <c>M</c>:\tall magnetic moments\n"
+"    <c>R</c>:\t\tall atomic positions\n"
+"    <c>S</c>:\tall selected atoms (boolean array)\n"
+"    <c>D</c>:\tall dynamic atoms (boolean array)\n"
+"    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atom commands work on each atom (or a selection) individually\n"
+"    - these can use global commands on the RHS of an equation\n"
+"    - use 'selected atoms only' to restrict application of command\n"
+"    <c>x,y,z</c>:\tatomic coordinates\n"
+"    <c>r,g,b</c>:\tatom display color, range is [0..1]\n"
+"    <c>rad</c>:\tatomic radius for display\n"
+"    <c>s</c>:\t\tatom is selected\n"
+"    <c>d</c>:\t\tatom is movable\n"
+"    <c>f</c>:\t\tforce\n"
+"    <c>Z</c>:\tatomic number\n"
+"    <c>m</c>:\tmagnetic moment\n"
+"    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Special commands and objects:\n"
+"    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame\n"
+"    <c>frame</c>:\tframe number\n"
+"    <c>center</c>:\tcenters the system in its existing unit cell\n"
+"    <c>del S</c>:\tdelete selection\n"
+"    <c>CM</c>:\tcenter of mass\n"
+"    <c>ans[-i]</c>:\tith last calculated result\n"
+"    <c>exec file</c>: executes commands listed in file\n"
+"    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z\n"
+"    <c>gui</c>:\tadvanced: ag window python object\n"
+"    <c>img</c>:\tadvanced: ag images object\n"
+"    "
+msgstr ""
+"\n"
+"    Globale kommandoer virker på alle billeder, eller kun på nuværende "
+"billede\n"
+"    - Tildeling af en global variabel refererer måske ikke til en lokal\n"
+"    - brug \"Nuværende billede\"-knappen til at slå anvendelse på alle "
+"billeder\n"
+"      til eller fra\n"
+"    <c>e</c>:\t\ttotalenergi af et billede\n"
+"    <c>fmax</c>:\tmaksimal kraft i et billede\n"
+"    <c>A</c>:\tenhedscelle\n"
+"    <c>E</c>:\t\ttotalenergi som array for alle billeder\n"
+"    <c>F</c>:\t\talle kræfter i et billede\n"
+"    <c>M</c>:\talle magnetiske momenter\n"
+"    <c>R</c>:\t\talle atompositioner\n"
+"    <c>S</c>:\talle markerede atoms (boolesk array)\n"
+"    <c>D</c>:\talle dynamiske atomer (boolesk array)\n"
+"    eksempler: <c>billede = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atomkommandoer virker på hvert atom (eller en markering) enkeltvis\n"
+"    - disse kan bruge globale kommandoer på højresiden af en ligning\n"
+"    - brug \"kun markerede atomer\" for at begrænse anvendelsen af en "
+"kommando\n"
+"    <c>x,y,z</c>:\tatomkoordinater\n"
+"    <c>r,g,b</c>:\tatomvisningsfarve; interval er [0..1]\n"
+"    <c>rad</c>:\tatomradius (grafisk)\n"
+"    <c>s</c>:\t\tatom er markeret\n"
+"    <c>d</c>:\t\tatom kan flyttes\n"
+"    <c>f</c>:\t\tkraft\n"
+"    <c>Z</c>:\tatomnummer\n"
+"    <c>m</c>:\tmagnetisk moment\n"
+"    eksempler: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Specialkommandoer og objekter:\n"
+"    <c>sa,cf</c>:\tslå begrænsning til markerede atomer/nuværende atomer \n"
+"                        til eller fra\n"
+"    <c>frame</c>:\tbillednummer\n"
+"    <c>center</c>:\tcentrerer systemet i dets eksisterende enhedscelle\n"
+"    <c>del S</c>:\tfjern markering\n"
+"    <c>CM</c>:\tmassemidtpunkt\n"
+"    <c>ans[-i]</c>:\ti'te sidst udregnede resultat\n"
+"    <c>exec file</c>: kører kommandoerne i en fil\n"
+"    <c>cov[Z]</c>:(skrivebeskyttet): kovalent radius for atomnummer Z\n"
+"    <c>gui</c>:\tavanceret: python-objekt for ag-vinduet\n"
+"    <c>img</c>:\tavanceret: ag-billeder som objekt\n"
+"    "
+
+#: ../execute.py:67
+msgid "Expert user mode"
+msgstr "Eksperttilstand"
+
+#: ../execute.py:80
+msgid "Welcome to the ASE Expert user mode"
+msgstr "Velkommen til ASE's eksperttilstand"
+
+#: ../execute.py:87
+msgid "Only selected atoms (sa)   "
+msgstr "Kun markerede atomer (sa)   "
+
+#: ../execute.py:89
+msgid "Only current frame (cf)  "
+msgstr "Kun nuværende billede (cf)  "
+
+#: ../execute.py:99
+msgid ""
+"Global: Use A, D, E, M, N, R, S, n, frame; Atoms: Use a, f, m, s, x, y, z, "
+"Z     "
+msgstr ""
+"Globalt: Brug A, D, E, M, N, R, S, n, frame; Atomer: Brug a, f, m, s, x, y, "
+"z, Z     "
+
+#: ../execute.py:198
+#, python-format
+msgid "*** WARNING: file does not exist - %s"
+msgstr "*** ADVARSEL: filen findes ikke - %s"
+
+#: ../execute.py:203
+msgid "*** WARNING: No atoms selected to work with"
+msgstr "*** ADVARSEL: Ingen atomer markeret til at arbejde på"
+
+#: ../execute.py:277
+msgid "*** Only working on selected atoms"
+msgstr "*** Arbejder kun på markerede atomer"
+
+#: ../execute.py:279
+msgid "*** Working on all atoms"
+msgstr "*** Arbejder på alle atomer"
+
+#: ../execute.py:283
+msgid "*** Only working on current image"
+msgstr "*** Arbejder kun på nuværende billede"
+
+#: ../execute.py:285
+msgid "*** Working on all images"
+msgstr "*** Arbejder på alle billeder"
+
+#: ../execute.py:301
+msgid "Save Terminal text ..."
+msgstr "Gem terminaltekst ..."
+
+#: ../graphene.py:15
+msgid ""
+"Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may\n"
+"optionally be saturated with hydrogen (or another element)."
+msgstr ""
+"Konstruér et grafénlag eller et grafénnanobånd.  Et nanobånd kan eventuelt\n"
+"mættes med hydrogen (eller et andet grundstof)."
+
+#: ../graphene.py:30 ../gui.py:298
+msgid "Graphene"
+msgstr "Grafén"
+
+#. Choose structure
+#. The structure and lattice constant
+#. Choose the surface structure
+#: ../graphene.py:37 ../nanoparticle.py:138 ../surfaceslab.py:78
+msgid "Structure: "
+msgstr "Struktur: "
+
+#: ../graphene.py:39
+msgid "Infinite sheet"
+msgstr "Uendeligt lag"
+
+#: ../graphene.py:39
+msgid "Unsaturated ribbon"
+msgstr "Umættet bånd"
+
+#: ../graphene.py:40
+msgid "Saturated ribbon"
+msgstr "Mættet bånd"
+
+#. Orientation
+#: ../graphene.py:47
+msgid "Orientation: "
+msgstr "Orientering: "
+
+#: ../graphene.py:50
+msgid "zigzag"
+msgstr "zigzag"
+
+#: ../graphene.py:50
+msgid "armchair"
+msgstr "lænestol"
+
+#: ../graphene.py:66 ../graphene.py:78 ../nanotube.py:46
+msgid "  Bond length: "
+msgstr "  Bindingslængde: "
+
+#. Choose the saturation element and bond length
+#: ../graphene.py:72
+msgid "Saturation: "
+msgstr "Mætning: "
+
+#: ../graphene.py:75
+msgid "H"
+msgstr "H"
+
+#. Size
+#: ../graphene.py:91
+msgid "Width: "
+msgstr "Bredde: "
+
+#: ../graphene.py:92 ../nanotube.py:65
+msgid "  Length: "
+msgstr "  Længde: "
+
+#. Vacuum
+#: ../graphene.py:100
+msgid "Vacuum: "
+msgstr "Vakuum: "
+
+#: ../graphene.py:138 ../nanotube.py:96 ../setupwindow.py:32
+msgid "  No element specified!"
+msgstr "  Intet grundstof angivet!"
+
+#: ../graphene.py:231 ../nanoparticle.py:614 ../nanotube.py:140
+#: ../pybutton.py:49 ../surfaceslab.py:249
+msgid "You have not (yet) specified a consistent set of parameters."
+msgstr "Du har (endnu) ikke angivet konsistente parametre."
+
+#: ../graphs.py:19
+msgid "Help for plot ..."
+msgstr "Hjælp til grafer ..."
+
+#: ../graphs.py:30 ../graphs.py:33
+msgid "Plot"
+msgstr "Graf"
+
+#: ../graphs.py:38
+msgid "clear"
+msgstr "ryd"
+
+#: ../graphs.py:92
+msgid "Save data to file ... "
+msgstr "Gem data til fil ... "
+
+#: ../gtkexcepthook.py:117
+msgid "Bug Detected"
+msgstr "Fejl fundet"
+
+#: ../gtkexcepthook.py:121
+msgid "A programming error has been detected."
+msgstr "Der blev fundet en programmeringsfejl."
+
+#: ../gtkexcepthook.py:124
+msgid ""
+"It probably isn't fatal, but the details should be reported to the "
+"developers nonetheless."
+msgstr ""
+"Den er nok ikke fatal, men detaljerne bør alligevel rapporteres til "
+"udviklerne."
+
+#: ../gtkexcepthook.py:140
+msgid "Report..."
+msgstr "Rapportér..."
+
+#: ../gtkexcepthook.py:144
+msgid "Details..."
+msgstr "Detaljer..."
+
+#: ../gtkexcepthook.py:160
+#, python-format
+msgid ""
+"From: buggy_application\"\n"
+"To: bad_programmer\n"
+"Subject: Exception feedback\n"
+"\n"
+"%s"
+msgstr ""
+"Fra: fejlagtigt_program\"\n"
+"Til: dårlig_programmør\n"
+"Emne: Exception feedback\n"
+"\n"
+"%s"
+
+#. Show details...
+#: ../gtkexcepthook.py:173
+msgid "Bug Details"
+msgstr "Detaljer om fejl"
+
+#: ../gui.py:164
+msgid "_File"
+msgstr "_Fil"
+
+#: ../gui.py:165
+msgid "_Edit"
+msgstr "_Redigér"
+
+#: ../gui.py:166
+msgid "_View"
+msgstr "_Vis"
+
+#: ../gui.py:167
+msgid "_Tools"
+msgstr "_Værktøjer"
+
+#. TRANSLATORS: Set up (i.e. build) surfaces, nanoparticles, ...
+#: ../gui.py:169
+msgid "_Setup"
+msgstr "_Byg"
+
+#: ../gui.py:170
+msgid "_Calculate"
+msgstr "_Beregn"
+
+#: ../gui.py:171
+msgid "_Help"
+msgstr "_Hjælp"
+
+#: ../gui.py:172
+msgid "_Open"
+msgstr "_Åbn"
+
+#: ../gui.py:173
+msgid "Create a new file"
+msgstr "Opret en ny fil"
+
+#: ../gui.py:175
+msgid "_New"
+msgstr "_Ny"
+
+#: ../gui.py:176
+msgid "New ase.gui window"
+msgstr "Nyt ase.gui-vindue"
+
+#: ../gui.py:178
+msgid "_Save"
+msgstr "_Gem"
+
+#: ../gui.py:179
+msgid "Save current file"
+msgstr "Gem den aktuelle fil"
+
+#: ../gui.py:181
+msgid "_Quit"
+msgstr "_Afslut"
+
+#: ../gui.py:182
+msgid "Quit"
+msgstr "Afslut"
+
+#: ../gui.py:184
+msgid "Select _all"
+msgstr "Vælg _alle"
+
+#: ../gui.py:187
+msgid "_Invert selection"
+msgstr "_Omvend markering"
+
+#: ../gui.py:190
+msgid "Select _constrained atoms"
+msgstr "Vælg _fastgjorte atomer"
+
+#: ../gui.py:193
+msgid "Select _immobile atoms"
+msgstr "Vælg _immobile atomer"
+
+#: ../gui.py:196
+msgid "_Copy"
+msgstr "_Kopiér"
+
+#: ../gui.py:197
+msgid "Copy current selection and its orientation to clipboard"
+msgstr "Kopiér nuværende markering og dens orientering til udklipsholderen"
+
+#: ../gui.py:199
+msgid "_Paste"
+msgstr "_Indsæt"
+
+#: ../gui.py:200
+msgid "Insert current clipboard selection"
+msgstr "Indsæt nuværende markering fra udklipsholderen"
+
+#: ../gui.py:202
+msgid "_Modify"
+msgstr "_Ændr"
+
+#: ../gui.py:203
+msgid "Change tags, moments and atom types of the selected atoms"
+msgstr "Ændr mærker, impuls og atomnummer for de valgte atomer"
+
+#: ../gui.py:205
+msgid "_Add atoms"
+msgstr "_Tilføj atomer"
+
+#: ../gui.py:206
+msgid "Insert or import atoms and molecules"
+msgstr "Indsæt eller importér atomer og molekyler"
+
+#: ../gui.py:208
+msgid "_Delete selected atoms"
+msgstr "_Slet markerede atomer"
+
+#: ../gui.py:209
+msgid "Delete the selected atoms"
+msgstr "Slet de markerede atomer"
+
+#: ../gui.py:211
+msgid "_First image"
+msgstr "_Første billede"
+
+#: ../gui.py:214
+msgid "_Previous image"
+msgstr "_Forrige billede"
+
+#: ../gui.py:217
+msgid "_Next image"
+msgstr "_Næste billede"
+
+#: ../gui.py:220
+msgid "_Last image"
+msgstr "_Sidste billede"
+
+#: ../gui.py:223
+msgid "Quick Info ..."
+msgstr "Hurtig info ..."
+
+#: ../gui.py:226
+msgid "Repeat ..."
+msgstr "Gentag ..."
+
+#: ../gui.py:229
+msgid "Rotate ..."
+msgstr "Rotér ..."
+
+#: ../gui.py:232
+msgid "Colors ..."
+msgstr "Farver ..."
+
+#. TRANSLATORS: verb
+#: ../gui.py:235
+msgid "Focus"
+msgstr "Fokusér"
+
+#: ../gui.py:238
+msgid "Zoom in"
+msgstr "Zoom ind"
+
+#: ../gui.py:241
+msgid "Zoom out"
+msgstr "Zoom ud"
+
+#: ../gui.py:244
+msgid "Reset View"
+msgstr "Nulstil perspektiv"
+
+#: ../gui.py:247
+msgid "Settings ..."
+msgstr "Indstillinger ..."
+
+#: ../gui.py:250
+msgid "VMD"
+msgstr "VMD"
+
+#: ../gui.py:253
+msgid "RasMol"
+msgstr "RasMol"
+
+#: ../gui.py:256
+msgid "xmakemol"
+msgstr "xmakemol"
+
+#: ../gui.py:259
+msgid "avogadro"
+msgstr "avogadro"
+
+#: ../gui.py:262
+msgid "Graphs ..."
+msgstr "Grafer ..."
+
+#: ../gui.py:265
+msgid "Movie ..."
+msgstr "Film ..."
+
+#: ../gui.py:268
+msgid "Expert mode ..."
+msgstr "Eksperttilstand ..."
+
+#: ../gui.py:271
+msgid "Constraints ..."
+msgstr "Begrænsninger ..."
+
+# gemmer et billede af atomerne
+#: ../gui.py:274
+msgid "Render scene ..."
+msgstr "Tegn struktur ..."
+
+#: ../gui.py:277
+msgid "DFT ..."
+msgstr "DFT ..."
+
+#: ../gui.py:280
+msgid "NE_B"
+msgstr "NE_B"
+
+#: ../gui.py:283
+msgid "B_ulk Modulus"
+msgstr "K_ompressibilitetsmodul"
+
+#: ../gui.py:286
+msgid "_Bulk Crystal"
+msgstr "_Krystal"
+
+#: ../gui.py:287
+msgid "Create a bulk crystal with arbitrary orientation"
+msgstr "Opret en krystalstruktur med arbitrær orientering"
+
+#: ../gui.py:289
+msgid "_Surface slab"
+msgstr "_Overflade"
+
+#: ../gui.py:290
+msgid "Create the most common surfaces"
+msgstr "Opret de mest almindelige overflader"
+
+#: ../gui.py:292
+msgid "_Nanoparticle"
+msgstr "_Nanopartikel"
+
+#: ../gui.py:293
+msgid "Create a crystalline nanoparticle"
+msgstr "Opret en krystallinsk nanopartikel"
+
+#: ../gui.py:295
+msgid "Nano_tube"
+msgstr "Nano_rør"
+
+#: ../gui.py:296
+msgid "Create a nanotube"
+msgstr "Opret et nanorør"
+
+#: ../gui.py:299
+msgid "Create a graphene sheet or nanoribbon"
+msgstr "Opret et lag eller bånd af grafén"
+
+#: ../gui.py:301
+msgid "Set _Calculator"
+msgstr "Angiv _beregner"
+
+#: ../gui.py:302
+msgid "Set a calculator used in all calculation modules"
+msgstr "Angiv en beregner der skal bruges i alle beregningsmoduler"
+
+#: ../gui.py:304
+msgid "_Energy and Forces"
+msgstr "_Energi og kræfter"
+
+#: ../gui.py:305
+msgid "Calculate energy and forces"
+msgstr "Beregn energi og kræfter"
+
+#: ../gui.py:307
+msgid "Energy _Minimization"
+msgstr "Energi_minimering"
+
+#: ../gui.py:308
+msgid "Minimize the energy"
+msgstr "Minimér energien"
+
+#: ../gui.py:310
+msgid "Scale system"
+msgstr "Skalér systemet"
+
+#: ../gui.py:311
+msgid "Deform system by scaling it"
+msgstr "Deformér systemet ved skalering"
+
+#: ../gui.py:313
+msgid "_About"
+msgstr "_Om"
+
+#: ../gui.py:316
+msgid "Webpage ..."
+msgstr "Webside ..."
+
+#: ../gui.py:317
+msgid "Debug ..."
+msgstr "Fejlsøgning ..."
+
+#: ../gui.py:319
+msgid "Show _unit cell"
+msgstr "Vis _enhedscelle"
+
+#: ../gui.py:323
+msgid "Show _axes"
+msgstr "Vis _akser"
+
+#: ../gui.py:327
+msgid "Show _bonds"
+msgstr "Vis _bindinger"
+
+#: ../gui.py:331
+msgid "_Move atoms"
+msgstr "_Flyt atomer"
+
+#: ../gui.py:335
+msgid "_Rotate atoms"
+msgstr "_Rotér atomer"
+
+#: ../gui.py:339
+msgid "Orien_t atoms"
+msgstr "Orien_tér atomer"
+
+#: ../gui.py:351
+#, python-format
+msgid "building menus failed: %s"
+msgstr "bygning af menuer mislykkedes: %s"
+
+#: ../gui.py:620 ../gui.py:1016 ../gui.py:1076
+msgid "Open ..."
+msgstr "Åbn ..."
+
+#: ../gui.py:624 ../gui.py:1019
+msgid "<<filename>>"
+msgstr "<<filnavn>>"
+
+#: ../gui.py:756
+msgid "Add atoms"
+msgstr "Tilføj atomer"
+
+#: ../gui.py:759
+msgid "Paste"
+msgstr "Indsæt"
+
+#: ../gui.py:765
+msgid "Insert atom or molecule"
+msgstr "Indsæt atom eller molekyle"
+
+#: ../gui.py:766 ../gui.py:883
+msgid "Tag"
+msgstr "Mærke"
+
+#: ../gui.py:767 ../gui.py:884
+msgid "Moment"
+msgstr "Moment"
+
+#: ../gui.py:768
+msgid "Position"
+msgstr "Position"
+
+#: ../gui.py:793
+msgid "_Load molecule"
+msgstr "_Indlæs molekyle"
+
+#: ../gui.py:797 ../gui.py:899
+msgid "_OK"
+msgstr "_O.k."
+
+#: ../gui.py:801 ../gui.py:903
+msgid "_Cancel"
+msgstr "_Annullér"
+
+#: ../gui.py:876
+msgid "Modify"
+msgstr "Ændr"
+
+#: ../gui.py:882
+msgid "Atom"
+msgstr "Atom"
+
+#: ../gui.py:927
+msgid "Confirmation"
+msgstr "Bekræftelse"
+
+#: ../gui.py:931
+msgid "Delete selected atom?"
+msgid_plural "Delete selected atoms?"
+msgstr[0] "Slet det valgte atom?"
+msgstr[1] "Slet de valgte atomer?"
+
+#: ../gui.py:938
+msgid "Cancel"
+msgstr "Annullér"
+
+#: ../gui.py:1024 ../gui.py:1106
+msgid "Automatic"
+msgstr "Automatisk"
+
+#: ../gui.py:1025
+msgid "Dacapo netCDF output file"
+msgstr "netCDF-uddatafil fra Dacapo"
+
+#: ../gui.py:1026
+msgid "Virtual Nano Lab file"
+msgstr "Virtual Nano Lab-fil"
+
+#: ../gui.py:1027
+msgid "ASE pickle trajectory"
+msgstr "Pickletrajectory fra ASE"
+
+#: ../gui.py:1028 ../gui.py:1119
+msgid "ASE bundle trajectory"
+msgstr "Bundletrajectory fra ASE"
+
+#: ../gui.py:1029
+msgid "GPAW text output"
+msgstr "Textudskrift fra GPAW"
+
+#: ../gui.py:1030
+msgid "CUBE file"
+msgstr "CUBE-fil"
+
+#: ../gui.py:1031
+msgid "XCrySDen Structure File"
+msgstr "XCrySDen-strukturfil"
+
+#: ../gui.py:1032
+msgid "Dacapo text output"
+msgstr "Tekstudskrift fra Dacapo"
+
+#: ../gui.py:1033
+msgid "XYZ-file"
+msgstr "XYZ-fil"
+
+#: ../gui.py:1034
+msgid "VASP POSCAR/CONTCAR file"
+msgstr "POSCAR/CONTCAR-fil fra VASP"
+
+#: ../gui.py:1035
+msgid "VASP OUTCAR file"
+msgstr "OUTCAR-fil fra VASP"
+
+#: ../gui.py:1036
+msgid "Protein Data Bank"
+msgstr "Proteindatabank"
+
+#: ../gui.py:1037
+msgid "CIF-file"
+msgstr "CIF-fil"
+
+#: ../gui.py:1038
+msgid "FHI-aims geometry file"
+msgstr "FHI-aims-geometrifil"
+
+#: ../gui.py:1039
+msgid "FHI-aims output file"
+msgstr "Uddatafil fra FHI-aims"
+
+#: ../gui.py:1040
+msgid "TURBOMOLE coord file"
+msgstr "TURBOMOLE-koordinatfil"
+
+# exciting er et program
+#: ../gui.py:1041
+msgid "exciting input"
+msgstr "exciting-inddata"
+
+#: ../gui.py:1042
+msgid "WIEN2k structure file"
+msgstr "WIEN2k-strukturfil"
+
+#: ../gui.py:1043
+msgid "DftbPlus input file"
+msgstr "DftbPlus-inddatafil"
+
+#: ../gui.py:1044
+msgid "ETSF format"
+msgstr "ETSF-format"
+
+#: ../gui.py:1045 ../gui.py:1117
+msgid "CASTEP geom file"
+msgstr "CASTEP-geom-fil"
+
+#: ../gui.py:1046
+msgid "CASTEP output file"
+msgstr "Uddatafil fra CASTEP"
+
+#: ../gui.py:1047
+msgid "CASTEP trajectory file"
+msgstr "Trajectory-fil fra CASTEP"
+
+#: ../gui.py:1048
+msgid "DFTBPlus GEN format"
+msgstr "GEN-format fra DFTBPlus"
+
+#: ../gui.py:1054
+msgid "File type:"
+msgstr "Filtype:"
+
+#: ../gui.py:1094
+msgid "Save ..."
+msgstr "Gem ..."
+
+#: ../gui.py:1107
+msgid "XYZ file"
+msgstr "XYZ-fil"
+
+#: ../gui.py:1108
+msgid "ASE trajectory"
+msgstr "Trajectory fra ASE"
+
+#: ../gui.py:1109
+msgid "PDB file"
+msgstr "PDB-fil"
+
+#: ../gui.py:1110
+msgid "Gaussian cube file"
+msgstr "Cube-fil fra Gaussian"
+
+#: ../gui.py:1111
+msgid "Python script"
+msgstr "Pythonscript"
+
+#: ../gui.py:1112
+msgid "VNL file"
+msgstr "VNL-fil"
+
+#: ../gui.py:1113
+msgid "Portable Network Graphics"
+msgstr "Portable Network Graphics"
+
+#: ../gui.py:1114
+msgid "Persistence of Vision"
+msgstr "Persistence of Vision"
+
+#: ../gui.py:1115
+msgid "Encapsulated PostScript"
+msgstr "Encapsulated PostScript"
+
+#: ../gui.py:1116
+msgid "FHI-aims geometry input"
+msgstr "Geometriinddata til FHI-aims"
+
+#: ../gui.py:1118
+msgid "VASP geometry input"
+msgstr "Geometriinddata til VASP"
+
+#: ../gui.py:1120
+msgid "cif file"
+msgstr "cif-fil"
+
+#: ../gui.py:1142
+#, python-format
+msgid "Save current image only (#%d)"
+msgstr "Gem kun nuværende billede (#%d)"
+
+# slice ~ opdel
+#: ../gui.py:1146
+msgid "Slice: "
+msgstr "Del: "
+
+#: ../gui.py:1147
+msgid "Help for slice ..."
+msgstr "Hjælp til opdeling ..."
+
+#: ../gui.py:1159
+msgid "AG INTERNAL ERROR: strange response in Save,"
+msgstr "INTERN FEJL I AG: mystisk svar i Save,"
+
+#: ../gui.py:1178
+msgid "Unknown output format!"
+msgstr "Ukendt uddataformat!"
+
+#: ../gui.py:1179
+#, python-format
+msgid "Use one of: %s"
+msgstr "Brug et af: %s"
+
+#: ../gui.py:1284
+msgid "Not implemented!"
+msgstr "Ikke implementeret!"
+
+#: ../gui.py:1285
+msgid "do you really need it?"
+msgstr "har du virkelig brug for dette?"
+
+#: ../minimize.py:20
+msgid "Algorithm: "
+msgstr "Algoritme: "
+
+#: ../minimize.py:25 ../progress.py:67
+msgid "Convergence criterion: F<sub>max</sub> = "
+msgstr "Konvergenskriterium: F<sub>max</sub> = "
+
+#: ../minimize.py:30 ../progress.py:70
+msgid "Max. number of steps: "
+msgstr "Maksimalt antal trin: "
+
+#. Special stuff for MDMin
+#: ../minimize.py:33
+msgid "Pseudo time step: "
+msgstr "Pseudotidsskridt: "
+
+#: ../minimize.py:54
+msgid "Energy minimization"
+msgstr "Energiminimering"
+
+#: ../minimize.py:58
+msgid "Minimize the energy with respect to the positions."
+msgstr "Minimér energien med hensyn til positionerne."
+
+#. Don't catch errors in the function.
+#. Display status message
+#: ../minimize.py:90 ../scaling.py:299
+msgid "Running ..."
+msgstr "Kører ..."
+
+#. Update display to reflect cancellation of simulation.
+#: ../minimize.py:107
+#, python-format
+msgid "Minimization CANCELLED after %i steps."
+msgstr "Minimering AFBRUDT efter %i trin."
+
+#: ../minimize.py:113 ../scaling.py:350
+msgid "Out of memory, consider using LBFGS instead"
+msgstr "Løbet tør for hukommelse; overvej at bruge LBFGS i stedet"
+
+#. Update display to reflect succesful end of simulation.
+#: ../minimize.py:120
+#, python-format
+msgid "Minimization completed in %i steps."
+msgstr "Minimering fuldført på %i trin."
+
+#. self.connect('delete_event', self.exit2)
+#: ../movie.py:14
+msgid "Movie"
+msgstr "Film"
+
+#: ../movie.py:16
+msgid "Image number:"
+msgstr "Billednummer:"
+
+#: ../movie.py:38
+msgid "Play"
+msgstr "Afspil"
+
+#: ../movie.py:40
+msgid "Stop"
+msgstr "Stop"
+
+#. TRANSLATORS: This function plays an animation forwards and backwards
+#. alternatingly, e.g. for displaying vibrational movement
+#: ../movie.py:44
+msgid "Rock"
+msgstr "Pendul"
+
+#: ../movie.py:60
+msgid " Frame rate: "
+msgstr " Billedrate: "
+
+#: ../movie.py:61
+msgid " Skip frames: "
+msgstr " Overspring billeder: "
+
+#: ../nanoparticle.py:19
+msgid ""
+"Create a nanoparticle either by specifying the number of layers, or using "
+"the\n"
+"Wulff construction.  Please press the [Help] button for instructions on how "
+"to\n"
+"specify the directions.\n"
+"WARNING: The Wulff construction currently only works with cubic crystals!\n"
+msgstr ""
+"Opret en nanopartikel enten ved at angive antallet af lag, eller ved\n"
+"brug af Wulffkonstruktion.  Tryk på knappen [Hjælp] for at få\n"
+"instruktioner om hvordan retninger angives.\n"
+"\n"
+"ADVARSEL: Wulffkonstruktion fungerer i øjeblikket kun med kubiske "
+"krystaller!\n"
+
+#: ../nanoparticle.py:26
+msgid ""
+"\n"
+"The nanoparticle module sets up a nano-particle or a cluster with a given\n"
+"crystal structure.\n"
+"\n"
+"1) Select the element, the crystal structure and the lattice constant(s).\n"
+"   The [Get structure] button will find the data for a given element.\n"
+"\n"
+"2) Choose if you want to specify the number of layers in each direction, or "
+"if\n"
+"   you want to use the Wulff construction.  In the latter case, you must "
+"specify\n"
+"   surface energies in each direction, and the size of the cluster.\n"
+"\n"
+"How to specify the directions:\n"
+"------------------------------\n"
+"\n"
+"First time a direction appears, it is interpreted as the entire family of\n"
+"directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of "
+"these\n"
+"directions is specified again, the second specification overrules that "
+"specific\n"
+"direction.  For this reason, the order matters and you can rearrange the\n"
+"directions with the [Up] and [Down] keys.  You can also add a new "
+"direction,\n"
+"remember to press [Add] or it will not be included.\n"
+"\n"
+"Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of "
+"directions,\n"
+"the {111} family and then the (001) direction, overruling the value given "
+"for\n"
+"the whole family of directions.\n"
+msgstr ""
+"\n"
+"Nanopartikelmodulet konstruerer en nanopartikel eller klynge med en\n"
+"given krystalstruktur.\n"
+"\n"
+"1) Vælg grundstoffet, krystalstrukturen og gitterkonstanterne.\n"
+"   Knappen [Hent struktur] vil finde data for et givet grundstof.\n"
+"\n"
+"2) Vælg om du vil angive antallet af lag i hver retning, eller om du\n"
+"   vil benytte en Wulffkonstruktion.  I sidstnævnte tilfælde skal du\n"
+"   angive overfladeenergier for hver retning samt klyngens størrelse.\n"
+"\n"
+"Hvordan retninger angives\n"
+"-------------------------\n"
+"\n"
+"Første gang en retning dukker op, fortolkes den som en hel familie af\n"
+"retninger - f.eks. dækker (0,0,1) også (1,0,0), (-1,0,0) osv.  Hvis en af\n"
+"disse retninger angives igen, vil anden specifikation særligt gælde\n"
+"denne specifikke retning.  Derfor er rækkefølgen ikke ligegyldig, og du kan\n"
+"omarrangere retningerne med knapperne [Op] og [Ned].  Du kan også tilføje in "
+"ny retning - husk at trykke [Tilføj], eller den vil ikke blive inkluderet.\n"
+"\n"
+"Eksempel: (1,0,0), (1,1,1), (0,0,1) ville angive familien {100} af "
+"retninger,\n"
+"{111}-familien og så (001) retningen, der tilsidesætter værdien givet til\n"
+"selve familien af retninger.\n"
+
+#. Structures:  Abbreviation, name, 4-index (boolean), two lattice const (bool), factory
+#: ../nanoparticle.py:83
+msgid "Face centered cubic (fcc)"
+msgstr "Face centered cubic (fcc)"
+
+#: ../nanoparticle.py:84
+msgid "Body centered cubic (bcc)"
+msgstr "Body centered cubic (bcc)"
+
+#: ../nanoparticle.py:85
+msgid "Simple cubic (sc)"
+msgstr "Simpel kubisk (sc)"
+
+#: ../nanoparticle.py:86
+msgid "Hexagonal closed-packed (hcp)"
+msgstr "Heksagonal tætpakket (hcp)"
+
+#: ../nanoparticle.py:87
+msgid "Graphite"
+msgstr "Grafit"
+
+#: ../nanoparticle.py:116
+msgid "Nanoparticle"
+msgstr "Nanopartikel"
+
+#. Choose the element
+#. Choose the element and bond length
+#. Choose the element
+#: ../nanoparticle.py:126 ../nanotube.py:40 ../surfaceslab.py:69
+msgid "Element: "
+msgstr "Grundstof: "
+
+#: ../nanoparticle.py:130
+msgid "Get structure"
+msgstr "Hent struktur"
+
+#: ../nanoparticle.py:154
+msgid "Lattice constant:  a ="
+msgstr "Gitterkonstant:  a ="
+
+#. Choose specification method
+#: ../nanoparticle.py:171
+msgid "Method: "
+msgstr "Metode: "
+
+#: ../nanoparticle.py:173
+msgid "Layer specification"
+msgstr "Lagspecifikation"
+
+#: ../nanoparticle.py:173
+msgid "Wulff construction"
+msgstr "Wulffkonstruktion"
+
+#: ../nanoparticle.py:193
+msgid "Dummy placeholder object"
+msgstr "Stedfortræderobjekt"
+
+#: ../nanoparticle.py:195
+msgid "Add new direction:"
+msgstr "Tilføj ny retning:"
+
+#: ../nanoparticle.py:211
+msgid "Add"
+msgstr "Tilføj"
+
+#: ../nanoparticle.py:219
+msgid "Set all directions to default values"
+msgstr "Sæt alle retninger til standardværdier"
+
+#: ../nanoparticle.py:227
+msgid "Particle size: "
+msgstr "Partikelstørrelse: "
+
+#: ../nanoparticle.py:228 ../nanoparticle.py:265 ../progress.py:196
+msgid "Number of atoms: "
+msgstr "Antal atomer: "
+
+#: ../nanoparticle.py:233
+msgid "Volume: "
+msgstr "Volumen: "
+
+#: ../nanoparticle.py:238
+msgid "ų"
+msgstr "ų"
+
+#: ../nanoparticle.py:243
+msgid "Rounding: If exact size is not possible, choose the size"
+msgstr "Afrunding: Hvis eksakt størrelse ikke kan opnås, så vælg størrelsen"
+
+#: ../nanoparticle.py:246
+msgid "above  "
+msgstr "over  "
+
+#: ../nanoparticle.py:247
+msgid "below  "
+msgstr "under  "
+
+#: ../nanoparticle.py:248
+msgid "closest  "
+msgstr "tættest på  "
+
+#: ../nanoparticle.py:251
+msgid "Smaller"
+msgstr "Mindre"
+
+#: ../nanoparticle.py:252
+msgid "Larger"
+msgstr "Større"
+
+#: ../nanoparticle.py:267
+msgid "   Approx. diameter: "
+msgstr "   Diameter omtrent: "
+
+#: ../nanoparticle.py:271
+msgid "Information about the created cluster:"
+msgstr "Information om den konstruerede klynge:"
+
+#. Buttons
+#: ../nanoparticle.py:277 ../nanotube.py:75
+msgid "Creating a nanoparticle."
+msgstr "Konstruktion af nanopartikel."
+
+#: ../nanoparticle.py:284
+msgid "Automatic Apply"
+msgstr "Anvend automatisk"
+
+#: ../nanoparticle.py:332
+msgid "Up"
+msgstr "Op"
+
+#: ../nanoparticle.py:337
+msgid "Down"
+msgstr "Ned"
+
+#: ../nanoparticle.py:342
+msgid "Delete"
+msgstr "Slet"
+
+#: ../nanoparticle.py:383
+msgid "Surface energies (as energy/area, NOT per atom):"
+msgstr "Overfladeenergier (som energi/areal, IKKE per atom):"
+
+#: ../nanoparticle.py:389
+msgid "Number of layers:"
+msgstr "Antal lag:"
+
+#: ../nanoparticle.py:418
+msgid "At least one index must be non-zero"
+msgstr "Mindst et indeks skal være forskelligt fra nul"
+
+#: ../nanoparticle.py:421
+msgid "Invalid hexagonal indices"
+msgstr "Ugyldige heksagonale indeks"
+
+#: ../nanoparticle.py:476 ../surfaceslab.py:218
+msgid "Invalid element."
+msgstr "Ugyldigt grundstof."
+
+#: ../nanoparticle.py:486
+msgid "Unsupported or unknown structure"
+msgstr "Uunderstøttet eller ukendt struktur"
+
+#: ../nanoparticle.py:603
+#, python-format
+msgid "%.1f Å"
+msgstr "%.1f Å"
+
+#: ../nanotube.py:14
+msgid ""
+"Set up a Carbon nanotube by specifying the (n,m) roll-up vector.\n"
+"Please note that m <= n.\n"
+"\n"
+"Nanotubes of other elements can be made by specifying the element\n"
+"and bond length."
+msgstr ""
+"Byg et kulstofnanorør ved at angive uprulningsvektoren (n, m).\n"
+"Bemærk at m <= n.\n"
+"\n"
+"Nanorør af andre grundstoffer kan bygges ved at angive hvilket grundstof,\n"
+"samt bindingslængde."
+
+#: ../nanotube.py:33
+msgid "Nanotube"
+msgstr "Nanorør"
+
+#. Choose the structure.
+#: ../nanotube.py:57
+msgid "Select roll-up vector (n,m) and tube length:"
+msgstr "Vælg oprulningsvektor (n,m) og rørlængde:"
+
+#: ../progress.py:25
+msgid "Progress"
+msgstr "Fremgang"
+
+#: ../progress.py:32
+msgid "Scaling deformation:"
+msgstr "Skaleringsdeformation:"
+
+#: ../progress.py:38
+#, python-format
+msgid "Step number %s of %s."
+msgstr "Trin nummer %s af %s."
+
+#. Minimization progress frame
+#. Box containing frame and spacing
+#: ../progress.py:53
+msgid "Energy minimization:"
+msgstr "Energiminimering:"
+
+#: ../progress.py:60
+msgid "Step number: "
+msgstr "Trinnummer: "
+
+#: ../progress.py:62
+msgid "F<sub>max</sub>: "
+msgstr "F<sub>max</sub>: "
+
+#: ../progress.py:102
+msgid "unknown"
+msgstr "ukendt"
+
+#: ../progress.py:179
+msgid "Status: "
+msgstr "Status: "
+
+#: ../progress.py:181
+msgid "Iteration: "
+msgstr "Iteration: "
+
+#: ../progress.py:184
+msgid "log<sub>10</sub>(change):"
+msgstr "log<sub>10</sub>(skift):"
+
+#: ../progress.py:187
+msgid "Wave functions: "
+msgstr "Bølgefunktioner: "
+
+#: ../progress.py:189
+msgid "Density: "
+msgstr "Tæthed: "
+
+#: ../progress.py:191
+msgid "Energy: "
+msgstr "Energi: "
+
+#: ../progress.py:194
+msgid "GPAW version: "
+msgstr "GPAW-version: "
+
+#: ../progress.py:197
+msgid "N/A"
+msgstr "-"
+
+#: ../progress.py:198
+msgid "Memory estimate: "
+msgstr "Hukommelsesestimat: "
+
+#: ../progress.py:233
+msgid "No info"
+msgstr "Ingen info"
+
+#: ../progress.py:243
+msgid "Initializing"
+msgstr "Klargør"
+
+#: ../progress.py:244
+msgid "Positions:"
+msgstr "Positioner:"
+
+#: ../progress.py:248
+msgid "Starting calculation"
+msgstr "Starter beregning"
+
+#: ../progress.py:285
+msgid "unchanged"
+msgstr "uændret"
+
+#: ../progress.py:295
+msgid "Self-consistency loop"
+msgstr "Selvkonsistensløkke"
+
+#: ../progress.py:300
+msgid "Calculating forces"
+msgstr "Beregner kræfter"
+
+#: ../progress.py:301
+msgid " (converged)"
+msgstr " (konvergeret)"
+
+#: ../pybutton.py:37
+msgid "Python"
+msgstr "Python"
+
+#: ../pybutton.py:48
+msgid "No Python code"
+msgstr "Ingen Pythonkode"
+
+#: ../pybutton.py:52
+#, python-format
+msgid ""
+"\n"
+"Title: %(title)s\n"
+"Time: %(time)s\n"
+msgstr ""
+"\n"
+"Titel: %(title)s\n"
+"Tid: %(time)s\n"
+
+#: ../pybutton.py:61
+msgid "ag: Python code"
+msgstr "ag: Pythonkode"
+
+#: ../pybutton.py:65
+msgid "Information:"
+msgstr "Information:"
+
+#: ../pybutton.py:72
+msgid "Python code:"
+msgstr "Pythonkode:"
+
+#: ../quickinfo.py:9
+msgid "Single image loaded."
+msgstr "Enkelt billede indlæst."
+
+#: ../quickinfo.py:10
+#, python-format
+msgid "Image %d loaded (0 - %d)."
+msgstr "Billede %d indlæst (0 - %d)."
+
+#: ../quickinfo.py:11
+msgid "Unit cell is fixed."
+msgstr "Enhedscelle fastholdes."
+
+#: ../quickinfo.py:12
+msgid "Unit cell varies."
+msgstr "Enhedscelle varierer."
+
+#: ../quickinfo.py:14
+#, python-format
+msgid ""
+"%s\n"
+"\n"
+"Number of atoms: %d.\n"
+"\n"
+"Unit cell:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+msgstr ""
+"%s\n"
+"\n"
+"Antal atomer: %d.\n"
+"\n"
+"Enhedscelle:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+
+#: ../quickinfo.py:29
+msgid "Quick Info"
+msgstr "Hurtig info"
+
+#: ../quickinfo.py:33
+msgid "No atoms loaded."
+msgstr "Ingen atomer indlæst."
+
+#: ../render.py:14
+msgid ""
+"    Textures can be used to highlight different parts of\n"
+"    an atomic structure. This window applies the default\n"
+"    texture to the entire structure and optionally\n"
+"    applies a different texture to subsets of atoms that\n"
+"    can be selected using the mouse.\n"
+"    An alternative selection method is based on a boolean\n"
+"    expression in the entry box provided, using the\n"
+"    variables x, y, z, or Z. For example, the expression\n"
+"    Z == 11 and x > 10 and y > 10 \n"
+"    will mark all sodium atoms with x or coordinates \n"
+"    larger than 10. In either case, the button labeled\n"
+"    `Create new texture from selection` will enable\n"
+"    to change the attributes of the current selection. \n"
+"    "
+msgstr ""
+"    Teksturer kan bruges til at fremhæve forskellige dele af en\n"
+"    atomar struktur. Dette vindue anvender standardteksturen på hele\n"
+"    strukturen, og anvender valgfrit en anden tekstur til bestemte\n"
+"    atomer som kan markeres med musen.\n"
+"    En alternativ markeringsmetode baseret på booleske udtryk\n"
+"    i et tekstfelt kan bruges med variabelnavnene x, y, z eller Z.\n"
+"    For eksempel vil udtrykket Z == 11 and x > 10 and y > 10\n"
+"    markere alle natriumatomer med x- eller y-koordinater\n"
+"    større end 10. I begge tilfælde vil knappen med teksten\n"
+"    \"Opret ny tekstur fra markering\" tillade ændring af\n"
+"    attributterne for den nuværende markering.\n"
+"    "
+
+# gemmer et billede af atomerne
+#: ../render.py:32
+msgid "Render current view in povray ... "
+msgstr "Tegn nuværende struktur i povray ... "
+
+#: ../render.py:36
+#, python-format
+msgid "Rendering %d atoms."
+msgstr "Tegner %d atomer."
+
+#: ../render.py:41
+msgid "Render constraints"
+msgstr "Tegn begrænsninger"
+
+#: ../render.py:44
+msgid "Width"
+msgstr "Bredde"
+
+#: ../render.py:45
+msgid "     Height"
+msgstr "     Højde"
+
+#: ../render.py:52
+msgid "Render unit cell"
+msgstr "Tegn _enhedscelle"
+
+#: ../render.py:61
+msgid "Line width"
+msgstr "Linjebredde"
+
+#: ../render.py:63
+msgid "Angstrom           "
+msgstr "Ångström           "
+
+#: ../render.py:73
+msgid "Set"
+msgstr "Angiv"
+
+#: ../render.py:75
+msgid "Output basename: "
+msgstr "Basisnavn for output: "
+
+#: ../render.py:77
+msgid "               Filename: "
+msgstr "               Filnavn: "
+
+#: ../render.py:88
+msgid " Default texture for atoms: "
+msgstr " Standardtekstur for atomer: "
+
+#: ../render.py:89
+msgid "    transparency: "
+msgstr "    gennemsigtighed: "
+
+#: ../render.py:90
+msgid "Define atom selection for new texture:"
+msgstr "Definér atommarkering til ny tekstur:"
+
+#: ../render.py:92
+msgid "Select"
+msgstr "Vælg"
+
+#: ../render.py:96
+msgid "Create new texture from selection"
+msgstr "Opret ny tekstur fra markering"
+
+#: ../render.py:98
+msgid "Help on textures"
+msgstr "Hjælp til teksturer"
+
+#: ../render.py:111
+msgid "Camera type: "
+msgstr "Kameratype: "
+
+#: ../render.py:112
+msgid "     Camera distance"
+msgstr "     Kameraafstand"
+
+#: ../render.py:113
+msgid "Render current frame"
+msgstr "Tegn det aktuelle billede"
+
+#: ../render.py:117
+#, python-format
+msgid "Render all %d frames"
+msgstr "Tegn alle %d billeder"
+
+#: ../render.py:122
+msgid "Transparent background"
+msgstr "Gennemsigtig baggrund"
+
+#: ../render.py:125
+msgid "Run povray       "
+msgstr "Kør povray       "
+
+#: ../render.py:128
+msgid "Keep povray files       "
+msgstr "Behold povray-filer       "
+
+#: ../render.py:131
+msgid "Show output window"
+msgstr "Vis outputvindue"
+
+#: ../render.py:212
+msgid "  transparency: "
+msgstr "  gennemsigtighed: "
+
+#: ../render.py:218
+msgid ""
+"Can not create new texture! Must have some atoms selected to create a new "
+"material!"
+msgstr ""
+"Kan ikke oprette ny tekstur! Der skal være atomer markeret for at kunne "
+"oprette nyt materiale!"
+
+#: ../repeat.py:14
+msgid "Repeat"
+msgstr "Gentag"
+
+#: ../repeat.py:21
+msgid "Set unit cell"
+msgstr "Angiv enhedscelle"
+
+#: ../rotate.py:15
+msgid "Rotate"
+msgstr "Rotér"
+
+#: ../rotate.py:17
+msgid "Rotation angles:"
+msgstr "Rotationsvinkler:"
+
+#: ../rotate.py:27
+msgid ""
+"Note:\n"
+"You can rotate freely\n"
+"with the mouse, by holding\n"
+"down mouse button 2."
+msgstr ""
+"Bemærk:\n"
+"Du kan frit rotere med\n"
+"musen ved at holde\n"
+"musetast 2 nede."
+
+#: ../scaling.py:49
+msgid "Homogeneous scaling"
+msgstr "Homogen skalering"
+
+#: ../scaling.py:59
+msgid "3D deformation   "
+msgstr "3D-deformation   "
+
+#: ../scaling.py:60
+msgid "2D deformation   "
+msgstr "2D-deformation   "
+
+#: ../scaling.py:61
+msgid "1D deformation   "
+msgstr "1D-deformation   "
+
+#: ../scaling.py:64
+msgid "Bulk"
+msgstr "Krystal"
+
+#: ../scaling.py:66
+msgid "xy-plane"
+msgstr "xy-plan"
+
+#: ../scaling.py:68
+msgid "xz-plane"
+msgstr "xz-plan"
+
+#: ../scaling.py:70
+msgid "yz-plane"
+msgstr "yz-plan"
+
+#: ../scaling.py:72
+msgid "x-axis"
+msgstr "x-akse"
+
+#: ../scaling.py:74
+msgid "y-axis"
+msgstr "y-akse"
+
+#: ../scaling.py:76
+msgid "z-axis"
+msgstr "z-akse"
+
+#: ../scaling.py:89
+msgid "Allow deformation along non-periodic directions."
+msgstr "Tillad deformation langs ikke-periodiske retninger."
+
+#. Parameters for the deformation
+#: ../scaling.py:94
+msgid "Deformation:"
+msgstr "Deformation:"
+
+#: ../scaling.py:100
+msgid "Maximal scale factor: "
+msgstr "Maksimal skaleringsfaktor: "
+
+#: ../scaling.py:103
+msgid "Scale offset: "
+msgstr "Forskydning ved skalering: "
+
+#: ../scaling.py:106
+msgid "Number of steps: "
+msgstr "Antal trin: "
+
+#: ../scaling.py:107
+msgid "Only positive deformation"
+msgstr "Kun positiv deformation"
+
+#. Atomic relaxations
+#: ../scaling.py:112
+msgid "Atomic relaxations:"
+msgstr "Atomrelakseringer:"
+
+#: ../scaling.py:116
+msgid "On   "
+msgstr "Til  "
+
+#: ../scaling.py:117
+msgid "Off"
+msgstr "Fra"
+
+#. Results
+#: ../scaling.py:128
+msgid "Results:"
+msgstr "Resultater:"
+
+#: ../scaling.py:130
+msgid "Keep original configuration"
+msgstr "Behold oprindelig konfiguration"
+
+#: ../scaling.py:132
+msgid "Load optimal configuration"
+msgstr "Indlæs optimal konfiguration"
+
+#: ../scaling.py:134
+msgid "Load all configurations"
+msgstr "Indlæs alle konfigurationer"
+
+#: ../scaling.py:143
+msgid "Strain\t\tEnergy [eV]"
+msgstr "Spænding\t\tEnergi [eV]"
+
+#: ../scaling.py:144
+msgid "Fit:"
+msgstr "Fit:"
+
+#: ../scaling.py:148
+msgid "2nd"
+msgstr "2."
+
+#: ../scaling.py:149
+msgid "3rd"
+msgstr "3."
+
+#: ../scaling.py:153
+msgid "Order of fit: "
+msgstr "Orden for fit: "
+
+#. Update display to reflect cancellation of simulation.
+#: ../scaling.py:346
+msgid "Calculation CANCELLED."
+msgstr "Beregning AFBRUDT."
+
+#. Update display to reflect succesful end of simulation.
+#: ../scaling.py:357
+msgid "Calculation completed."
+msgstr "Beregning fuldført."
+
+#: ../scaling.py:380
+msgid "No trustworthy minimum: Old configuration kept."
+msgstr "Intet troværdigt minimum: Gammel konfiguration beholdt."
+
+#: ../scaling.py:420
+#, python-format
+msgid ""
+"Insufficent data for a fit\n"
+"(only %i data points)\n"
+msgstr ""
+"Utilstrækkelige data til fit\n"
+"(kun %i datapunkter)\n"
+
+#: ../scaling.py:424
+msgid ""
+"REVERTING TO 2ND ORDER FIT\n"
+"(only 3 data points)\n"
+"\n"
+msgstr ""
+"GÅR NED TIL ANDENORDENS FIT\n"
+"(kun 3 datapunkter)\n"
+"\n"
+
+#: ../scaling.py:440
+msgid "No minimum found!"
+msgstr "Intet minimum fundet!"
+
+#: ../scaling.py:454
+msgid ""
+"\n"
+"WARNING: Minimum is outside interval\n"
+msgstr ""
+"\n"
+"ADVARSEL: Minimum ligger uden for interval\n"
+
+#: ../scaling.py:455
+msgid "It is UNRELIABLE!\n"
+msgstr "Det er UTILREGNELIGT!\n"
+
+#: ../settings.py:16
+msgid "Constraints:"
+msgstr "Begrænsninger:"
+
+#: ../settings.py:19
+msgid "release"
+msgstr "frigiv"
+
+# I dette tilfælde er constrain = fastgøre
+#: ../settings.py:23
+msgid "Constrain immobile atoms"
+msgstr "Fastgør immobile atomer"
+
+#: ../settings.py:25
+msgid "Clear all constraints"
+msgstr "Ryd alle begrænsninger"
+
+#: ../settings.py:31
+msgid "Visibility:"
+msgstr "Synlighed:"
+
+#: ../settings.py:32
+msgid "Hide"
+msgstr "Skjul"
+
+#: ../settings.py:34
+msgid "show"
+msgstr "vis"
+
+#: ../settings.py:38
+msgid "View all atoms"
+msgstr "Vis alle atomer"
+
+#: ../settings.py:44
+msgid "Miscellaneous:"
+msgstr "Diverse:"
+
+#: ../settings.py:47
+msgid "Scale atomic radii:"
+msgstr "Skalér atomradier:"
+
+#. A close button
+#: ../settings.py:52
+msgid "\n"
+msgstr "\n"
+
+#: ../setupwindow.py:51
+msgid "No crystal structure data"
+msgstr "Ingen data for krystalstruktur"
+
+#: ../setupwindow.py:62
+msgid "  ERROR: Invalid element!"
+msgstr "  FEJL: ugyldigt grundstof!"
+
+#: ../simulation.py:24
+msgid " (rerun simulation)"
+msgstr " (kør simulation igen)"
+
+#: ../simulation.py:25
+msgid " (continue simulation)"
+msgstr " (fortsæt simulation)"
+
+#: ../simulation.py:27
+msgid "Select starting configuration:"
+msgstr "Vælg startkonfiguration:"
+
+#: ../simulation.py:32
+#, python-format
+msgid "There are currently %i configurations loaded."
+msgstr "Der er i øjeblikket indlæst %i konfigurationer."
+
+#: ../simulation.py:36
+msgid "Choose which one to use as the initial configuration"
+msgstr "Vælg hvilken, der skal bruges som begyndelseskonfiguration"
+
+#: ../simulation.py:40
+#, python-format
+msgid "The first configuration %s."
+msgstr "Første konfiguration %s."
+
+#: ../simulation.py:43
+msgid "Configuration number "
+msgstr "Konfiguration nummer "
+
+#: ../simulation.py:49
+#, python-format
+msgid "The last configuration %s."
+msgstr "Sidste konfiguration %s."
+
+#: ../simulation.py:85
+msgid "Run"
+msgstr "Kør"
+
+#: ../simulation.py:105
+msgid "No calculator: Use Calculate/Set Calculator on the menu."
+msgstr "Ingen beregner: Brug Beregn/Angiv beregner i menuen."
+
+#: ../status.py:37 ../status.py:39
+msgid "Tip for status box ..."
+msgstr "Fif til statusboks ..."
+
+#. TRANSLATORS: mom refers to magnetic moment
+#: ../status.py:63
+#, python-format
+msgid " tag=%(tag)s mom=%(mom)1.2f"
+msgstr " mærke=%(tag)s mom=%(mom)1.2f"
+
+#: ../status.py:104
+msgid "dihedral"
+msgstr "dihedral"
+
+#: ../surfaceslab.py:14
+msgid ""
+"  Use this dialog to create surface slabs.  Select the element by\n"
+"writing the chemical symbol or the atomic number in the box.  Then\n"
+"select the desired surface structure.  Note that some structures can\n"
+"be created with an othogonal or a non-orthogonal unit cell, in these\n"
+"cases the non-orthogonal unit cell will contain fewer atoms.\n"
+"\n"
+"  If the structure matches the experimental crystal structure, you can\n"
+"look up the lattice constant, otherwise you have to specify it\n"
+"yourself."
+msgstr ""
+"  Brug denne dialog til at oprette overflader.  Vælg grundstoffet ved at \n"
+"skrive det kemiske symbol eller atomnummeret i boksen. Vælg så den ønskede\n"
+"overfladestruktur.  Bemærk at visse strukturer kan oprettes med både en\n"
+"ortogonal og en ikke-ortogonal enhedscelle; i disse tilfælde vil\n"
+"den ikke-ortogonale enhedscelle indeholde færre atomer.\n"
+"\n"
+"  Hvis strukturen svarer til den eksperimentelle krystalstruktur, kan\n"
+"du slå gitterkonstanten op. Ellers skal du angive den selv."
+
+#. Name, structure, orthogonal, support-nonorthogonal, function
+#: ../surfaceslab.py:26
+msgid "FCC(100)"
+msgstr "FCC(100)"
+
+#: ../surfaceslab.py:26 ../surfaceslab.py:27 ../surfaceslab.py:28
+#: ../surfaceslab.py:30
+msgid "fcc"
+msgstr "fcc"
+
+#: ../surfaceslab.py:27
+msgid "FCC(110)"
+msgstr "FCC(110)"
+
+#: ../surfaceslab.py:28
+msgid "FCC(111) non-orthogonal"
+msgstr "FCC(111) ikke-ortogonal"
+
+#: ../surfaceslab.py:30
+msgid "FCC(111) orthogonal"
+msgstr "FCC(111) ortogonal"
+
+#: ../surfaceslab.py:31
+msgid "BCC(100)"
+msgstr "BCC(100)"
+
+#: ../surfaceslab.py:31 ../surfaceslab.py:32 ../surfaceslab.py:34
+#: ../surfaceslab.py:35 ../surfaceslab.py:37
+msgid "bcc"
+msgstr "bcc"
+
+#: ../surfaceslab.py:32
+msgid "BCC(110) non-orthogonal"
+msgstr "BCC(110) ikke-ortogonal"
+
+#: ../surfaceslab.py:34
+msgid "BCC(110) orthogonal"
+msgstr "BCC(110) ortogonal"
+
+#: ../surfaceslab.py:35
+msgid "BCC(111) non-orthogonal"
+msgstr "BCC(111) ikke-ortogonal"
+
+#: ../surfaceslab.py:37
+msgid "BCC(111) orthogonal"
+msgstr "BCC(111) ortogonal"
+
+#: ../surfaceslab.py:38
+msgid "HCP(0001) non-orthogonal"
+msgstr "HCP(0001) ikke-ortogonal"
+
+#: ../surfaceslab.py:38 ../surfaceslab.py:40 ../surfaceslab.py:41
+msgid "hcp"
+msgstr "hcp"
+
+#: ../surfaceslab.py:40
+msgid "HCP(0001) orthogonal"
+msgstr "HCP(0001) ortogonal"
+
+#: ../surfaceslab.py:41
+msgid "HCP(10-10) orthogonal"
+msgstr "HCP(10-10) ortogonal"
+
+#: ../surfaceslab.py:43
+msgid "DIAMOND(100) orthogonal"
+msgstr "DIAMOND(100) ortogonal"
+
+#: ../surfaceslab.py:43 ../surfaceslab.py:45
+msgid "diamond"
+msgstr "diamant"
+
+#: ../surfaceslab.py:45
+msgid "DIAMOND(111) non-orthogonal"
+msgstr "DIAMANT(111) ikke-ortogonal"
+
+#: ../surfaceslab.py:60
+msgid "Surface"
+msgstr "Overflade"
+
+#: ../surfaceslab.py:90
+msgid "Lattice constant: "
+msgstr "Gitterkonstant: "
+
+#: ../surfaceslab.py:97
+msgid "a:"
+msgstr "a:"
+
+#: ../surfaceslab.py:109
+#, python-format
+msgid "(%.1f %% of ideal)"
+msgstr "(%.1f %% af ideel)"
+
+#: ../surfaceslab.py:126
+msgid "Size: \tx: "
+msgstr "Størr.:\tx: "
+
+#: ../surfaceslab.py:128
+msgid "\t\ty: "
+msgstr "\t\ty: "
+
+#: ../surfaceslab.py:130
+msgid "      \t\tz: "
+msgstr "      \t\tz: "
+
+#: ../surfaceslab.py:131
+msgid " layers,  "
+msgstr " lag,     "
+
+#: ../surfaceslab.py:132
+msgid " Å vacuum"
+msgstr " Å vakuum"
+
+#: ../surfaceslab.py:133
+msgid "\t\tNo size information yet."
+msgstr "\t\tEndnu ingen størrelsesoplysninger."
+
+#. Buttons
+#: ../surfaceslab.py:142
+msgid "Creating a surface slab."
+msgstr "Oprettelse af overflade."
+
+#: ../surfaceslab.py:212
+#, python-format
+msgid "%i atoms."
+msgstr "%i atomer."
+
+#: ../surfaceslab.py:224
+msgid "No structure specified!"
+msgstr "Ingen struktur angivet!"
+
+# %s ~ BCC
+#: ../surfaceslab.py:233
+#, python-format
+msgid "%(struct)s lattice constant unknown for %(element)s."
+msgstr "%(struct)s-gitterkonstant ukendt for %(element)s."
+
+#: ../widgets.py:53 ../widgets.py:80
+msgid "Help"
+msgstr "Hjælp"
+
+#: ../widgets.py:97
+msgid "Clear constraint"
+msgstr "Ryd begrænsninger"
+
+#~ msgid "  %8.3f, %8.3f, %8.3f eV/Å\n"
+#~ msgstr "  %8.3f, %8.3f, %8.3f eV/Å\n"
+
+#~ msgid "%s (a=%.3f Å)"
+#~ msgstr "%s (a=%.3f Å)"
+
+#~ msgid "  %s: %s, Z=%i, %s"
+#~ msgstr "  %s: %s, Z=%i, %s"
+
+#~ msgid " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+#~ msgstr " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+
+#~ msgid " %s-%s: %.3f Å"
+#~ msgstr " %s-%s: %.3f Å"
+
+#~ msgid " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+#~ msgstr " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+
+#~ msgid "dihedral %s->%s->%s->%s: %.1f°"
+#~ msgstr "dihedral %s->%s->%s->%s: %.1f°"
+
+#~ msgid "c:"
+#~ msgstr "c:"
+
+#~ msgid "\t\t%.2f Å x %.2f Å x %.2f Å,  %i atoms."
+#~ msgstr "\t\t%.2f Å x %.2f Å x %.2f Å,  %i atomer."
+
+#~ msgid "FILE"
+#~ msgstr "FIL"
+
+#~ msgid "%prog [options] [file[, file2, ...]]"
+#~ msgstr "%prog [tilvalg] [fil[, fil2, ...]]"
+
+#~ msgid "NUMBER"
+#~ msgstr "NUMMER"
+
+#~ msgid ""
+#~ "Pick image(s) from trajectory.  NUMBER can be a single number (use a "
+#~ "negative number to count from the back) or a range: start:stop:step, "
+#~ "where the \":step\" part can be left out - default values are 0:nimages:1."
+#~ msgstr ""
+#~ "Vælg billeder fra traj-fil.  NUMMER kan være et enkelt tal (brug et "
+#~ "negativt tal til at tælle bagfra) eller et interval på formen start:stop:"
+#~ "trin, hvor elementet \":trin\" kan udelades.  Standardværdi er 0:"
+#~ "antalbilleder:1."
+
+#~ msgid "I"
+#~ msgstr "I"
+
+#~ msgid ""
+#~ "0: Don't show unit cell.  1: Show unit cell.  2: Show all of unit cell."
+#~ msgstr ""
+#~ "0: Vis ikke enhedscellen.  1: Vis enhedscellen.  2: Vis hele enhedscellen."
+
+#~ msgid "Repeat unit cell.  Use \"-r 2\" or \"-r 2,3,1\"."
+#~ msgstr "Gentag enhedscellen.  Brug \"-r 2\" eller \"-r 2,3,1\"."
+
+#~ msgid "Examples: \"-R -90x\", \"-R 90z,-30x\"."
+#~ msgstr "Eksempler: \"-R -90x\", \"-R 90z,-30x\"."
+
+#~ msgid "Write configurations to FILE."
+#~ msgstr "Skriv konfigurationer til FIL."
+
+#~ msgid "EXPR"
+#~ msgstr "UDTRYK"
+
+#~ msgid ""
+#~ "Plot x,y1,y2,... graph from configurations or write data to sdtout in "
+#~ "terminal mode.  Use the symbols: i, s, d, fmax, e, ekin, A, R, E and F.  "
+#~ "See https://wiki.fysik.dtu.dk/ase/ase/gui.html#plotting-data for more "
+#~ "details."
+#~ msgstr ""
+#~ "Tegn graf for x,y1,y2,... fra konfigurationer, eller skriv data til "
+#~ "stdout i teksttilstand.  Brug symbolerne i, s, d, fmax, e, ekin, A, R, E "
+#~ "og F.  Yderligere detaljer kan findes på https://wiki.fysik.dtu.dk/ase/"
+#~ "ase/gui.html#plotting-data for more details."
+
+#~ msgid "Run in terminal window - no GUI."
+#~ msgstr "Kør i terminalvindue - uden grafisk grænseflade."
+
+#~ msgid "Read ANEB data."
+#~ msgstr "Læs ANEB-data."
+
+#~ msgid "N"
+#~ msgstr "N"
+
+#~ msgid "Interpolate N images between 2 given images."
+#~ msgstr "Interpolér N billeder mellem to givne billeder."
+
+#~ msgid "Draw bonds between atoms."
+#~ msgstr "Tegn bindinger mellem atomer."
diff --git a/ase/gui/po/en_GB/LC_MESSAGES/ag.po b/ase/gui/po/en_GB/LC_MESSAGES/ag.po
new file mode 100644
index 0000000..dcbfbd5
--- /dev/null
+++ b/ase/gui/po/en_GB/LC_MESSAGES/ag.po
@@ -0,0 +1,3165 @@
+# English translations for ASE package
+# Copyright (C) 2011 CAMD
+# This file is distributed under the same license as the ASE package.
+#
+# Ask Hjorth Larsen <askhl at fysik.dtu.dk>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ase-3.5.2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-02-01 16:08+0100\n"
+"PO-Revision-Date: 2012-02-01 16:10+0100\n"
+"Last-Translator: Ask Hjorth Larsen <askhl at fysik.dtu.dk>\n"
+"Language-Team: English (British) <ase-developers at listserv.fysik.dtu.dk>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en_GB\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../ag.py:130
+msgid ""
+"\n"
+"An exception occurred!  Please report the issue to\n"
+"ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if\n"
+"it was a user error, so that a better error message can be provided\n"
+"next time."
+msgstr ""
+"\n"
+"An exception occurred!  Please report the issue to\n"
+"ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if\n"
+"it was a user error, so that a better error message can be provided\n"
+"next time."
+
+#. Asap and GPAW may be imported if selected.
+#: ../calculator.py:18
+msgid ""
+"To make most calculations on the atoms, a Calculator object must first\n"
+"be associated with it.  ASE supports a number of calculators, supporting\n"
+"different elements, and implementing different physical models for the\n"
+"interatomic interactions."
+msgstr ""
+"To make most calculations on the atoms, a Calculator object must first\n"
+"be associated with it.  ASE supports a number of calculators, supporting\n"
+"different elements, and implementing different physical models for the\n"
+"interatomic interactions."
+
+#. Informational text about the calculators
+#: ../calculator.py:26
+msgid ""
+"The Lennard-Jones pair potential is one of the simplest\n"
+"possible models for interatomic interactions, mostly\n"
+"suitable for noble gasses and model systems.\n"
+"\n"
+"Interactions are described by an interaction length and an\n"
+"interaction strength."
+msgstr ""
+"The Lennard-Jones pair potential is one of the simplest\n"
+"possible models for interatomic interactions, mostly\n"
+"suitable for noble gasses and model systems.\n"
+"\n"
+"Interactions are described by an interaction length and an\n"
+"interaction strength."
+
+#: ../calculator.py:35
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au, the Al potential is however not suitable for materials\n"
+"science application, as the stacking fault energy is wrong.\n"
+"\n"
+"A number of parameter sets are provided.\n"
+"\n"
+"<b>Default parameters:</b>\n"
+"\n"
+"The default EMT parameters, as published in K. W. Jacobsen,\n"
+"P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternative Cu, Ag and Au:</b>\n"
+"\n"
+"An alternative set of parameters for Cu, Ag and Au,\n"
+"reoptimized to experimental data including the stacking\n"
+"fault energies by Torben Rasmussen (partly unpublished).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parameters for Ruthenium, as published in J. Gavnholt and\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metallic glasses:</b>\n"
+"\n"
+"Parameters for MgCu and CuZr metallic glasses. MgCu\n"
+"parameters are in N. P. Bailey, J. Schiøtz and\n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).\n"
+"CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+msgstr ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au, the Al potential is however not suitable for materials\n"
+"science application, as the stacking fault energy is wrong.\n"
+"\n"
+"A number of parameter sets are provided.\n"
+"\n"
+"<b>Default parameters:</b>\n"
+"\n"
+"The default EMT parameters, as published in K. W. Jacobsen,\n"
+"P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternative Cu, Ag and Au:</b>\n"
+"\n"
+"An alternative set of parameters for Cu, Ag and Au,\n"
+"reoptimised to experimental data including the stacking\n"
+"fault energies by Torben Rasmussen (partly unpublished).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parameters for Ruthenium, as published in J. Gavnholt and\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metallic glasses:</b>\n"
+"\n"
+"Parameters for MgCu and CuZr metallic glasses. MgCu\n"
+"parameters are in N. P. Bailey, J. Schiøtz and\n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).\n"
+"CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+
+#: ../calculator.py:70
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au.  In addition, this implementation allows for the use of\n"
+"H, N, O and C adatoms, although the description of these is\n"
+"most likely not very good.\n"
+"\n"
+"<b>This is the ASE implementation of EMT.</b> For large\n"
+"simulations the ASAP implementation is more suitable; this\n"
+"implementation is mainly to make EMT available when ASAP is\n"
+"not installed.\n"
+msgstr ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au.  In addition, this implementation allows for the use of\n"
+"H, N, O and C adatoms, although the description of these is\n"
+"most likely not very good.\n"
+"\n"
+"<b>This is the ASE implementation of EMT.</b> For large\n"
+"simulations the ASAP implementation is more suitable; this\n"
+"implementation is mainly to make EMT available when ASAP is\n"
+"not installed.\n"
+
+#: ../calculator.py:85
+msgid ""
+"The Brenner potential is a reactive bond-order potential for\n"
+"carbon and hydrocarbons.  As a bond-order potential, it takes\n"
+"into account that carbon orbitals can hybridize in different\n"
+"ways, and that carbon can form single, double and triple\n"
+"bonds.  That the potential is reactive means that it can\n"
+"handle gradual changes in the bond order as chemical bonds\n"
+"are formed or broken.\n"
+"\n"
+"The Brenner potential is implemented in Asap, based on a\n"
+"C implentation published at http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"The potential is documented here:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+msgstr ""
+"The Brenner potential is a reactive bond-order potential for\n"
+"carbon and hydrocarbons.  As a bond-order potential, it takes\n"
+"into account that carbon orbitals can hybridise in different\n"
+"ways, and that carbon can form single, double and triple\n"
+"bonds.  That the potential is reactive means that it can\n"
+"handle gradual changes in the bond order as chemical bonds\n"
+"are formed or broken.\n"
+"\n"
+"The Brenner potential is implemented in Asap, based on a\n"
+"C implentation published at http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"The potential is documented here:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+
+#: ../calculator.py:107
+msgid ""
+"GPAW implements Density Functional Theory using a\n"
+"<b>G</b>rid-based real-space representation of the wave\n"
+"functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave\n"
+"method for handling the core regions.  \n"
+msgstr ""
+"GPAW implements Density Functional Theory using a\n"
+"<b>G</b>rid-based real-space representation of the wave\n"
+"functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave\n"
+"method for handling the core regions.  \n"
+
+#: ../calculator.py:114
+msgid ""
+"FHI-aims is an external package implementing density \n"
+"functional theory and quantum chemical methods using \n"
+"all-electron methods and a numeric local orbital basis set. \n"
+"For full details, see http://www.fhi-berlin.mpg.de/aims/ \n"
+"or Comp. Phys. Comm. v180 2175 (2009). The ASE \n"
+"documentation contains information on the keywords and \n"
+"functionalities available within this interface. \n"
+msgstr ""
+"FHI-aims is an external package implementing density \n"
+"functional theory and quantum chemical methods using \n"
+"all-electron methods and a numeric local orbital basis set. \n"
+"For full details, see http://www.fhi-berlin.mpg.de/aims/ \n"
+"or Comp. Phys. Comm. v180 2175 (2009). The ASE \n"
+"documentation contains information on the keywords and \n"
+"functionalities available within this interface. \n"
+
+#: ../calculator.py:124
+msgid ""
+"WARNING:\n"
+"Your system seems to have more than zero but less than \n"
+"three periodic dimensions. Please check that this is \n"
+"really what you want to compute. Assuming full \n"
+"3D periodicity for this calculator."
+msgstr ""
+"WARNING:\n"
+"Your system seems to have more than zero but less than \n"
+"three periodic dimensions. Please check that this is \n"
+"really what you want to compute. Assuming full \n"
+"3D periodicity for this calculator."
+
+#: ../calculator.py:131
+msgid ""
+"VASP is an external package implementing density \n"
+"functional functional theory using pseudopotentials \n"
+"or the projector-augmented wave method together \n"
+"with a plane wave basis set. For full details, see\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+msgstr ""
+"VASP is an external package implementing density \n"
+"functional functional theory using pseudopotentials \n"
+"or the projector-augmented wave method together \n"
+"with a plane wave basis set. For full details, see\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+
+#: ../calculator.py:140
+msgid "Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+msgstr "Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+
+#: ../calculator.py:141
+msgid "Alternative Cu, Ag and Au"
+msgstr "Alternative Cu, Ag and Au"
+
+#: ../calculator.py:142
+msgid "Ruthenium"
+msgstr "Ruthenium"
+
+#: ../calculator.py:143
+msgid "CuMg and CuZr metallic glass"
+msgstr "CuMg and CuZr metallic glass"
+
+#: ../calculator.py:158
+msgid "Select calculator"
+msgstr "Select calculator"
+
+#: ../calculator.py:164
+msgid "Calculator:"
+msgstr "Calculator:"
+
+#. No calculator (the default)
+#: ../calculator.py:167
+msgid "None"
+msgstr "None"
+
+#: ../calculator.py:172
+msgid "Lennard-Jones (ASAP)"
+msgstr "Lennard-Jones (ASAP)"
+
+#: ../calculator.py:173 ../calculator.py:206 ../calculator.py:215
+#: ../calculator.py:224
+msgid "Setup"
+msgstr "Setup"
+
+#: ../calculator.py:180
+msgid "EMT - Effective Medium Theory (ASAP)"
+msgstr "EMT - Effective Medium Theory (ASAP)"
+
+#: ../calculator.py:192
+msgid "EMT - Effective Medium Theory (ASE)"
+msgstr "EMT - Effective Medium Theory (ASE)"
+
+#: ../calculator.py:198
+msgid "Brenner Potential (ASAP)"
+msgstr "Brenner Potential (ASAP)"
+
+#: ../calculator.py:204
+msgid "Density Functional Theory (GPAW)"
+msgstr "Density Functional Theory (GPAW)"
+
+#: ../calculator.py:213
+msgid "Density Functional Theory (FHI-aims)"
+msgstr "Density Functional Theory (FHI-aims)"
+
+#: ../calculator.py:222
+msgid "Density Functional Theory (VASP)"
+msgstr "Density Functional Theory (VASP)"
+
+#: ../calculator.py:235
+msgid "Check that the calculator is reasonable."
+msgstr "Check that the calculator is reasonable."
+
+#: ../calculator.py:298 ../simulation.py:114
+msgid "No atoms present"
+msgstr "No atoms present"
+
+#: ../calculator.py:388 ../calculator.py:422 ../calculator.py:456
+msgid "ASAP is not installed. (Failed to import asap3)"
+msgstr "ASAP is not installed. (Failed to import asap3)"
+
+#: ../calculator.py:391
+msgid "You must set up the Lennard-Jones parameters"
+msgstr "You must set up the Lennard-Jones parameters"
+
+#: ../calculator.py:396
+msgid "Could not create useful Lennard-Jones calculator."
+msgstr "Could not create useful Lennard-Jones calculator."
+
+#: ../calculator.py:430
+msgid "Could not attach EMT calculator to the atoms."
+msgstr "Could not attach EMT calculator to the atoms."
+
+#: ../calculator.py:472 ../calculator.py:484
+msgid "GPAW is not installed. (Failed to import gpaw)"
+msgstr "GPAW is not installed. (Failed to import gpaw)"
+
+#: ../calculator.py:475
+msgid "You must set up the GPAW parameters"
+msgstr "You must set up the GPAW parameters"
+
+#: ../calculator.py:516
+msgid "You must set up the FHI-aims parameters"
+msgstr "You must set up the FHI-aims parameters"
+
+#: ../calculator.py:530
+msgid "You must set up the VASP parameters"
+msgstr "You must set up the VASP parameters"
+
+#: ../calculator.py:554
+#, python-format
+msgid "Element %(sym)s not allowed by the '%(name)s' calculator"
+msgstr "Element %(sym)s not allowed by the '%(name)s' calculator"
+
+#: ../calculator.py:561
+msgid "Info"
+msgstr "Info"
+
+#: ../calculator.py:577
+msgid "Lennard-Jones parameters"
+msgstr "Lennard-Jones parameters"
+
+#: ../calculator.py:589
+msgid "Specify the Lennard-Jones parameters here"
+msgstr "Specify the Lennard-Jones parameters here"
+
+#: ../calculator.py:592
+msgid "Epsilon (eV):"
+msgstr "Epsilon (eV):"
+
+#: ../calculator.py:596
+msgid "Sigma (Å):"
+msgstr "Sigma (Å):"
+
+#. TRANSLATORS: Shift roughly means adjust (about a potential)
+#: ../calculator.py:600
+msgid "Shift to make smooth at cutoff"
+msgstr "Shift to make smooth at cutoff"
+
+#: ../calculator.py:681
+msgid "GPAW parameters"
+msgstr "GPAW parameters"
+
+#. label = gtk.Label("Specify the GPAW parameters here")
+#. pack(vbox, [label])
+#. Print some info
+#: ../calculator.py:696 ../calculator.py:985 ../calculator.py:1473
+#, python-format
+msgid "%i atoms.\n"
+msgstr "%i atoms.\n"
+
+#: ../calculator.py:698
+#, python-format
+msgid "Orthogonal unit cell: %.2f x %.2f x %.2f Å."
+msgstr "Orthogonal unit cell: %.2f x %.2f x %.2f Å."
+
+#: ../calculator.py:700
+msgid "Non-orthogonal unit cell:\n"
+msgstr "Non-orthogonal unit cell:\n"
+
+#: ../calculator.py:710 ../calculator.py:1001 ../calculator.py:1488
+msgid "Exchange-correlation functional: "
+msgstr "Exchange-correlation functional: "
+
+#. Grid spacing
+#: ../calculator.py:714
+msgid "Grid spacing"
+msgstr "Grid spacing"
+
+#: ../calculator.py:718 ../graphene.py:67 ../graphene.py:79 ../graphene.py:102
+#: ../nanotube.py:47 ../surfaceslab.py:97
+msgid "Å"
+msgstr "Å"
+
+#: ../calculator.py:719
+msgid "Grid points"
+msgstr "Grid points"
+
+#: ../calculator.py:728
+#, python-format
+msgid "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+msgstr "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+
+#: ../calculator.py:754 ../calculator.py:1016 ../calculator.py:1524
+msgid "k-points  k = ("
+msgstr "k-points  k = ("
+
+#: ../calculator.py:758 ../calculator.py:1020
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å"
+msgstr "k-points x size:  (%.1f, %.1f, %.1f) Å"
+
+#. Spin polarized
+#: ../calculator.py:763 ../calculator.py:1486
+msgid "Spin polarized"
+msgstr "Spin polarised"
+
+#: ../calculator.py:769
+msgid "FD - Finite Difference (grid) mode"
+msgstr "FD - Finite Difference (grid) mode"
+
+#: ../calculator.py:770
+msgid "LCAO - Linear Combination of Atomic Orbitals"
+msgstr "LCAO - Linear Combination of Atomic Orbitals"
+
+#: ../calculator.py:773
+msgid "Mode: "
+msgstr "Mode: "
+
+#: ../calculator.py:775
+msgid "sz - Single Zeta"
+msgstr "sz - Single Zeta"
+
+#: ../calculator.py:776
+msgid "szp - Single Zeta polarized"
+msgstr "szp - Single Zeta polarised"
+
+#: ../calculator.py:777
+msgid "dzp - Double Zeta polarized"
+msgstr "dzp - Double Zeta polarised"
+
+#. dzp
+#: ../calculator.py:779
+msgid "Basis functions: "
+msgstr "Basis functions: "
+
+#. Mixer
+#: ../calculator.py:785
+msgid "Non-standard mixer parameters"
+msgstr "Non-standard mixer parameters"
+
+#: ../calculator.py:981
+msgid "FHI-aims parameters"
+msgstr "FHI-aims parameters"
+
+#: ../calculator.py:988
+msgid "Periodic geometry, unit cell is:\n"
+msgstr "Periodic geometry, unit cell is:\n"
+
+#: ../calculator.py:993
+msgid "Non-periodic geometry.\n"
+msgstr "Non-periodic geometry.\n"
+
+#: ../calculator.py:1000
+msgid "Hirshfeld-based dispersion correction"
+msgstr "Hirshfeld-based dispersion correction"
+
+#. Spin polarized, charge, relativity
+#: ../calculator.py:1026
+msgid "Spin / initial moment "
+msgstr "Spin / initial moment "
+
+#: ../calculator.py:1044
+msgid "   Charge"
+msgstr "   Charge"
+
+#: ../calculator.py:1046
+msgid "   Relativity"
+msgstr "   Relativity"
+
+#: ../calculator.py:1048
+msgid " Threshold"
+msgstr " Threshold"
+
+#. self-consistency criteria
+#: ../calculator.py:1053
+msgid "Self-consistency convergence:"
+msgstr "Self-consistency convergence:"
+
+#: ../calculator.py:1066
+msgid "Compute forces"
+msgstr "Compute forces"
+
+#. XXX: use gtk table for layout.  Spaces will not work well otherwise
+#. (depend on fonts, widget style, ...)
+#. TRANSLATORS: Don't care too much about these, just get approximately
+#. the same string lengths
+#: ../calculator.py:1077
+msgid "Energy:                 "
+msgstr "Energy:                 "
+
+#: ../calculator.py:1079
+msgid " eV   Sum of eigenvalues:  "
+msgstr " eV   Sum of eigenvalues:  "
+
+#: ../calculator.py:1081 ../calculator.py:1559
+msgid " eV"
+msgstr " eV"
+
+#: ../calculator.py:1082
+msgid "Electron density: "
+msgstr "Electron density: "
+
+#: ../calculator.py:1084
+msgid "        Force convergence:  "
+msgstr "        Force convergence:  "
+
+#: ../calculator.py:1086
+msgid " eV/Ang  "
+msgstr " eV/Ang  "
+
+#: ../calculator.py:1099 ../calculator.py:1570
+msgid "Additional keywords: "
+msgstr "Additional keywords: "
+
+#. run command and species defaults:
+#: ../calculator.py:1113
+msgid "FHI-aims execution command: "
+msgstr "FHI-aims execution command: "
+
+#: ../calculator.py:1115 ../calculator.py:1587
+msgid "Directory for species defaults: "
+msgstr "Directory for species defaults: "
+
+#: ../calculator.py:1127 ../calculator.py:1595
+msgid "Set Defaults"
+msgstr "Set Defaults"
+
+#: ../calculator.py:1129
+msgid "Import control.in"
+msgstr "Import control.in"
+
+#: ../calculator.py:1131
+msgid "Export control.in"
+msgstr "Export control.in"
+
+#: ../calculator.py:1317
+msgid "Export parameters ... "
+msgstr "Export parameters ... "
+
+#: ../calculator.py:1337
+msgid "Import control.in file ... "
+msgstr "Import control.in file ... "
+
+#: ../calculator.py:1393 ../calculator.py:1907
+#, python-format
+msgid ""
+"Please use the facilities provided in this window to manipulate the keyword: "
+"%s!"
+msgstr ""
+"Please use the facilities provided in this window to manipulate the keyword: "
+"%s!"
+
+#: ../calculator.py:1396
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/aims.py."
+msgstr ""
+"Don't know this keyword: %s\n"
+"\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/aims.py."
+
+#: ../calculator.py:1469
+msgid "VASP parameters"
+msgstr "VASP parameters"
+
+#: ../calculator.py:1475
+msgid "Periodic geometry, unit cell is: \n"
+msgstr "Periodic geometry, unit cell is: \n"
+
+#: ../calculator.py:1527
+msgid ")    Cutoff: "
+msgstr ")    Cutoff: "
+
+#: ../calculator.py:1528
+msgid "    Precision: "
+msgstr "    Precision: "
+
+#: ../calculator.py:1530
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å       "
+msgstr "k-points x size:  (%.1f, %.1f, %.1f) Å       "
+
+#: ../calculator.py:1546
+msgid "Smearing: "
+msgstr "Smearing: "
+
+#: ../calculator.py:1548
+msgid " order: "
+msgstr " order: "
+
+#: ../calculator.py:1550
+msgid " width: "
+msgstr " width: "
+
+#: ../calculator.py:1557
+msgid "Self-consistency convergence: "
+msgstr "Self-consistency convergence: "
+
+#. run command and location of POTCAR files:
+#: ../calculator.py:1583
+msgid "VASP execution command: "
+msgstr "VASP execution command: "
+
+#: ../calculator.py:1597
+msgid "Import VASP files"
+msgstr "Import VASP files"
+
+#: ../calculator.py:1599
+msgid "Export VASP files"
+msgstr "Export VASP files"
+
+#: ../calculator.py:1810
+msgid "<b>WARNING:</b> cutoff energy is lower than recommended minimum!"
+msgstr "<b>WARNING:</b> cutoff energy is lower than recommended minimum!"
+
+#: ../calculator.py:1862
+msgid "Import VASP input files: choose directory ... "
+msgstr "Import VASP input files: choose directory ... "
+
+#: ../calculator.py:1877
+msgid "Export VASP input files: choose directory ... "
+msgstr "Export VASP input files: choose directory ... "
+
+#: ../calculator.py:1910
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/vasp.py."
+msgstr ""
+"Don't know this keyword: %s\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/vasp.py."
+
+#: ../colors.py:24
+msgid "Colors"
+msgstr "Colours"
+
+#. Upper left: Choose how the atoms are colored.
+#: ../colors.py:41
+msgid "Choose how the atoms are colored:"
+msgstr "Choose how the atoms are coloured:"
+
+#: ../colors.py:43
+msgid "By atomic number, default \"jmol\" colors"
+msgstr "By atomic number, default \"jmol\" colours"
+
+#: ../colors.py:45
+msgid "By atomic number, user specified"
+msgstr "By atomic number, user specified"
+
+#: ../colors.py:46
+msgid "By tag"
+msgstr "By tag"
+
+#: ../colors.py:47
+msgid "By force"
+msgstr "By force"
+
+#: ../colors.py:48
+msgid "By velocity"
+msgstr "By velocity"
+
+#: ../colors.py:49
+msgid "Manually specified"
+msgstr "Manually specified"
+
+#: ../colors.py:50
+msgid "All the same color"
+msgstr "All the same colour"
+
+#. Now fill in the box for additional information in case the force is used.
+#: ../colors.py:60
+msgid "This should not be displayed!"
+msgstr "This should not be displayed!"
+
+#: ../colors.py:65 ../colors.py:82 ../rotate.py:25
+msgid "Update"
+msgstr "Update"
+
+#: ../colors.py:67 ../colors.py:84
+msgid "Min: "
+msgstr "Min: "
+
+#: ../colors.py:69 ../colors.py:86
+msgid "  Max: "
+msgstr "  Max: "
+
+#: ../colors.py:71 ../colors.py:88
+msgid "  Steps: "
+msgstr "  Steps: "
+
+#: ../colors.py:95
+msgid "Create a color scale:"
+msgstr "Create a colour scale:"
+
+#: ../colors.py:98
+msgid "Black - white"
+msgstr "Black - white"
+
+#: ../colors.py:99
+msgid "Black - red - yellow - white"
+msgstr "Black - red - yellow - white"
+
+#: ../colors.py:100
+msgid "Black - green - white"
+msgstr "Black - green - white"
+
+#: ../colors.py:101
+msgid "Black - blue - cyan"
+msgstr "Black - blue - cyan"
+
+#: ../colors.py:102
+msgid "Hue"
+msgstr "Hue"
+
+#: ../colors.py:103
+msgid "Named colors"
+msgstr "Named colours"
+
+#: ../colors.py:109
+msgid "Create"
+msgstr "Create"
+
+#: ../colors.py:367
+#, python-format
+msgid "Max force: %.2f (this frame), %.2f (all frames)"
+msgstr "Max force: %.2f (this frame), %.2f (all frames)"
+
+#: ../colors.py:369
+#, python-format
+msgid "Max force: %.2f."
+msgstr "Max force: %.2f."
+
+#: ../colors.py:383
+#, python-format
+msgid "Max velocity: %.2f (this frame), %.2f (all frames)"
+msgstr "Max velocity: %.2f (this frame), %.2f (all frames)"
+
+#: ../colors.py:385
+#, python-format
+msgid "Max velocity: %.2f."
+msgstr "Max velocity: %.2f."
+
+#: ../colors.py:426
+msgid "ERROR"
+msgstr "ERROR"
+
+#: ../colors.py:455
+msgid "ERR"
+msgstr "ERR"
+
+#: ../colors.py:542
+msgid "Incorrect color specification"
+msgstr "Incorrect colour specification"
+
+#: ../constraints.py:13 ../widgets.py:89
+msgid "Constraints"
+msgstr "Constraints"
+
+#: ../constraints.py:15 ../constraints.py:18 ../settings.py:17
+#: ../widgets.py:91 ../widgets.py:94
+msgid "Constrain"
+msgstr "Constrain"
+
+#: ../constraints.py:16 ../settings.py:20 ../settings.py:35 ../widgets.py:92
+msgid " selected atoms"
+msgstr " selected atoms"
+
+#: ../constraints.py:19 ../widgets.py:95
+msgid " immobile atoms:"
+msgstr " immobile atoms:"
+
+#: ../constraints.py:21
+msgid "Unconstrain"
+msgstr "Unconstrain"
+
+#: ../constraints.py:22
+msgid " selected atoms:"
+msgstr " selected atoms:"
+
+#: ../constraints.py:24
+msgid "Clear constraints"
+msgstr "Clear constraints"
+
+#: ../constraints.py:26 ../dft.py:29 ../settings.py:53 ../widgets.py:60
+#: ../widgets.py:99
+msgid "Close"
+msgstr "Close"
+
+#: ../crystal.py:16
+msgid ""
+"  Use this dialog to create crystal lattices. First select the structure,\n"
+"  either from a set of common crystal structures, or by space group "
+"description.\n"
+"  Then add all other lattice parameters.\n"
+"\n"
+"  If an experimental crystal structure is available for an atom, you can\n"
+"  look up the crystal type and lattice constant, otherwise you have to "
+"specify it\n"
+"  yourself.  "
+msgstr ""
+"  Use this dialog to create crystal lattices. First select the structure,\n"
+"  either from a set of common crystal structures, or by space group "
+"description.\n"
+"  Then add all other lattice parameters.\n"
+"\n"
+"  If an experimental crystal structure is available for an atom, you can\n"
+"  look up the crystal type and lattice constant, otherwise you have to "
+"specify it\n"
+"  yourself.  "
+
+#: ../crystal.py:33
+#, python-format
+msgid " %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>"
+msgstr " %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>"
+
+#: ../crystal.py:58
+msgid "Create Bulk Crystal by Spacegroup"
+msgstr "Create Bulk Crystal by Spacegroup"
+
+#: ../crystal.py:72
+msgid "Number: 1"
+msgstr "Number: 1"
+
+#: ../crystal.py:73
+msgid "Lattice: "
+msgstr "Lattice: "
+
+#: ../crystal.py:73
+msgid "\tSpace group: "
+msgstr "\tSpace group: "
+
+#: ../crystal.py:77
+msgid "Size: x: "
+msgstr "Size: x: "
+
+#: ../crystal.py:78 ../crystal.py:138
+msgid "  y: "
+msgstr "  y: "
+
+#: ../crystal.py:79 ../crystal.py:139
+msgid "  z: "
+msgstr "  z: "
+
+#: ../crystal.py:80 ../surfaceslab.py:127 ../surfaceslab.py:129
+msgid " unit cells"
+msgstr " unit cells"
+
+#: ../crystal.py:92 ../crystal.py:96 ../crystal.py:100 ../crystal.py:104
+#: ../crystal.py:108 ../crystal.py:112
+msgid "free"
+msgstr "free"
+
+#: ../crystal.py:93 ../crystal.py:102
+msgid "equals b"
+msgstr "equals b"
+
+#: ../crystal.py:94 ../crystal.py:98
+msgid "equals c"
+msgstr "equals c"
+
+#: ../crystal.py:95 ../crystal.py:99 ../crystal.py:103 ../crystal.py:107
+#: ../crystal.py:111 ../crystal.py:115
+msgid "fixed"
+msgstr "fixed"
+
+#: ../crystal.py:97 ../crystal.py:101
+msgid "equals a"
+msgstr "equals a"
+
+#: ../crystal.py:105 ../crystal.py:114
+msgid "equals beta"
+msgstr "equals beta"
+
+#: ../crystal.py:106 ../crystal.py:110
+msgid "equals gamma"
+msgstr "equals gamma"
+
+#: ../crystal.py:109 ../crystal.py:113
+msgid "equals alpha"
+msgstr "equals alpha"
+
+#: ../crystal.py:119
+msgid "Lattice parameters"
+msgstr "Lattice parameters"
+
+#: ../crystal.py:120
+msgid "\t\ta:\t"
+msgstr "\t\ta:\t"
+
+#: ../crystal.py:121
+msgid "\talpha:\t"
+msgstr "\talpha:\t"
+
+#: ../crystal.py:122
+msgid "\t\tb:\t"
+msgstr "\t\tb:\t"
+
+#: ../crystal.py:123
+msgid "\tbeta:\t"
+msgstr "\tbeta:\t"
+
+#: ../crystal.py:124
+msgid "\t\tc:\t"
+msgstr "\t\tc:\t"
+
+#: ../crystal.py:125
+msgid "\tgamma:\t"
+msgstr "\tgamma:\t"
+
+#: ../crystal.py:126 ../surfaceslab.py:99
+msgid "Get from database"
+msgstr "Get from database"
+
+#: ../crystal.py:131
+msgid "Basis: "
+msgstr "Basis: "
+
+#: ../crystal.py:137
+msgid "  Element:\t"
+msgstr "  Element:\t"
+
+#: ../crystal.py:137
+msgid "\tx: "
+msgstr "\tx: "
+
+#: ../crystal.py:157
+msgid "Creating a crystal."
+msgstr "Creating a crystal."
+
+#: ../crystal.py:202
+#, python-format
+msgid "Symbol: %s"
+msgstr "Symbol: %s"
+
+#: ../crystal.py:207
+#, python-format
+msgid "Number: %s"
+msgstr "Number: %s"
+
+#: ../crystal.py:210
+msgid "Invalid Spacegroup!"
+msgstr "Invalid Spacegroup!"
+
+#: ../crystal.py:336 ../crystal.py:339
+msgid "Please specify a consistent set of atoms."
+msgstr "Please specify a consistent set of atoms."
+
+#: ../crystal.py:348 ../graphene.py:230 ../nanoparticle.py:613
+#: ../nanotube.py:139 ../surfaceslab.py:248
+msgid "No valid atoms."
+msgstr "No valid atoms."
+
+#: ../crystal.py:465
+msgid "Can't find lattice definition!"
+msgstr "Can't find lattice definition!"
+
+#: ../debug.py:11
+msgid "Debug"
+msgstr "Debug"
+
+#: ../dft.py:13
+msgid "DFT"
+msgstr "DFT"
+
+#: ../dft.py:19
+msgid "XC-functional: "
+msgstr "XC-functional: "
+
+#: ../dft.py:23 ../repeat.py:16
+msgid "Repeat atoms:"
+msgstr "Repeat atoms:"
+
+#: ../energyforces.py:11
+msgid "Output:"
+msgstr "Output:"
+
+#: ../energyforces.py:41
+msgid "Save output"
+msgstr "Save output"
+
+#: ../energyforces.py:57
+msgid "Potential energy and forces"
+msgstr "Potential energy and forces"
+
+#: ../energyforces.py:61
+msgid "Calculate potential energy and the force on all atoms"
+msgstr "Calculate potential energy and the force on all atoms"
+
+#: ../energyforces.py:65
+msgid "Write forces on the atoms"
+msgstr "Write forces on the atoms"
+
+#: ../energyforces.py:82
+msgid "Potential Energy:\n"
+msgstr "Potential Energy:\n"
+
+#: ../energyforces.py:83
+#, python-format
+msgid "  %8.2f eV\n"
+msgstr "  %8.2f eV\n"
+
+#: ../energyforces.py:84
+#, python-format
+msgid ""
+"  %8.4f eV/atom\n"
+"\n"
+msgstr ""
+"  %8.4f eV/atom\n"
+"\n"
+
+#: ../energyforces.py:86
+msgid "Forces:\n"
+msgstr "Forces:\n"
+
+#: ../execute.py:23
+msgid ""
+"\n"
+"    Global commands work on all frames or only on the current frame\n"
+"    - Assignment of a global variable may not reference a local one\n"
+"    - use 'Current frame' switch to switch off application to all frames\n"
+"    <c>e</c>:\t\ttotal energy of one frame\n"
+"    <c>fmax</c>:\tmaximal force in one frame\n"
+"    <c>A</c>:\tunit cell\n"
+"    <c>E</c>:\t\ttotal energy array of all frames\n"
+"    <c>F</c>:\t\tall forces in one frame\n"
+"    <c>M</c>:\tall magnetic moments\n"
+"    <c>R</c>:\t\tall atomic positions\n"
+"    <c>S</c>:\tall selected atoms (boolean array)\n"
+"    <c>D</c>:\tall dynamic atoms (boolean array)\n"
+"    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atom commands work on each atom (or a selection) individually\n"
+"    - these can use global commands on the RHS of an equation\n"
+"    - use 'selected atoms only' to restrict application of command\n"
+"    <c>x,y,z</c>:\tatomic coordinates\n"
+"    <c>r,g,b</c>:\tatom display color, range is [0..1]\n"
+"    <c>rad</c>:\tatomic radius for display\n"
+"    <c>s</c>:\t\tatom is selected\n"
+"    <c>d</c>:\t\tatom is movable\n"
+"    <c>f</c>:\t\tforce\n"
+"    <c>Z</c>:\tatomic number\n"
+"    <c>m</c>:\tmagnetic moment\n"
+"    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Special commands and objects:\n"
+"    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame\n"
+"    <c>frame</c>:\tframe number\n"
+"    <c>center</c>:\tcenters the system in its existing unit cell\n"
+"    <c>del S</c>:\tdelete selection\n"
+"    <c>CM</c>:\tcenter of mass\n"
+"    <c>ans[-i]</c>:\tith last calculated result\n"
+"    <c>exec file</c>: executes commands listed in file\n"
+"    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z\n"
+"    <c>gui</c>:\tadvanced: ag window python object\n"
+"    <c>img</c>:\tadvanced: ag images object\n"
+"    "
+msgstr ""
+"\n"
+"    Global commands work on all frames or only on the current frame\n"
+"    - Assignment of a global variable may not reference a local one\n"
+"    - use 'Current frame' switch to switch off application to all frames\n"
+"    <c>e</c>:\t\ttotal energy of one frame\n"
+"    <c>fmax</c>:\tmaximal force in one frame\n"
+"    <c>A</c>:\tunit cell\n"
+"    <c>E</c>:\t\ttotal energy array of all frames\n"
+"    <c>F</c>:\t\tall forces in one frame\n"
+"    <c>M</c>:\tall magnetic moments\n"
+"    <c>R</c>:\t\tall atomic positions\n"
+"    <c>S</c>:\tall selected atoms (boolean array)\n"
+"    <c>D</c>:\tall dynamic atoms (boolean array)\n"
+"    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atom commands work on each atom (or a selection) individually\n"
+"    - these can use global commands on the RHS of an equation\n"
+"    - use 'selected atoms only' to restrict application of command\n"
+"    <c>x,y,z</c>:\tatomic coordinates\n"
+"    <c>r,g,b</c>:\tatom display colour, range is [0..1]\n"
+"    <c>rad</c>:\tatomic radius for display\n"
+"    <c>s</c>:\t\tatom is selected\n"
+"    <c>d</c>:\t\tatom is movable\n"
+"    <c>f</c>:\t\tforce\n"
+"    <c>Z</c>:\tatomic number\n"
+"    <c>m</c>:\tmagnetic moment\n"
+"    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Special commands and objects:\n"
+"    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame\n"
+"    <c>frame</c>:\tframe number\n"
+"    <c>center</c>:\tcenters the system in its existing unit cell\n"
+"    <c>del S</c>:\tdelete selection\n"
+"    <c>CM</c>:\tcentre of mass\n"
+"    <c>ans[-i]</c>:\tith last calculated result\n"
+"    <c>exec file</c>: executes commands listed in file\n"
+"    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z\n"
+"    <c>gui</c>:\tadvanced: ag window python object\n"
+"    <c>img</c>:\tadvanced: ag images object\n"
+"    "
+
+#: ../execute.py:67
+msgid "Expert user mode"
+msgstr "Expert user mode"
+
+#: ../execute.py:80
+msgid "Welcome to the ASE Expert user mode"
+msgstr "Welcome to the ASE Expert user mode"
+
+#: ../execute.py:87
+msgid "Only selected atoms (sa)   "
+msgstr "Only selected atoms (sa)   "
+
+#: ../execute.py:89
+msgid "Only current frame (cf)  "
+msgstr "Only current frame (cf)  "
+
+#: ../execute.py:99
+msgid ""
+"Global: Use A, D, E, M, N, R, S, n, frame; Atoms: Use a, f, m, s, x, y, z, "
+"Z     "
+msgstr ""
+"Global: Use A, D, E, M, N, R, S, n, frame; Atoms: Use a, f, m, s, x, y, z, "
+"Z     "
+
+#: ../execute.py:198
+#, python-format
+msgid "*** WARNING: file does not exist - %s"
+msgstr "*** WARNING: file does not exist - %s"
+
+#: ../execute.py:203
+msgid "*** WARNING: No atoms selected to work with"
+msgstr "*** WARNING: No atoms selected to work with"
+
+#: ../execute.py:277
+msgid "*** Only working on selected atoms"
+msgstr "*** Only working on selected atoms"
+
+#: ../execute.py:279
+msgid "*** Working on all atoms"
+msgstr "*** Working on all atoms"
+
+#: ../execute.py:283
+msgid "*** Only working on current image"
+msgstr "*** Only working on current image"
+
+#: ../execute.py:285
+msgid "*** Working on all images"
+msgstr "*** Working on all images"
+
+#: ../execute.py:301
+msgid "Save Terminal text ..."
+msgstr "Save Terminal text ..."
+
+#: ../graphene.py:15
+msgid ""
+"Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may\n"
+"optionally be saturated with hydrogen (or another element)."
+msgstr ""
+"Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may\n"
+"optionally be saturated with hydrogen (or another element)."
+
+#: ../graphene.py:30 ../gui.py:298
+msgid "Graphene"
+msgstr "Graphene"
+
+#. Choose structure
+#. The structure and lattice constant
+#. Choose the surface structure
+#: ../graphene.py:37 ../nanoparticle.py:138 ../surfaceslab.py:78
+msgid "Structure: "
+msgstr "Structure: "
+
+#: ../graphene.py:39
+msgid "Infinite sheet"
+msgstr "Infinite sheet"
+
+#: ../graphene.py:39
+msgid "Unsaturated ribbon"
+msgstr "Unsaturated ribbon"
+
+#: ../graphene.py:40
+msgid "Saturated ribbon"
+msgstr "Saturated ribbon"
+
+#. Orientation
+#: ../graphene.py:47
+msgid "Orientation: "
+msgstr "Orientation: "
+
+#: ../graphene.py:50
+msgid "zigzag"
+msgstr "zigzag"
+
+#: ../graphene.py:50
+msgid "armchair"
+msgstr "armchair"
+
+#: ../graphene.py:66 ../graphene.py:78 ../nanotube.py:46
+msgid "  Bond length: "
+msgstr "  Bond length: "
+
+#. Choose the saturation element and bond length
+#: ../graphene.py:72
+msgid "Saturation: "
+msgstr "Saturation: "
+
+#: ../graphene.py:75
+msgid "H"
+msgstr "H"
+
+#. Size
+#: ../graphene.py:91
+msgid "Width: "
+msgstr "Width: "
+
+#: ../graphene.py:92 ../nanotube.py:65
+msgid "  Length: "
+msgstr "  Length: "
+
+#. Vacuum
+#: ../graphene.py:100
+msgid "Vacuum: "
+msgstr "Vacuum: "
+
+#: ../graphene.py:138 ../nanotube.py:96 ../setupwindow.py:32
+msgid "  No element specified!"
+msgstr "  No element specified!"
+
+#: ../graphene.py:231 ../nanoparticle.py:614 ../nanotube.py:140
+#: ../pybutton.py:49 ../surfaceslab.py:249
+msgid "You have not (yet) specified a consistent set of parameters."
+msgstr "You have not (yet) specified a consistent set of parameters."
+
+#: ../graphs.py:19
+msgid "Help for plot ..."
+msgstr "Help for plot ..."
+
+#: ../graphs.py:30 ../graphs.py:33
+msgid "Plot"
+msgstr "Plot"
+
+#: ../graphs.py:38
+msgid "clear"
+msgstr "clear"
+
+#: ../graphs.py:92
+msgid "Save data to file ... "
+msgstr "Save data to file ... "
+
+#: ../gtkexcepthook.py:117
+msgid "Bug Detected"
+msgstr "Bug Detected"
+
+#: ../gtkexcepthook.py:121
+msgid "A programming error has been detected."
+msgstr "A programming error has been detected."
+
+#: ../gtkexcepthook.py:124
+msgid ""
+"It probably isn't fatal, but the details should be reported to the "
+"developers nonetheless."
+msgstr ""
+"It probably isn't fatal, but the details should be reported to the "
+"developers nonetheless."
+
+#: ../gtkexcepthook.py:140
+msgid "Report..."
+msgstr "Report..."
+
+#: ../gtkexcepthook.py:144
+msgid "Details..."
+msgstr "Details..."
+
+#: ../gtkexcepthook.py:160
+#, python-format
+msgid ""
+"From: buggy_application\"\n"
+"To: bad_programmer\n"
+"Subject: Exception feedback\n"
+"\n"
+"%s"
+msgstr ""
+"From: buggy_application\"\n"
+"To: bad_programmer\n"
+"Subject: Exception feedback\n"
+"\n"
+"%s"
+
+#. Show details...
+#: ../gtkexcepthook.py:173
+msgid "Bug Details"
+msgstr "Bug Details"
+
+#: ../gui.py:164
+msgid "_File"
+msgstr "_File"
+
+#: ../gui.py:165
+msgid "_Edit"
+msgstr "_Edit"
+
+#: ../gui.py:166
+msgid "_View"
+msgstr "_View"
+
+#: ../gui.py:167
+msgid "_Tools"
+msgstr "_Tools"
+
+#. TRANSLATORS: Set up (i.e. build) surfaces, nanoparticles, ...
+#: ../gui.py:169
+msgid "_Setup"
+msgstr "_Setup"
+
+#: ../gui.py:170
+msgid "_Calculate"
+msgstr "_Calculate"
+
+#: ../gui.py:171
+msgid "_Help"
+msgstr "_Help"
+
+#: ../gui.py:172
+msgid "_Open"
+msgstr "_Open"
+
+#: ../gui.py:173
+msgid "Create a new file"
+msgstr "Create a new file"
+
+#: ../gui.py:175
+msgid "_New"
+msgstr "_New"
+
+#: ../gui.py:176
+msgid "New ase.gui window"
+msgstr "New ase.gui window"
+
+#: ../gui.py:178
+msgid "_Save"
+msgstr "_Save"
+
+#: ../gui.py:179
+msgid "Save current file"
+msgstr "Save current file"
+
+#: ../gui.py:181
+msgid "_Quit"
+msgstr "_Quit"
+
+#: ../gui.py:182
+msgid "Quit"
+msgstr "Quit"
+
+#: ../gui.py:184
+msgid "Select _all"
+msgstr "Select _all"
+
+#: ../gui.py:187
+msgid "_Invert selection"
+msgstr "_Invert selection"
+
+#: ../gui.py:190
+msgid "Select _constrained atoms"
+msgstr "Select _constrained atoms"
+
+#: ../gui.py:193
+msgid "Select _immobile atoms"
+msgstr "Select _immobile atoms"
+
+#: ../gui.py:196
+msgid "_Copy"
+msgstr "_Copy"
+
+#: ../gui.py:197
+msgid "Copy current selection and its orientation to clipboard"
+msgstr "Copy current selection and its orientation to clipboard"
+
+#: ../gui.py:199
+msgid "_Paste"
+msgstr "_Paste"
+
+#: ../gui.py:200
+msgid "Insert current clipboard selection"
+msgstr "Insert current clipboard selection"
+
+#: ../gui.py:202
+msgid "_Modify"
+msgstr "_Modify"
+
+#: ../gui.py:203
+msgid "Change tags, moments and atom types of the selected atoms"
+msgstr "Change tags, moments and atom types of the selected atoms"
+
+#: ../gui.py:205
+msgid "_Add atoms"
+msgstr "_Add atoms"
+
+#: ../gui.py:206
+msgid "Insert or import atoms and molecules"
+msgstr "Insert or import atoms and molecules"
+
+#: ../gui.py:208
+msgid "_Delete selected atoms"
+msgstr "_Delete selected atoms"
+
+#: ../gui.py:209
+msgid "Delete the selected atoms"
+msgstr "Delete the selected atoms"
+
+#: ../gui.py:211
+msgid "_First image"
+msgstr "_First image"
+
+#: ../gui.py:214
+msgid "_Previous image"
+msgstr "_Previous image"
+
+#: ../gui.py:217
+msgid "_Next image"
+msgstr "_Next image"
+
+#: ../gui.py:220
+msgid "_Last image"
+msgstr "_Last image"
+
+#: ../gui.py:223
+msgid "Quick Info ..."
+msgstr "Quick Info ..."
+
+#: ../gui.py:226
+msgid "Repeat ..."
+msgstr "Repeat ..."
+
+#: ../gui.py:229
+msgid "Rotate ..."
+msgstr "Rotate ..."
+
+#: ../gui.py:232
+msgid "Colors ..."
+msgstr "Colours ..."
+
+#. TRANSLATORS: verb
+#: ../gui.py:235
+msgid "Focus"
+msgstr "Focus"
+
+#: ../gui.py:238
+msgid "Zoom in"
+msgstr "Zoom in"
+
+#: ../gui.py:241
+msgid "Zoom out"
+msgstr "Zoom out"
+
+#: ../gui.py:244
+msgid "Reset View"
+msgstr "Reset View"
+
+#: ../gui.py:247
+msgid "Settings ..."
+msgstr "Settings ..."
+
+#: ../gui.py:250
+msgid "VMD"
+msgstr "VMD"
+
+#: ../gui.py:253
+msgid "RasMol"
+msgstr "RasMol"
+
+#: ../gui.py:256
+msgid "xmakemol"
+msgstr "xmakemol"
+
+#: ../gui.py:259
+msgid "avogadro"
+msgstr "avogadro"
+
+#: ../gui.py:262
+msgid "Graphs ..."
+msgstr "Graphs ..."
+
+#: ../gui.py:265
+msgid "Movie ..."
+msgstr "Movie ..."
+
+#: ../gui.py:268
+msgid "Expert mode ..."
+msgstr "Expert mode ..."
+
+#: ../gui.py:271
+msgid "Constraints ..."
+msgstr "Constraints ..."
+
+#: ../gui.py:274
+msgid "Render scene ..."
+msgstr "Render scene ..."
+
+#: ../gui.py:277
+msgid "DFT ..."
+msgstr "DFT ..."
+
+#: ../gui.py:280
+msgid "NE_B"
+msgstr "NE_B"
+
+#: ../gui.py:283
+msgid "B_ulk Modulus"
+msgstr "B_ulk Modulus"
+
+#: ../gui.py:286
+msgid "_Bulk Crystal"
+msgstr "_Bulk Crystal"
+
+#: ../gui.py:287
+msgid "Create a bulk crystal with arbitrary orientation"
+msgstr "Create a bulk crystal with arbitrary orientation"
+
+#: ../gui.py:289
+msgid "_Surface slab"
+msgstr "_Surface slab"
+
+#: ../gui.py:290
+msgid "Create the most common surfaces"
+msgstr "Create the most common surfaces"
+
+#: ../gui.py:292
+msgid "_Nanoparticle"
+msgstr "_Nanoparticle"
+
+#: ../gui.py:293
+msgid "Create a crystalline nanoparticle"
+msgstr "Create a crystalline nanoparticle"
+
+#: ../gui.py:295
+msgid "Nano_tube"
+msgstr "Nano_tube"
+
+#: ../gui.py:296
+msgid "Create a nanotube"
+msgstr "Create a nanotube"
+
+#: ../gui.py:299
+msgid "Create a graphene sheet or nanoribbon"
+msgstr "Create a graphene sheet or nanoribbon"
+
+#: ../gui.py:301
+msgid "Set _Calculator"
+msgstr "Set _Calculator"
+
+#: ../gui.py:302
+msgid "Set a calculator used in all calculation modules"
+msgstr "Set a calculator used in all calculation modules"
+
+#: ../gui.py:304
+msgid "_Energy and Forces"
+msgstr "_Energy and Forces"
+
+#: ../gui.py:305
+msgid "Calculate energy and forces"
+msgstr "Calculate energy and forces"
+
+#: ../gui.py:307
+msgid "Energy _Minimization"
+msgstr "Energy _Minimisation"
+
+#: ../gui.py:308
+msgid "Minimize the energy"
+msgstr "Minimise the energy"
+
+#: ../gui.py:310
+msgid "Scale system"
+msgstr "Scale system"
+
+#: ../gui.py:311
+msgid "Deform system by scaling it"
+msgstr "Deform system by scaling it"
+
+#: ../gui.py:313
+msgid "_About"
+msgstr "_About"
+
+#: ../gui.py:316
+msgid "Webpage ..."
+msgstr "Webpage ..."
+
+#: ../gui.py:317
+msgid "Debug ..."
+msgstr "Debug ..."
+
+#: ../gui.py:319
+msgid "Show _unit cell"
+msgstr "Show _unit cell"
+
+#: ../gui.py:323
+msgid "Show _axes"
+msgstr "Show _axes"
+
+#: ../gui.py:327
+msgid "Show _bonds"
+msgstr "Show _bonds"
+
+#: ../gui.py:331
+msgid "_Move atoms"
+msgstr "_Move atoms"
+
+#: ../gui.py:335
+msgid "_Rotate atoms"
+msgstr "_Rotate atoms"
+
+#: ../gui.py:339
+msgid "Orien_t atoms"
+msgstr "Orien_t atoms"
+
+#: ../gui.py:351
+#, python-format
+msgid "building menus failed: %s"
+msgstr "building menus failed: %s"
+
+#: ../gui.py:620 ../gui.py:1016 ../gui.py:1076
+msgid "Open ..."
+msgstr "Open ..."
+
+#: ../gui.py:624 ../gui.py:1019
+msgid "<<filename>>"
+msgstr "<<filename>>"
+
+#: ../gui.py:756
+msgid "Add atoms"
+msgstr "Add atoms"
+
+#: ../gui.py:759
+msgid "Paste"
+msgstr "Paste"
+
+#: ../gui.py:765
+msgid "Insert atom or molecule"
+msgstr "Insert atom or molecule"
+
+#: ../gui.py:766 ../gui.py:883
+msgid "Tag"
+msgstr "Tag"
+
+#: ../gui.py:767 ../gui.py:884
+msgid "Moment"
+msgstr "Moment"
+
+#: ../gui.py:768
+msgid "Position"
+msgstr "Position"
+
+#: ../gui.py:793
+msgid "_Load molecule"
+msgstr "_Load molecule"
+
+#: ../gui.py:797 ../gui.py:899
+msgid "_OK"
+msgstr "_OK"
+
+#: ../gui.py:801 ../gui.py:903
+msgid "_Cancel"
+msgstr "_Cancel"
+
+#: ../gui.py:876
+msgid "Modify"
+msgstr "Modify"
+
+#: ../gui.py:882
+msgid "Atom"
+msgstr "Atom"
+
+#: ../gui.py:927
+msgid "Confirmation"
+msgstr "Confirmation"
+
+#: ../gui.py:931
+msgid "Delete selected atom?"
+msgid_plural "Delete selected atoms?"
+msgstr[0] "Delete selected atom?"
+msgstr[1] "Delete selected atoms?"
+
+#: ../gui.py:938
+msgid "Cancel"
+msgstr "Cancel"
+
+#: ../gui.py:1024 ../gui.py:1106
+msgid "Automatic"
+msgstr "Automatic"
+
+#: ../gui.py:1025
+msgid "Dacapo netCDF output file"
+msgstr "Dacapo netCDF output file"
+
+#: ../gui.py:1026
+msgid "Virtual Nano Lab file"
+msgstr "Virtual Nano Lab file"
+
+#: ../gui.py:1027
+msgid "ASE pickle trajectory"
+msgstr "ASE pickle trajectory"
+
+#: ../gui.py:1028 ../gui.py:1119
+msgid "ASE bundle trajectory"
+msgstr "ASE bundle trajectory"
+
+#: ../gui.py:1029
+msgid "GPAW text output"
+msgstr "GPAW text output"
+
+#: ../gui.py:1030
+msgid "CUBE file"
+msgstr "CUBE file"
+
+#: ../gui.py:1031
+msgid "XCrySDen Structure File"
+msgstr "XCrySDen Structure File"
+
+#: ../gui.py:1032
+msgid "Dacapo text output"
+msgstr "Dacapo text output"
+
+#: ../gui.py:1033
+msgid "XYZ-file"
+msgstr "XYZ-file"
+
+#: ../gui.py:1034
+msgid "VASP POSCAR/CONTCAR file"
+msgstr "VASP POSCAR/CONTCAR file"
+
+#: ../gui.py:1035
+msgid "VASP OUTCAR file"
+msgstr "VASP OUTCAR file"
+
+#: ../gui.py:1036
+msgid "Protein Data Bank"
+msgstr "Protein Data Bank"
+
+#: ../gui.py:1037
+msgid "CIF-file"
+msgstr "CIF-file"
+
+#: ../gui.py:1038
+msgid "FHI-aims geometry file"
+msgstr "FHI-aims geometry file"
+
+#: ../gui.py:1039
+msgid "FHI-aims output file"
+msgstr "FHI-aims output file"
+
+#: ../gui.py:1040
+msgid "TURBOMOLE coord file"
+msgstr "TURBOMOLE coord file"
+
+#: ../gui.py:1041
+msgid "exciting input"
+msgstr "exciting input"
+
+#: ../gui.py:1042
+msgid "WIEN2k structure file"
+msgstr "WIEN2k structure file"
+
+#: ../gui.py:1043
+msgid "DftbPlus input file"
+msgstr "DftbPlus input file"
+
+#: ../gui.py:1044
+msgid "ETSF format"
+msgstr "ETSF format"
+
+#: ../gui.py:1045 ../gui.py:1117
+msgid "CASTEP geom file"
+msgstr "CASTEP geom file"
+
+#: ../gui.py:1046
+msgid "CASTEP output file"
+msgstr "CASTEP output file"
+
+#: ../gui.py:1047
+msgid "CASTEP trajectory file"
+msgstr "CASTEP trajectory file"
+
+#: ../gui.py:1048
+msgid "DFTBPlus GEN format"
+msgstr "DFTBPlus GEN format"
+
+#: ../gui.py:1054
+msgid "File type:"
+msgstr "File type:"
+
+#: ../gui.py:1094
+msgid "Save ..."
+msgstr "Save ..."
+
+#: ../gui.py:1107
+msgid "XYZ file"
+msgstr "XYZ file"
+
+#: ../gui.py:1108
+msgid "ASE trajectory"
+msgstr "ASE trajectory"
+
+#: ../gui.py:1109
+msgid "PDB file"
+msgstr "PDB file"
+
+#: ../gui.py:1110
+msgid "Gaussian cube file"
+msgstr "Gaussian cube file"
+
+#: ../gui.py:1111
+msgid "Python script"
+msgstr "Python script"
+
+#: ../gui.py:1112
+msgid "VNL file"
+msgstr "VNL file"
+
+#: ../gui.py:1113
+msgid "Portable Network Graphics"
+msgstr "Portable Network Graphics"
+
+#: ../gui.py:1114
+msgid "Persistence of Vision"
+msgstr "Persistence of Vision"
+
+#: ../gui.py:1115
+msgid "Encapsulated PostScript"
+msgstr "Encapsulated PostScript"
+
+#: ../gui.py:1116
+msgid "FHI-aims geometry input"
+msgstr "FHI-aims geometry input"
+
+#: ../gui.py:1118
+msgid "VASP geometry input"
+msgstr "VASP geometry input"
+
+#: ../gui.py:1120
+msgid "cif file"
+msgstr "cif file"
+
+#: ../gui.py:1142
+#, python-format
+msgid "Save current image only (#%d)"
+msgstr "Save current image only (#%d)"
+
+#: ../gui.py:1146
+msgid "Slice: "
+msgstr "Slice: "
+
+#: ../gui.py:1147
+msgid "Help for slice ..."
+msgstr "Help for slice ..."
+
+#: ../gui.py:1159
+msgid "AG INTERNAL ERROR: strange response in Save,"
+msgstr "AG INTERNAL ERROR: strange response in Save,"
+
+#: ../gui.py:1178
+msgid "Unknown output format!"
+msgstr "Unknown output format!"
+
+#: ../gui.py:1179
+#, python-format
+msgid "Use one of: %s"
+msgstr "Use one of: %s"
+
+#: ../gui.py:1284
+msgid "Not implemented!"
+msgstr "Not implemented!"
+
+#: ../gui.py:1285
+msgid "do you really need it?"
+msgstr "do you really need it?"
+
+#: ../minimize.py:20
+msgid "Algorithm: "
+msgstr "Algorithm: "
+
+#: ../minimize.py:25 ../progress.py:67
+msgid "Convergence criterion: F<sub>max</sub> = "
+msgstr "Convergence criterion: F<sub>max</sub> = "
+
+#: ../minimize.py:30 ../progress.py:70
+msgid "Max. number of steps: "
+msgstr "Max. number of steps: "
+
+#. Special stuff for MDMin
+#: ../minimize.py:33
+msgid "Pseudo time step: "
+msgstr "Pseudo time step: "
+
+#: ../minimize.py:54
+msgid "Energy minimization"
+msgstr "Energy minimisation"
+
+#: ../minimize.py:58
+msgid "Minimize the energy with respect to the positions."
+msgstr "Minimise the energy with respect to the positions."
+
+#. Don't catch errors in the function.
+#. Display status message
+#: ../minimize.py:90 ../scaling.py:299
+msgid "Running ..."
+msgstr "Running ..."
+
+#. Update display to reflect cancellation of simulation.
+#: ../minimize.py:107
+#, python-format
+msgid "Minimization CANCELLED after %i steps."
+msgstr "Minimisation CANCELLED after %i steps."
+
+#: ../minimize.py:113 ../scaling.py:350
+msgid "Out of memory, consider using LBFGS instead"
+msgstr "Out of memory, consider using LBFGS instead"
+
+#. Update display to reflect succesful end of simulation.
+#: ../minimize.py:120
+#, python-format
+msgid "Minimization completed in %i steps."
+msgstr "Minimisation completed in %i steps."
+
+#. self.connect('delete_event', self.exit2)
+#: ../movie.py:14
+msgid "Movie"
+msgstr "Movie"
+
+#: ../movie.py:16
+msgid "Image number:"
+msgstr "Image number:"
+
+#: ../movie.py:38
+msgid "Play"
+msgstr "Play"
+
+#: ../movie.py:40
+msgid "Stop"
+msgstr "Stop"
+
+#. TRANSLATORS: This function plays an animation forwards and backwards
+#. alternatingly, e.g. for displaying vibrational movement
+#: ../movie.py:44
+msgid "Rock"
+msgstr "Rock"
+
+#: ../movie.py:60
+msgid " Frame rate: "
+msgstr " Frame rate: "
+
+#: ../movie.py:61
+msgid " Skip frames: "
+msgstr " Skip frames: "
+
+#: ../nanoparticle.py:19
+msgid ""
+"Create a nanoparticle either by specifying the number of layers, or using "
+"the\n"
+"Wulff construction.  Please press the [Help] button for instructions on how "
+"to\n"
+"specify the directions.\n"
+"WARNING: The Wulff construction currently only works with cubic crystals!\n"
+msgstr ""
+"Create a nanoparticle either by specifying the number of layers, or using "
+"the\n"
+"Wulff construction.  Please press the [Help] button for instructions on how "
+"to\n"
+"specify the directions.\n"
+"WARNING: The Wulff construction currently only works with cubic crystals!\n"
+
+#: ../nanoparticle.py:26
+msgid ""
+"\n"
+"The nanoparticle module sets up a nano-particle or a cluster with a given\n"
+"crystal structure.\n"
+"\n"
+"1) Select the element, the crystal structure and the lattice constant(s).\n"
+"   The [Get structure] button will find the data for a given element.\n"
+"\n"
+"2) Choose if you want to specify the number of layers in each direction, or "
+"if\n"
+"   you want to use the Wulff construction.  In the latter case, you must "
+"specify\n"
+"   surface energies in each direction, and the size of the cluster.\n"
+"\n"
+"How to specify the directions:\n"
+"------------------------------\n"
+"\n"
+"First time a direction appears, it is interpreted as the entire family of\n"
+"directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of "
+"these\n"
+"directions is specified again, the second specification overrules that "
+"specific\n"
+"direction.  For this reason, the order matters and you can rearrange the\n"
+"directions with the [Up] and [Down] keys.  You can also add a new "
+"direction,\n"
+"remember to press [Add] or it will not be included.\n"
+"\n"
+"Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of "
+"directions,\n"
+"the {111} family and then the (001) direction, overruling the value given "
+"for\n"
+"the whole family of directions.\n"
+msgstr ""
+"\n"
+"The nanoparticle module sets up a nano-particle or a cluster with a given\n"
+"crystal structure.\n"
+"\n"
+"1) Select the element, the crystal structure and the lattice constant(s).\n"
+"   The [Get structure] button will find the data for a given element.\n"
+"\n"
+"2) Choose if you want to specify the number of layers in each direction, or "
+"if\n"
+"   you want to use the Wulff construction.  In the latter case, you must "
+"specify\n"
+"   surface energies in each direction, and the size of the cluster.\n"
+"\n"
+"How to specify the directions:\n"
+"------------------------------\n"
+"\n"
+"First time a direction appears, it is interpreted as the entire family of\n"
+"directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of "
+"these\n"
+"directions is specified again, the second specification overrules that "
+"specific\n"
+"direction.  For this reason, the order matters and you can rearrange the\n"
+"directions with the [Up] and [Down] keys.  You can also add a new "
+"direction,\n"
+"remember to press [Add] or it will not be included.\n"
+"\n"
+"Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of "
+"directions,\n"
+"the {111} family and then the (001) direction, overruling the value given "
+"for\n"
+"the whole family of directions.\n"
+
+#. Structures:  Abbreviation, name, 4-index (boolean), two lattice const (bool), factory
+#: ../nanoparticle.py:83
+msgid "Face centered cubic (fcc)"
+msgstr "Face centered cubic (fcc)"
+
+#: ../nanoparticle.py:84
+msgid "Body centered cubic (bcc)"
+msgstr "Body centered cubic (bcc)"
+
+#: ../nanoparticle.py:85
+msgid "Simple cubic (sc)"
+msgstr "Simple cubic (sc)"
+
+#: ../nanoparticle.py:86
+msgid "Hexagonal closed-packed (hcp)"
+msgstr "Hexagonal closed-packed (hcp)"
+
+#: ../nanoparticle.py:87
+msgid "Graphite"
+msgstr "Graphite"
+
+#: ../nanoparticle.py:116
+msgid "Nanoparticle"
+msgstr "Nanoparticle"
+
+#. Choose the element
+#. Choose the element and bond length
+#. Choose the element
+#: ../nanoparticle.py:126 ../nanotube.py:40 ../surfaceslab.py:69
+msgid "Element: "
+msgstr "Element: "
+
+#: ../nanoparticle.py:130
+msgid "Get structure"
+msgstr "Get structure"
+
+#: ../nanoparticle.py:154
+msgid "Lattice constant:  a ="
+msgstr "Lattice constant:  a ="
+
+#. Choose specification method
+#: ../nanoparticle.py:171
+msgid "Method: "
+msgstr "Method: "
+
+#: ../nanoparticle.py:173
+msgid "Layer specification"
+msgstr "Layer specification"
+
+#: ../nanoparticle.py:173
+msgid "Wulff construction"
+msgstr "Wulff construction"
+
+#: ../nanoparticle.py:193
+msgid "Dummy placeholder object"
+msgstr "Dummy placeholder object"
+
+#: ../nanoparticle.py:195
+msgid "Add new direction:"
+msgstr "Add new direction:"
+
+#: ../nanoparticle.py:211
+msgid "Add"
+msgstr "Add"
+
+#: ../nanoparticle.py:219
+msgid "Set all directions to default values"
+msgstr "Set all directions to default values"
+
+#: ../nanoparticle.py:227
+msgid "Particle size: "
+msgstr "Particle size: "
+
+#: ../nanoparticle.py:228 ../nanoparticle.py:265 ../progress.py:196
+msgid "Number of atoms: "
+msgstr "Number of atoms: "
+
+#: ../nanoparticle.py:233
+msgid "Volume: "
+msgstr "Volume: "
+
+#: ../nanoparticle.py:238
+msgid "ų"
+msgstr "ų"
+
+#: ../nanoparticle.py:243
+msgid "Rounding: If exact size is not possible, choose the size"
+msgstr "Rounding: If exact size is not possible, choose the size"
+
+#: ../nanoparticle.py:246
+msgid "above  "
+msgstr "above  "
+
+#: ../nanoparticle.py:247
+msgid "below  "
+msgstr "below  "
+
+#: ../nanoparticle.py:248
+msgid "closest  "
+msgstr "closest  "
+
+#: ../nanoparticle.py:251
+msgid "Smaller"
+msgstr "Smaller"
+
+#: ../nanoparticle.py:252
+msgid "Larger"
+msgstr "Larger"
+
+#: ../nanoparticle.py:267
+msgid "   Approx. diameter: "
+msgstr "   Approx. diameter: "
+
+#: ../nanoparticle.py:271
+msgid "Information about the created cluster:"
+msgstr "Information about the created cluster:"
+
+#. Buttons
+#: ../nanoparticle.py:277 ../nanotube.py:75
+msgid "Creating a nanoparticle."
+msgstr "Creating a nanoparticle."
+
+#: ../nanoparticle.py:284
+msgid "Automatic Apply"
+msgstr "Automatic Apply"
+
+#: ../nanoparticle.py:332
+msgid "Up"
+msgstr "Up"
+
+#: ../nanoparticle.py:337
+msgid "Down"
+msgstr "Down"
+
+#: ../nanoparticle.py:342
+msgid "Delete"
+msgstr "Delete"
+
+#: ../nanoparticle.py:383
+msgid "Surface energies (as energy/area, NOT per atom):"
+msgstr "Surface energies (as energy/area, NOT per atom):"
+
+#: ../nanoparticle.py:389
+msgid "Number of layers:"
+msgstr "Number of layers:"
+
+#: ../nanoparticle.py:418
+msgid "At least one index must be non-zero"
+msgstr "At least one index must be non-zero"
+
+#: ../nanoparticle.py:421
+msgid "Invalid hexagonal indices"
+msgstr "Invalid hexagonal indices"
+
+#: ../nanoparticle.py:476 ../surfaceslab.py:218
+msgid "Invalid element."
+msgstr "Invalid element."
+
+#: ../nanoparticle.py:486
+msgid "Unsupported or unknown structure"
+msgstr "Unsupported or unknown structure"
+
+#: ../nanoparticle.py:603
+#, python-format
+msgid "%.1f Å"
+msgstr "%.1f Å"
+
+#: ../nanotube.py:14
+msgid ""
+"Set up a Carbon nanotube by specifying the (n,m) roll-up vector.\n"
+"Please note that m <= n.\n"
+"\n"
+"Nanotubes of other elements can be made by specifying the element\n"
+"and bond length."
+msgstr ""
+"Set up a Carbon nanotube by specifying the (n,m) roll-up vector.\n"
+"Please note that m <= n.\n"
+"\n"
+"Nanotubes of other elements can be made by specifying the element\n"
+"and bond length."
+
+#: ../nanotube.py:33
+msgid "Nanotube"
+msgstr "Nanotube"
+
+#. Choose the structure.
+#: ../nanotube.py:57
+msgid "Select roll-up vector (n,m) and tube length:"
+msgstr "Select roll-up vector (n,m) and tube length:"
+
+#: ../progress.py:25
+msgid "Progress"
+msgstr "Progress"
+
+#: ../progress.py:32
+msgid "Scaling deformation:"
+msgstr "Scaling deformation:"
+
+#: ../progress.py:38
+#, python-format
+msgid "Step number %s of %s."
+msgstr "Step number %s of %s."
+
+#. Minimization progress frame
+#. Box containing frame and spacing
+#: ../progress.py:53
+msgid "Energy minimization:"
+msgstr "Energy minimisation:"
+
+#: ../progress.py:60
+msgid "Step number: "
+msgstr "Step number: "
+
+#: ../progress.py:62
+msgid "F<sub>max</sub>: "
+msgstr "F<sub>max</sub>: "
+
+#: ../progress.py:102
+msgid "unknown"
+msgstr "unknown"
+
+#: ../progress.py:179
+msgid "Status: "
+msgstr "Status: "
+
+#: ../progress.py:181
+msgid "Iteration: "
+msgstr "Iteration: "
+
+#: ../progress.py:184
+msgid "log<sub>10</sub>(change):"
+msgstr "log<sub>10</sub>(change):"
+
+#: ../progress.py:187
+msgid "Wave functions: "
+msgstr "Wave functions: "
+
+#: ../progress.py:189
+msgid "Density: "
+msgstr "Density: "
+
+#: ../progress.py:191
+msgid "Energy: "
+msgstr "Energy: "
+
+#: ../progress.py:194
+msgid "GPAW version: "
+msgstr "GPAW version: "
+
+#: ../progress.py:197
+msgid "N/A"
+msgstr "N/A"
+
+#: ../progress.py:198
+msgid "Memory estimate: "
+msgstr "Memory estimate: "
+
+#: ../progress.py:233
+msgid "No info"
+msgstr "No info"
+
+#: ../progress.py:243
+msgid "Initializing"
+msgstr "Initializing"
+
+#: ../progress.py:244
+msgid "Positions:"
+msgstr "Positions:"
+
+#: ../progress.py:248
+msgid "Starting calculation"
+msgstr "Starting calculation"
+
+#: ../progress.py:285
+msgid "unchanged"
+msgstr "unchanged"
+
+#: ../progress.py:295
+msgid "Self-consistency loop"
+msgstr "Self-consistency loop"
+
+#: ../progress.py:300
+msgid "Calculating forces"
+msgstr "Calculating forces"
+
+#: ../progress.py:301
+msgid " (converged)"
+msgstr " (converged)"
+
+#: ../pybutton.py:37
+msgid "Python"
+msgstr "Python"
+
+#: ../pybutton.py:48
+msgid "No Python code"
+msgstr "No Python code"
+
+#: ../pybutton.py:52
+#, python-format
+msgid ""
+"\n"
+"Title: %(title)s\n"
+"Time: %(time)s\n"
+msgstr ""
+"\n"
+"Title: %(title)s\n"
+"Time: %(time)s\n"
+
+#: ../pybutton.py:61
+msgid "ag: Python code"
+msgstr "ag: Python code"
+
+#: ../pybutton.py:65
+msgid "Information:"
+msgstr "Information:"
+
+#: ../pybutton.py:72
+msgid "Python code:"
+msgstr "Python code:"
+
+#: ../quickinfo.py:9
+msgid "Single image loaded."
+msgstr "Single image loaded."
+
+#: ../quickinfo.py:10
+#, python-format
+msgid "Image %d loaded (0 - %d)."
+msgstr "Image %d loaded (0 - %d)."
+
+#: ../quickinfo.py:11
+msgid "Unit cell is fixed."
+msgstr "Unit cell is fixed."
+
+#: ../quickinfo.py:12
+msgid "Unit cell varies."
+msgstr "Unit cell varies."
+
+#: ../quickinfo.py:14
+#, python-format
+msgid ""
+"%s\n"
+"\n"
+"Number of atoms: %d.\n"
+"\n"
+"Unit cell:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+msgstr ""
+"%s\n"
+"\n"
+"Number of atoms: %d.\n"
+"\n"
+"Unit cell:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+
+#: ../quickinfo.py:29
+msgid "Quick Info"
+msgstr "Quick Info"
+
+#: ../quickinfo.py:33
+msgid "No atoms loaded."
+msgstr "No atoms loaded."
+
+#: ../render.py:14
+msgid ""
+"    Textures can be used to highlight different parts of\n"
+"    an atomic structure. This window applies the default\n"
+"    texture to the entire structure and optionally\n"
+"    applies a different texture to subsets of atoms that\n"
+"    can be selected using the mouse.\n"
+"    An alternative selection method is based on a boolean\n"
+"    expression in the entry box provided, using the\n"
+"    variables x, y, z, or Z. For example, the expression\n"
+"    Z == 11 and x > 10 and y > 10 \n"
+"    will mark all sodium atoms with x or coordinates \n"
+"    larger than 10. In either case, the button labeled\n"
+"    `Create new texture from selection` will enable\n"
+"    to change the attributes of the current selection. \n"
+"    "
+msgstr ""
+"    Textures can be used to highlight different parts of\n"
+"    an atomic structure. This window applies the default\n"
+"    texture to the entire structure and optionally\n"
+"    applies a different texture to subsets of atoms that\n"
+"    can be selected using the mouse.\n"
+"    An alternative selection method is based on a boolean\n"
+"    expression in the entry box provided, using the\n"
+"    variables x, y, z, or Z. For example, the expression\n"
+"    Z == 11 and x > 10 and y > 10 \n"
+"    will mark all sodium atoms with x or coordinates \n"
+"    larger than 10. In either case, the button labeled\n"
+"    `Create new texture from selection` will enable\n"
+"    to change the attributes of the current selection. \n"
+"    "
+
+#: ../render.py:32
+msgid "Render current view in povray ... "
+msgstr "Render current view in povray ... "
+
+#: ../render.py:36
+#, python-format
+msgid "Rendering %d atoms."
+msgstr "Rendering %d atoms."
+
+#: ../render.py:41
+msgid "Render constraints"
+msgstr "Render constraints"
+
+#: ../render.py:44
+msgid "Width"
+msgstr "Width"
+
+#: ../render.py:45
+msgid "     Height"
+msgstr "     Height"
+
+#: ../render.py:52
+msgid "Render unit cell"
+msgstr "Render unit cell"
+
+#: ../render.py:61
+msgid "Line width"
+msgstr "Line width"
+
+#: ../render.py:63
+msgid "Angstrom           "
+msgstr "Angstrom           "
+
+#: ../render.py:73
+msgid "Set"
+msgstr "Set"
+
+#: ../render.py:75
+msgid "Output basename: "
+msgstr "Output basename: "
+
+#: ../render.py:77
+msgid "               Filename: "
+msgstr "               Filename: "
+
+#: ../render.py:88
+msgid " Default texture for atoms: "
+msgstr " Default texture for atoms: "
+
+#: ../render.py:89
+msgid "    transparency: "
+msgstr "    transparency: "
+
+#: ../render.py:90
+msgid "Define atom selection for new texture:"
+msgstr "Define atom selection for new texture:"
+
+#: ../render.py:92
+msgid "Select"
+msgstr "Select"
+
+#: ../render.py:96
+msgid "Create new texture from selection"
+msgstr "Create new texture from selection"
+
+#: ../render.py:98
+msgid "Help on textures"
+msgstr "Help on textures"
+
+#: ../render.py:111
+msgid "Camera type: "
+msgstr "Camera type: "
+
+#: ../render.py:112
+msgid "     Camera distance"
+msgstr "     Camera distance"
+
+#: ../render.py:113
+msgid "Render current frame"
+msgstr "Render current frame"
+
+#: ../render.py:117
+#, python-format
+msgid "Render all %d frames"
+msgstr "Render all %d frames"
+
+#: ../render.py:122
+msgid "Transparent background"
+msgstr "Transparent background"
+
+#: ../render.py:125
+msgid "Run povray       "
+msgstr "Run povray       "
+
+#: ../render.py:128
+msgid "Keep povray files       "
+msgstr "Keep povray files       "
+
+#: ../render.py:131
+msgid "Show output window"
+msgstr "Show output window"
+
+#: ../render.py:212
+msgid "  transparency: "
+msgstr "  transparency: "
+
+#: ../render.py:218
+msgid ""
+"Can not create new texture! Must have some atoms selected to create a new "
+"material!"
+msgstr ""
+"Can not create new texture! Must have some atoms selected to create a new "
+"material!"
+
+#: ../repeat.py:14
+msgid "Repeat"
+msgstr "Repeat"
+
+#: ../repeat.py:21
+msgid "Set unit cell"
+msgstr "Set unit cell"
+
+#: ../rotate.py:15
+msgid "Rotate"
+msgstr "Rotate"
+
+#: ../rotate.py:17
+msgid "Rotation angles:"
+msgstr "Rotation angles:"
+
+#: ../rotate.py:27
+msgid ""
+"Note:\n"
+"You can rotate freely\n"
+"with the mouse, by holding\n"
+"down mouse button 2."
+msgstr ""
+"Note:\n"
+"You can rotate freely\n"
+"with the mouse, by holding\n"
+"down mouse button 2."
+
+#: ../scaling.py:49
+msgid "Homogeneous scaling"
+msgstr "Homogeneous scaling"
+
+#: ../scaling.py:59
+msgid "3D deformation   "
+msgstr "3D deformation   "
+
+#: ../scaling.py:60
+msgid "2D deformation   "
+msgstr "2D deformation   "
+
+#: ../scaling.py:61
+msgid "1D deformation   "
+msgstr "1D deformation   "
+
+#: ../scaling.py:64
+msgid "Bulk"
+msgstr "Bulk"
+
+#: ../scaling.py:66
+msgid "xy-plane"
+msgstr "xy-plane"
+
+#: ../scaling.py:68
+msgid "xz-plane"
+msgstr "xz-plane"
+
+#: ../scaling.py:70
+msgid "yz-plane"
+msgstr "yz-plane"
+
+#: ../scaling.py:72
+msgid "x-axis"
+msgstr "x-axis"
+
+#: ../scaling.py:74
+msgid "y-axis"
+msgstr "y-axis"
+
+#: ../scaling.py:76
+msgid "z-axis"
+msgstr "z-axis"
+
+#: ../scaling.py:89
+msgid "Allow deformation along non-periodic directions."
+msgstr "Allow deformation along non-periodic directions."
+
+#. Parameters for the deformation
+#: ../scaling.py:94
+msgid "Deformation:"
+msgstr "Deformation:"
+
+#: ../scaling.py:100
+msgid "Maximal scale factor: "
+msgstr "Maximal scale factor: "
+
+#: ../scaling.py:103
+msgid "Scale offset: "
+msgstr "Scale offset: "
+
+#: ../scaling.py:106
+msgid "Number of steps: "
+msgstr "Number of steps: "
+
+#: ../scaling.py:107
+msgid "Only positive deformation"
+msgstr "Only positive deformation"
+
+#. Atomic relaxations
+#: ../scaling.py:112
+msgid "Atomic relaxations:"
+msgstr "Atomic relaxations:"
+
+#: ../scaling.py:116
+msgid "On   "
+msgstr "On   "
+
+#: ../scaling.py:117
+msgid "Off"
+msgstr "Off"
+
+#. Results
+#: ../scaling.py:128
+msgid "Results:"
+msgstr "Results:"
+
+#: ../scaling.py:130
+msgid "Keep original configuration"
+msgstr "Keep original configuration"
+
+#: ../scaling.py:132
+msgid "Load optimal configuration"
+msgstr "Load optimal configuration"
+
+#: ../scaling.py:134
+msgid "Load all configurations"
+msgstr "Load all configurations"
+
+#: ../scaling.py:143
+msgid "Strain\t\tEnergy [eV]"
+msgstr "Strain\t\tEnergy [eV]"
+
+#: ../scaling.py:144
+msgid "Fit:"
+msgstr "Fit:"
+
+#: ../scaling.py:148
+msgid "2nd"
+msgstr "2nd"
+
+#: ../scaling.py:149
+msgid "3rd"
+msgstr "3rd"
+
+#: ../scaling.py:153
+msgid "Order of fit: "
+msgstr "Order of fit: "
+
+#. Update display to reflect cancellation of simulation.
+#: ../scaling.py:346
+msgid "Calculation CANCELLED."
+msgstr "Calculation CANCELLED."
+
+#. Update display to reflect succesful end of simulation.
+#: ../scaling.py:357
+msgid "Calculation completed."
+msgstr "Calculation completed."
+
+#: ../scaling.py:380
+msgid "No trustworthy minimum: Old configuration kept."
+msgstr "No trustworthy minimum: Old configuration kept."
+
+#: ../scaling.py:420
+#, python-format
+msgid ""
+"Insufficent data for a fit\n"
+"(only %i data points)\n"
+msgstr ""
+"Insufficent data for a fit\n"
+"(only %i data points)\n"
+
+#: ../scaling.py:424
+msgid ""
+"REVERTING TO 2ND ORDER FIT\n"
+"(only 3 data points)\n"
+"\n"
+msgstr ""
+"REVERTING TO 2ND ORDER FIT\n"
+"(only 3 data points)\n"
+"\n"
+
+#: ../scaling.py:440
+msgid "No minimum found!"
+msgstr "No minimum found!"
+
+#: ../scaling.py:454
+msgid ""
+"\n"
+"WARNING: Minimum is outside interval\n"
+msgstr ""
+"\n"
+"WARNING: Minimum is outside interval\n"
+
+#: ../scaling.py:455
+msgid "It is UNRELIABLE!\n"
+msgstr "It is UNRELIABLE!\n"
+
+#: ../settings.py:16
+msgid "Constraints:"
+msgstr "Constraints:"
+
+#: ../settings.py:19
+msgid "release"
+msgstr "release"
+
+#: ../settings.py:23
+msgid "Constrain immobile atoms"
+msgstr "Constrain immobile atoms"
+
+#: ../settings.py:25
+msgid "Clear all constraints"
+msgstr "Clear all constraints"
+
+#: ../settings.py:31
+msgid "Visibility:"
+msgstr "Visibility:"
+
+#: ../settings.py:32
+msgid "Hide"
+msgstr "Hide"
+
+#: ../settings.py:34
+msgid "show"
+msgstr "show"
+
+#: ../settings.py:38
+msgid "View all atoms"
+msgstr "View all atoms"
+
+#: ../settings.py:44
+msgid "Miscellaneous:"
+msgstr "Miscellaneous:"
+
+#: ../settings.py:47
+msgid "Scale atomic radii:"
+msgstr "Scale atomic radii:"
+
+#. A close button
+#: ../settings.py:52
+msgid "\n"
+msgstr "\n"
+
+#: ../setupwindow.py:51
+msgid "No crystal structure data"
+msgstr "No crystal structure data"
+
+#: ../setupwindow.py:62
+msgid "  ERROR: Invalid element!"
+msgstr "  ERROR: Invalid element!"
+
+#: ../simulation.py:24
+msgid " (rerun simulation)"
+msgstr " (rerun simulation)"
+
+#: ../simulation.py:25
+msgid " (continue simulation)"
+msgstr " (continue simulation)"
+
+#: ../simulation.py:27
+msgid "Select starting configuration:"
+msgstr "Select starting configuration:"
+
+#: ../simulation.py:32
+#, python-format
+msgid "There are currently %i configurations loaded."
+msgstr "There are currently %i configurations loaded."
+
+#: ../simulation.py:36
+msgid "Choose which one to use as the initial configuration"
+msgstr "Choose which one to use as the initial configuration"
+
+#: ../simulation.py:40
+#, python-format
+msgid "The first configuration %s."
+msgstr "The first configuration %s."
+
+#: ../simulation.py:43
+msgid "Configuration number "
+msgstr "Configuration number "
+
+#: ../simulation.py:49
+#, python-format
+msgid "The last configuration %s."
+msgstr "The last configuration %s."
+
+#: ../simulation.py:85
+msgid "Run"
+msgstr "Run"
+
+#: ../simulation.py:105
+msgid "No calculator: Use Calculate/Set Calculator on the menu."
+msgstr "No calculator: Use Calculate/Set Calculator on the menu."
+
+#: ../status.py:37 ../status.py:39
+msgid "Tip for status box ..."
+msgstr "Tip for status box ..."
+
+#. TRANSLATORS: mom refers to magnetic moment
+#: ../status.py:63
+#, python-format
+msgid " tag=%(tag)s mom=%(mom)1.2f"
+msgstr " tag=%(tag)s mom=%(mom)1.2f"
+
+#: ../status.py:104
+msgid "dihedral"
+msgstr "dihedral"
+
+#: ../surfaceslab.py:14
+msgid ""
+"  Use this dialog to create surface slabs.  Select the element by\n"
+"writing the chemical symbol or the atomic number in the box.  Then\n"
+"select the desired surface structure.  Note that some structures can\n"
+"be created with an othogonal or a non-orthogonal unit cell, in these\n"
+"cases the non-orthogonal unit cell will contain fewer atoms.\n"
+"\n"
+"  If the structure matches the experimental crystal structure, you can\n"
+"look up the lattice constant, otherwise you have to specify it\n"
+"yourself."
+msgstr ""
+"  Use this dialog to create surface slabs.  Select the element by\n"
+"writing the chemical symbol or the atomic number in the box.  Then\n"
+"select the desired surface structure.  Note that some structures can\n"
+"be created with an othogonal or a non-orthogonal unit cell, in these\n"
+"cases the non-orthogonal unit cell will contain fewer atoms.\n"
+"\n"
+"  If the structure matches the experimental crystal structure, you can\n"
+"look up the lattice constant, otherwise you have to specify it\n"
+"yourself."
+
+#. Name, structure, orthogonal, support-nonorthogonal, function
+#: ../surfaceslab.py:26
+msgid "FCC(100)"
+msgstr "FCC(100)"
+
+#: ../surfaceslab.py:26 ../surfaceslab.py:27 ../surfaceslab.py:28
+#: ../surfaceslab.py:30
+msgid "fcc"
+msgstr "fcc"
+
+#: ../surfaceslab.py:27
+msgid "FCC(110)"
+msgstr "FCC(110)"
+
+#: ../surfaceslab.py:28
+msgid "FCC(111) non-orthogonal"
+msgstr "FCC(111) non-orthogonal"
+
+#: ../surfaceslab.py:30
+msgid "FCC(111) orthogonal"
+msgstr "FCC(111) orthogonal"
+
+#: ../surfaceslab.py:31
+msgid "BCC(100)"
+msgstr "BCC(100)"
+
+#: ../surfaceslab.py:31 ../surfaceslab.py:32 ../surfaceslab.py:34
+#: ../surfaceslab.py:35 ../surfaceslab.py:37
+msgid "bcc"
+msgstr "bcc"
+
+#: ../surfaceslab.py:32
+msgid "BCC(110) non-orthogonal"
+msgstr "BCC(110) non-orthogonal"
+
+#: ../surfaceslab.py:34
+msgid "BCC(110) orthogonal"
+msgstr "BCC(110) orthogonal"
+
+#: ../surfaceslab.py:35
+msgid "BCC(111) non-orthogonal"
+msgstr "BCC(111) non-orthogonal"
+
+#: ../surfaceslab.py:37
+msgid "BCC(111) orthogonal"
+msgstr "BCC(111) orthogonal"
+
+#: ../surfaceslab.py:38
+msgid "HCP(0001) non-orthogonal"
+msgstr "HCP(0001) non-orthogonal"
+
+#: ../surfaceslab.py:38 ../surfaceslab.py:40 ../surfaceslab.py:41
+msgid "hcp"
+msgstr "hcp"
+
+#: ../surfaceslab.py:40
+msgid "HCP(0001) orthogonal"
+msgstr "HCP(0001) orthogonal"
+
+#: ../surfaceslab.py:41
+msgid "HCP(10-10) orthogonal"
+msgstr "HCP(10-10) orthogonal"
+
+#: ../surfaceslab.py:43
+msgid "DIAMOND(100) orthogonal"
+msgstr "DIAMOND(100) orthogonal"
+
+#: ../surfaceslab.py:43 ../surfaceslab.py:45
+msgid "diamond"
+msgstr "diamond"
+
+#: ../surfaceslab.py:45
+msgid "DIAMOND(111) non-orthogonal"
+msgstr "DIAMOND(111) non-orthogonal"
+
+#: ../surfaceslab.py:60
+msgid "Surface"
+msgstr "Surface"
+
+#: ../surfaceslab.py:90
+msgid "Lattice constant: "
+msgstr "Lattice constant: "
+
+#: ../surfaceslab.py:97
+msgid "a:"
+msgstr "a:"
+
+#: ../surfaceslab.py:109
+#, python-format
+msgid "(%.1f %% of ideal)"
+msgstr "(%.1f %% of ideal)"
+
+#: ../surfaceslab.py:126
+msgid "Size: \tx: "
+msgstr "Size: \tx: "
+
+#: ../surfaceslab.py:128
+msgid "\t\ty: "
+msgstr "\t\ty: "
+
+#: ../surfaceslab.py:130
+msgid "      \t\tz: "
+msgstr "      \t\tz: "
+
+#: ../surfaceslab.py:131
+msgid " layers,  "
+msgstr " layers,  "
+
+#: ../surfaceslab.py:132
+msgid " Å vacuum"
+msgstr " Å vacuum"
+
+#: ../surfaceslab.py:133
+msgid "\t\tNo size information yet."
+msgstr "\t\tNo size information yet."
+
+#. Buttons
+#: ../surfaceslab.py:142
+msgid "Creating a surface slab."
+msgstr "Creating a surface slab."
+
+#: ../surfaceslab.py:212
+#, python-format
+msgid "%i atoms."
+msgstr "%i atoms."
+
+#: ../surfaceslab.py:224
+msgid "No structure specified!"
+msgstr "No structure specified!"
+
+#: ../surfaceslab.py:233
+#, python-format
+msgid "%(struct)s lattice constant unknown for %(element)s."
+msgstr "%(struct)s lattice constant unknown for %(element)s."
+
+#: ../widgets.py:53 ../widgets.py:80
+msgid "Help"
+msgstr "Help"
+
+#: ../widgets.py:97
+msgid "Clear constraint"
+msgstr "Clear constraint"
+
+#~ msgid "  %8.3f, %8.3f, %8.3f eV/Å\n"
+#~ msgstr "  %8.3f, %8.3f, %8.3f eV/Å\n"
+
+#~ msgid "%s (a=%.3f Å)"
+#~ msgstr "%s (a=%.3f Å)"
+
+#~ msgid "  %s: %s, Z=%i, %s"
+#~ msgstr "  %s: %s, Z=%i, %s"
+
+#~ msgid " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+#~ msgstr " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+
+#~ msgid " %s-%s: %.3f Å"
+#~ msgstr " %s-%s: %.3f Å"
+
+#~ msgid " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+#~ msgstr " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+
+#~ msgid "dihedral %s->%s->%s->%s: %.1f°"
+#~ msgstr "dihedral %s->%s->%s->%s: %.1f°"
+
+#~ msgid "c:"
+#~ msgstr "c:"
+
+#~ msgid "\t\t%.2f Å x %.2f Å x %.2f Å,  %i atoms."
+#~ msgstr "\t\t%.2f Å x %.2f Å x %.2f Å,  %i atoms."
+
+#~ msgid "FILE"
+#~ msgstr "FILE"
+
+#~ msgid "%prog [options] [file[, file2, ...]]"
+#~ msgstr "%prog [options] [file[, file2, ...]]"
+
+#~ msgid "NUMBER"
+#~ msgstr "NUMBER"
+
+#~ msgid ""
+#~ "Pick image(s) from trajectory.  NUMBER can be a single number (use a "
+#~ "negative number to count from the back) or a range: start:stop:step, "
+#~ "where the \":step\" part can be left out - default values are 0:nimages:1."
+#~ msgstr ""
+#~ "Pick image(s) from trajectory.  NUMBER can be a single number (use a "
+#~ "negative number to count from the back) or a range: start:stop:step, "
+#~ "where the \":step\" part can be left out - default values are 0:nimages:1."
+
+#~ msgid "I"
+#~ msgstr "I"
+
+#~ msgid ""
+#~ "0: Don't show unit cell.  1: Show unit cell.  2: Show all of unit cell."
+#~ msgstr ""
+#~ "0: Don't show unit cell.  1: Show unit cell.  2: Show all of unit cell."
+
+#~ msgid "Repeat unit cell.  Use \"-r 2\" or \"-r 2,3,1\"."
+#~ msgstr "Repeat unit cell.  Use \"-r 2\" or \"-r 2,3,1\"."
+
+#~ msgid "Examples: \"-R -90x\", \"-R 90z,-30x\"."
+#~ msgstr "Examples: \"-R -90x\", \"-R 90z,-30x\"."
+
+#~ msgid "Write configurations to FILE."
+#~ msgstr "Write configurations to FILE."
+
+#~ msgid "EXPR"
+#~ msgstr "EXPR"
+
+#~ msgid ""
+#~ "Plot x,y1,y2,... graph from configurations or write data to sdtout in "
+#~ "terminal mode.  Use the symbols: i, s, d, fmax, e, ekin, A, R, E and F.  "
+#~ "See https://wiki.fysik.dtu.dk/ase/ase/gui.html#plotting-data for more "
+#~ "details."
+#~ msgstr ""
+#~ "Plot x,y1,y2,... graph from configurations or write data to sdtout in "
+#~ "terminal mode.  Use the symbols: i, s, d, fmax, e, ekin, A, R, E and F.  "
+#~ "See https://wiki.fysik.dtu.dk/ase/ase/gui.html#plotting-data for more "
+#~ "details."
+
+#~ msgid "Run in terminal window - no GUI."
+#~ msgstr "Run in terminal window - no GUI."
+
+#~ msgid "Read ANEB data."
+#~ msgstr "Read ANEB data."
+
+#~ msgid "N"
+#~ msgstr "N"
+
+#~ msgid "Interpolate N images between 2 given images."
+#~ msgstr "Interpolate N images between 2 given images."
+
+#~ msgid "Draw bonds between atoms."
+#~ msgstr "Draw bonds between atoms."
diff --git a/ase/gui/po/es_ES/LC_MESSAGES/ag.po b/ase/gui/po/es_ES/LC_MESSAGES/ag.po
new file mode 100644
index 0000000..234fe26
--- /dev/null
+++ b/ase/gui/po/es_ES/LC_MESSAGES/ag.po
@@ -0,0 +1,3152 @@
+# Spanish translations for ASE package.
+# Copyright (C) 2012 CAMD
+# This file is distributed under the same license as the ASE package.
+# Ask Hjorth Larsen <askhl at fysik.dtu.dk>, 2012.
+# Max Ramirez <m.ramirez at mx.uni-saarland.de>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ase-3.5.2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-02-01 16:08+0100\n"
+"PO-Revision-Date: 2012-02-02 00:15+0100\n"
+"Last-Translator: Max Ramirez <m.ramirez at mx.uni-saarland.de>\n"
+"Language-Team: Spanish\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../ag.py:130
+msgid ""
+"\n"
+"An exception occurred!  Please report the issue to\n"
+"ase-developers at listserv.fysik.dtu.dk - thanks!  Please also report this if\n"
+"it was a user error, so that a better error message can be provided\n"
+"next time."
+msgstr ""
+"\n"
+"Se produjo un error! Por favor, reporte el problema a\n"
+"\n"
+"ase-developers at listserv.fysik.dtu.dk \n"
+"\n"
+"Muchas gracias! Por favor, reporte también el problema si\n"
+"fue un error de usuario, de esta forma podemos proveer un\n"
+"mejor mensaje de error la próxima vez."
+
+#. Asap and GPAW may be imported if selected.
+#: ../calculator.py:18
+msgid ""
+"To make most calculations on the atoms, a Calculator object must first\n"
+"be associated with it.  ASE supports a number of calculators, supporting\n"
+"different elements, and implementing different physical models for the\n"
+"interatomic interactions."
+msgstr ""
+"Para realizar la mayoría de los calculos sobre los átomos,\n"
+"se debe primero definir y asociar un objeto calculador \n"
+"(Calculator) con éstos. ASE soporta un número de calculadores, \n"
+"los cuales además de poder realizar cálculos sobre distintos \n"
+"elementos, implementan también modelos físicos diferentes para \n"
+"las interacciones interatómicas."
+
+#. Informational text about the calculators
+#: ../calculator.py:26
+msgid ""
+"The Lennard-Jones pair potential is one of the simplest\n"
+"possible models for interatomic interactions, mostly\n"
+"suitable for noble gasses and model systems.\n"
+"\n"
+"Interactions are described by an interaction length and an\n"
+"interaction strength."
+msgstr ""
+"El potencial de pares de Lennard-Jones es uno de los \n"
+"modelos más simples para las interacciones atómicas. \n"
+"Éste es adecuado para describir y modelar sistemas \n"
+"compuestos de gases nobles.\n"
+"\n"
+"Las interacciones en este potencial son descritas por \n"
+"un largo de interacción y una fuerza de interacción."
+
+#: ../calculator.py:35
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au, the Al potential is however not suitable for materials\n"
+"science application, as the stacking fault energy is wrong.\n"
+"\n"
+"A number of parameter sets are provided.\n"
+"\n"
+"<b>Default parameters:</b>\n"
+"\n"
+"The default EMT parameters, as published in K. W. Jacobsen,\n"
+"P. Stoltze and J. K. Nørskov, <i>Surf. Sci.</i> <b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Alternative Cu, Ag and Au:</b>\n"
+"\n"
+"An alternative set of parameters for Cu, Ag and Au,\n"
+"reoptimized to experimental data including the stacking\n"
+"fault energies by Torben Rasmussen (partly unpublished).\n"
+"\n"
+"<b>Ruthenium:</b>\n"
+"\n"
+"Parameters for Ruthenium, as published in J. Gavnholt and\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Metallic glasses:</b>\n"
+"\n"
+"Parameters for MgCu and CuZr metallic glasses. MgCu\n"
+"parameters are in N. P. Bailey, J. Schiøtz and\n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004).\n"
+"CuZr in A. Paduraru, A. Kenoufi, N. P. Bailey and\n"
+"J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+msgstr ""
+"El potencial EMT es un potencial de muchos cuerpos, el cual describe\n"
+"de manera correcta a los metales de transición que cristalizan en una\n"
+"estructura tipo FCC. Los elementos descritos por el conjunto de pará-\n"
+"metros del EMT son Al, Ni, Cu, Pd, Ag, Pt y Au. Sin embargo, la descripción\n"
+"de este potencial para el aluminio no es adecuado para su uso en la\n"
+"ciencia de materiales, ya que la energía de apilamiento es incorrecta.\n"
+"\n"
+"Con ASE se provee un conjunto de parámetros para este potencial.\n"
+"\n"
+"<b>Parámetros por defecto:</b>\n"
+"\n"
+"Los parámetros por defecto de este potencial son extraídos de la siguiente\n"
+"publicación:  K. W. Jacobsen, P. Stoltze y J. K. Nørskov, <i>Surf. Sci.</"
+"i> \n"
+"<b>366</b>, 394 (1996).\n"
+"\n"
+"<b>Parámetros alternativos para Cu, Ag y Au:</b>\n"
+"\n"
+"Un conjunto de parámetros alternativo para Cu, Ag y Au fueron reoptimizados\n"
+"con datos experimentales incluyendo la energía de apilamiento (parcialmente "
+"no\n"
+"publicada) por Torben Rasmussen.\n"
+"\n"
+"<b>Rutenio:</b>\n"
+"\n"
+"Los parámetros para Rutenio fueron extraídos de la publicación J. Gavnholt "
+"y\n"
+"J. Schiøtz, <i>Phys. Rev. B</i> <b>77</b>, 035404 (2008).\n"
+"\n"
+"<b>Vidrios metálicos:</b>\n"
+"\n"
+"Conjunto de parámetros para vidrios metálicos compuestos de MgCu y CuZr. "
+"Los\n"
+"parámetros para MgCu fueron extraídos desde N. P. Bailey, J. Schiøtz y \n"
+"K. W. Jacobsen, <i>Phys. Rev. B</i> <b>69</b>, 144205 (2004). Para CuZr los\n"
+"parámetros fueron extraídos de la publicación A. Paduraru, A. Kenoufi, \n"
+"N. P. Bailey y J. Schiøtz, <i>Adv. Eng. Mater.</i> <b>9</b>, 505 (2007).\n"
+
+#: ../calculator.py:70
+msgid ""
+"The EMT potential is a many-body potential, giving a\n"
+"good description of the late transition metals crystalling\n"
+"in the FCC crystal structure.  The elements described by the\n"
+"main set of EMT parameters are Al, Ni, Cu, Pd, Ag, Pt, and\n"
+"Au.  In addition, this implementation allows for the use of\n"
+"H, N, O and C adatoms, although the description of these is\n"
+"most likely not very good.\n"
+"\n"
+"<b>This is the ASE implementation of EMT.</b> For large\n"
+"simulations the ASAP implementation is more suitable; this\n"
+"implementation is mainly to make EMT available when ASAP is\n"
+"not installed.\n"
+msgstr ""
+"El potencial EMT es un potencial de muchos cuerpos, el\n"
+"cual describe de manera correcta a los metales de tran-\n"
+"sición que cristalizan en una estructura tipo FCC. Los \n"
+"elementos descritos por el conjunto de parámetros del \n"
+"EMT son Al, Ni, Cu, Pd, Ag, Pt y Au. Adicionalmente, esta\n"
+"implementación permite el uso de H, N, O y C. Sin embargo, \n"
+"la descripción de estos no es muy buena.\n"
+"\n"
+"<b>Esta es la implementación de ASE de EMT.</b> Para simu-\n"
+"laciones más grandes la implementación ASAP es más confiable.\n"
+"Esta implemetación es para tener un EMT cuando ASAP no está\n"
+"instalado.\n"
+
+#: ../calculator.py:85
+msgid ""
+"The Brenner potential is a reactive bond-order potential for\n"
+"carbon and hydrocarbons.  As a bond-order potential, it takes\n"
+"into account that carbon orbitals can hybridize in different\n"
+"ways, and that carbon can form single, double and triple\n"
+"bonds.  That the potential is reactive means that it can\n"
+"handle gradual changes in the bond order as chemical bonds\n"
+"are formed or broken.\n"
+"\n"
+"The Brenner potential is implemented in Asap, based on a\n"
+"C implentation published at http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"The potential is documented here:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802.\n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+msgstr ""
+"El potencial de Brenner es un potencial reactivo al orden\n"
+"del enlace para carbón e hidrocarbones. Como es un potencial\n"
+"con orden de enlace, toma en cuenta que los orbitales de \n"
+"carbono pueden hibridizarse de diferentes maneras, y que el\n"
+"carbon puede formar enlaces simples, dobles y triples. \n"
+"Que el potencial sea reactivo significa que puede manejar \n"
+"cambios graduales en el orden del enlace, como por ejemplo\n"
+" que los enlaces químicos se pueden crear y destruir.\n"
+"\n"
+"El potencial de Brenner está implementado en ASAP, basado \n"
+"en una implementación de C publicada en \n"
+"\n"
+"http://www.rahul.net/pcm/brenner/ .\n"
+"\n"
+"El potencial está documentado aquí:\n"
+"  Donald W Brenner, Olga A Shenderova, Judith A Harrison,\n"
+"  Steven J Stuart, Boris Ni and Susan B Sinnott:\n"
+"  \"A second-generation reactive empirical bond order (REBO)\n"
+"  potential energy expression for hydrocarbons\",\n"
+"  J. Phys.: Condens. Matter 14 (2002) 783-802. \n"
+"  doi: 10.1088/0953-8984/14/4/312\n"
+
+#: ../calculator.py:107
+msgid ""
+"GPAW implements Density Functional Theory using a\n"
+"<b>G</b>rid-based real-space representation of the wave\n"
+"functions, and the <b>P</b>rojector <b>A</b>ugmented <b>W</b>ave\n"
+"method for handling the core regions.  \n"
+msgstr ""
+"GPAW implementa la teoría del funcional de la densidad\n"
+"utilizando una representación en el espacio real basado\n"
+"en grillas de las funciones de onda, y el método de las\n"
+"ondas aumentadas proyectadas para manejar las regiones\n"
+"del núcleo.\n"
+
+#: ../calculator.py:114
+msgid ""
+"FHI-aims is an external package implementing density \n"
+"functional theory and quantum chemical methods using \n"
+"all-electron methods and a numeric local orbital basis set. \n"
+"For full details, see http://www.fhi-berlin.mpg.de/aims/ \n"
+"or Comp. Phys. Comm. v180 2175 (2009). The ASE \n"
+"documentation contains information on the keywords and \n"
+"functionalities available within this interface. \n"
+msgstr ""
+"FHI-aims es un paquete externo que implementa la teoría del \n"
+"funcional de la densidad y métodos químicos cuánticos utilizando\n"
+"métodos con todos los electrones y bases numéricas locales. \n"
+"\n"
+"Para mayores detalles, visite\n"
+"\n"
+"http://www.fhi-berlin.mpg.de/aims/ \n"
+"\n"
+"o revise la referencia Comp. Phys. Comm. v180 2175 (2009). \n"
+"La documentación de ASE contiene información sobre las \n"
+"palabras clave y las funcionalidades disponibles en esta \n"
+"interfaz.\n"
+
+#: ../calculator.py:124
+msgid ""
+"WARNING:\n"
+"Your system seems to have more than zero but less than \n"
+"three periodic dimensions. Please check that this is \n"
+"really what you want to compute. Assuming full \n"
+"3D periodicity for this calculator."
+msgstr ""
+"ADVERTENCIA: al parecer su sistema tiene más de una pero menos\n"
+"de tres dimensiones periódicas. Por favor, compruebe que esto es\n"
+"realmente lo que desea calcular. Asumiendo periodicidad 3D completa\n"
+"para éste cálculo."
+
+#: ../calculator.py:131
+msgid ""
+"VASP is an external package implementing density \n"
+"functional functional theory using pseudopotentials \n"
+"or the projector-augmented wave method together \n"
+"with a plane wave basis set. For full details, see\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+msgstr ""
+"VASP es un paquete externo, el cual implementa la\n"
+"teoría del funcional de la densidad utilizando\n"
+"pseudopotenciales ó el método de las ondas proyectadas\n"
+"y aumentadas en conjunto con un conjunto de ondas planas.\n"
+"Para más detalles, visite\n"
+"\n"
+"http://cms.mpi.univie.ac.at/vasp/vasp/\n"
+
+#: ../calculator.py:140
+msgid "Default (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+msgstr "Por defecto (Al, Ni, Cu, Pd, Ag, Pt, Au)"
+
+#: ../calculator.py:141
+msgid "Alternative Cu, Ag and Au"
+msgstr "Parámetros alternativos para Cu, Ag y Au"
+
+#: ../calculator.py:142
+msgid "Ruthenium"
+msgstr "Rutenio"
+
+#: ../calculator.py:143
+msgid "CuMg and CuZr metallic glass"
+msgstr "Vidrios metálicos de CuMg y CuZr"
+
+#: ../calculator.py:158
+msgid "Select calculator"
+msgstr "Seleccione Calculador (Calculator)"
+
+#: ../calculator.py:164
+msgid "Calculator:"
+msgstr "Calculador:"
+
+#. No calculator (the default)
+#: ../calculator.py:167
+msgid "None"
+msgstr "Ninguno"
+
+#: ../calculator.py:172
+msgid "Lennard-Jones (ASAP)"
+msgstr "Lennard-Jones (ASAP)"
+
+#: ../calculator.py:173 ../calculator.py:206 ../calculator.py:215
+#: ../calculator.py:224
+msgid "Setup"
+msgstr "Configuración"
+
+# EMT - Effective Medium Theory (ASAP)
+#: ../calculator.py:180
+msgid "EMT - Effective Medium Theory (ASAP)"
+msgstr "EMT - Teoría Media Efectiva (ASAP)"
+
+#: ../calculator.py:192
+msgid "EMT - Effective Medium Theory (ASE)"
+msgstr "EMT - Teoría Media Efectiva (ASE)"
+
+#: ../calculator.py:198
+msgid "Brenner Potential (ASAP)"
+msgstr "Potencial de Brenner (ASAP)"
+
+#: ../calculator.py:204
+msgid "Density Functional Theory (GPAW)"
+msgstr "Teoría del funcional de la densidad (GPAW)"
+
+#: ../calculator.py:213
+msgid "Density Functional Theory (FHI-aims)"
+msgstr "Teoría del funcional de la densidad (FHI-aims)"
+
+#: ../calculator.py:222
+msgid "Density Functional Theory (VASP)"
+msgstr "Teoría del funcional de la densidad (VASP)"
+
+#: ../calculator.py:235
+msgid "Check that the calculator is reasonable."
+msgstr "Compruebe que el calculador sea razonable."
+
+#: ../calculator.py:298 ../simulation.py:114
+msgid "No atoms present"
+msgstr "No hay átomos presentes"
+
+#: ../calculator.py:388 ../calculator.py:422 ../calculator.py:456
+msgid "ASAP is not installed. (Failed to import asap3)"
+msgstr "ASAP no está instalado. (Error al importar asap3)"
+
+#: ../calculator.py:391
+msgid "You must set up the Lennard-Jones parameters"
+msgstr "Usted debe establecer los parámetros del potencial de Lennard-Jones"
+
+#: ../calculator.py:396
+msgid "Could not create useful Lennard-Jones calculator."
+msgstr "No se pudo crear un calculador Lennard-Jones útil."
+
+#: ../calculator.py:430
+msgid "Could not attach EMT calculator to the atoms."
+msgstr "No se pudo adjuntar el calculador EMT a los átomos."
+
+#: ../calculator.py:472 ../calculator.py:484
+msgid "GPAW is not installed. (Failed to import gpaw)"
+msgstr "GPAW no está instalado (Error al importar gpaw)"
+
+#: ../calculator.py:475
+msgid "You must set up the GPAW parameters"
+msgstr "Debe establecer los parámetros para GPAW"
+
+#: ../calculator.py:516
+msgid "You must set up the FHI-aims parameters"
+msgstr "Debe establecer los parámetros para FHI-aims"
+
+#: ../calculator.py:530
+msgid "You must set up the VASP parameters"
+msgstr "Debe establecer los parámetros para VASP"
+
+#: ../calculator.py:554
+#, python-format
+msgid "Element %(sym)s not allowed by the '%(name)s' calculator"
+msgstr "El elemento %(sym)s no esta permitido para el calculador '%(name)s'"
+
+#: ../calculator.py:561
+msgid "Info"
+msgstr "Información"
+
+#: ../calculator.py:577
+msgid "Lennard-Jones parameters"
+msgstr "Parámetros de Lennard-Jones"
+
+#: ../calculator.py:589
+msgid "Specify the Lennard-Jones parameters here"
+msgstr "Especifique los parámetros de Lennard-Jones aquí"
+
+# Epsilon (eV)
+#: ../calculator.py:592
+msgid "Epsilon (eV):"
+msgstr "Epsilon (eV):"
+
+#: ../calculator.py:596
+msgid "Sigma (Å):"
+msgstr "Sigma (Å):"
+
+#. TRANSLATORS: Shift roughly means adjust (about a potential)
+#: ../calculator.py:600
+msgid "Shift to make smooth at cutoff"
+msgstr "Cambie para que el corte sea suave"
+
+#: ../calculator.py:681
+msgid "GPAW parameters"
+msgstr "Parámetros de GPAW"
+
+#. label = gtk.Label("Specify the GPAW parameters here")
+#. pack(vbox, [label])
+#. Print some info
+#: ../calculator.py:696 ../calculator.py:985 ../calculator.py:1473
+#, python-format
+msgid "%i atoms.\n"
+msgstr "átomos %i.\n"
+
+#: ../calculator.py:698
+#, python-format
+msgid "Orthogonal unit cell: %.2f x %.2f x %.2f Å."
+msgstr "Celda unitaria ortogonal:  %.2f x %.2f x %.2f Å."
+
+#: ../calculator.py:700
+msgid "Non-orthogonal unit cell:\n"
+msgstr "Celda unitaria no ortogonal:\n"
+
+#: ../calculator.py:710 ../calculator.py:1001 ../calculator.py:1488
+msgid "Exchange-correlation functional: "
+msgstr "Funcional de intercambio y correlación: "
+
+#. Grid spacing
+#: ../calculator.py:714
+msgid "Grid spacing"
+msgstr "Espacio de grilla"
+
+#: ../calculator.py:718 ../graphene.py:67 ../graphene.py:79 ../graphene.py:102
+#: ../nanotube.py:47 ../surfaceslab.py:97
+msgid "Å"
+msgstr "Å"
+
+#: ../calculator.py:719
+msgid "Grid points"
+msgstr "Puntos de grilla"
+
+#: ../calculator.py:728
+#, python-format
+msgid "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+msgstr "h<sub>eff</sub> = (%.3f, %.3f, %.3f) Å"
+
+#: ../calculator.py:754 ../calculator.py:1016 ../calculator.py:1524
+msgid "k-points  k = ("
+msgstr "puntos k k=("
+
+#: ../calculator.py:758 ../calculator.py:1020
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å"
+msgstr "Tamaño de los puntos k: (%.1f, %.1f, %.1f) Å"
+
+#. Spin polarized
+#: ../calculator.py:763 ../calculator.py:1486
+msgid "Spin polarized"
+msgstr "Polarizado de espín"
+
+#: ../calculator.py:769
+msgid "FD - Finite Difference (grid) mode"
+msgstr "Modo de grilla tipo diferencias finitas"
+
+#: ../calculator.py:770
+msgid "LCAO - Linear Combination of Atomic Orbitals"
+msgstr "LCAO - Combinaciones lineales de orbitales atómicos"
+
+#: ../calculator.py:773
+msgid "Mode: "
+msgstr "Modo: "
+
+#: ../calculator.py:775
+msgid "sz - Single Zeta"
+msgstr "sz - Single Zeta"
+
+#: ../calculator.py:776
+msgid "szp - Single Zeta polarized"
+msgstr "szp - Single Zeta polarizado"
+
+#: ../calculator.py:777
+msgid "dzp - Double Zeta polarized"
+msgstr "dzp - Doble Zeta polarizado"
+
+#. dzp
+#: ../calculator.py:779
+msgid "Basis functions: "
+msgstr "Funciones base: "
+
+#. Mixer
+#: ../calculator.py:785
+msgid "Non-standard mixer parameters"
+msgstr "Parámetros combinados no estándar"
+
+#: ../calculator.py:981
+msgid "FHI-aims parameters"
+msgstr "Parámetros de FHI-aims"
+
+#: ../calculator.py:988
+msgid "Periodic geometry, unit cell is:\n"
+msgstr "Geometría periódica, la celda unitaria es:\n"
+
+#: ../calculator.py:993
+msgid "Non-periodic geometry.\n"
+msgstr "Geometría no periódica\n"
+
+#: ../calculator.py:1000
+msgid "Hirshfeld-based dispersion correction"
+msgstr "Corrección a la dispersión basada en el método de Hirshfeld"
+
+#. Spin polarized, charge, relativity
+#: ../calculator.py:1026
+msgid "Spin / initial moment "
+msgstr "Spin / momento inicial"
+
+#: ../calculator.py:1044
+msgid "   Charge"
+msgstr "   Carga"
+
+#: ../calculator.py:1046
+msgid "   Relativity"
+msgstr "   Relatividad"
+
+#: ../calculator.py:1048
+msgid " Threshold"
+msgstr " Umbral"
+
+#. self-consistency criteria
+#: ../calculator.py:1053
+msgid "Self-consistency convergence:"
+msgstr "Convergencia auto-consistente:"
+
+#: ../calculator.py:1066
+msgid "Compute forces"
+msgstr "Calcule las fuerzas"
+
+#. XXX: use gtk table for layout.  Spaces will not work well otherwise
+#. (depend on fonts, widget style, ...)
+#. TRANSLATORS: Don't care too much about these, just get approximately
+#. the same string lengths
+#: ../calculator.py:1077
+msgid "Energy:                 "
+msgstr "Energía:                "
+
+#: ../calculator.py:1079
+msgid " eV   Sum of eigenvalues:  "
+msgstr " eV   Suma de los autovalores:  "
+
+#: ../calculator.py:1081 ../calculator.py:1559
+msgid " eV"
+msgstr " eV"
+
+#: ../calculator.py:1082
+msgid "Electron density: "
+msgstr "Densidad electrónica: "
+
+#: ../calculator.py:1084
+msgid "        Force convergence:  "
+msgstr "       Convergencia de la fuerza:  "
+
+#: ../calculator.py:1086
+msgid " eV/Ang  "
+msgstr " eV/Å    "
+
+#: ../calculator.py:1099 ../calculator.py:1570
+msgid "Additional keywords: "
+msgstr "Palabras clave adicionales: "
+
+#. run command and species defaults:
+#: ../calculator.py:1113
+msgid "FHI-aims execution command: "
+msgstr "Comando de ejecución por FHI-aims: "
+
+#: ../calculator.py:1115 ../calculator.py:1587
+msgid "Directory for species defaults: "
+msgstr "Directorio para las especies por defecto: "
+
+#: ../calculator.py:1127 ../calculator.py:1595
+msgid "Set Defaults"
+msgstr "Establecer por defecto"
+
+#: ../calculator.py:1129
+msgid "Import control.in"
+msgstr "Importar control.in"
+
+#: ../calculator.py:1131
+msgid "Export control.in"
+msgstr "Exportar control.in"
+
+#: ../calculator.py:1317
+msgid "Export parameters ... "
+msgstr "Exportar parámetros ... "
+
+#: ../calculator.py:1337
+msgid "Import control.in file ... "
+msgstr "Importar el archivo control.in ... "
+
+#: ../calculator.py:1393 ../calculator.py:1907
+#, python-format
+msgid ""
+"Please use the facilities provided in this window to manipulate the keyword: "
+"%s!"
+msgstr ""
+"Por favor use las interfases previstas en esta ventana para manipular la "
+"palabra clave: \n"
+"%s!"
+
+#: ../calculator.py:1396
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/aims.py."
+msgstr ""
+"No conozco la palabra clave %s\n"
+"\n"
+"Por favor, compruebe que la palabra clave esté correcta!\n"
+"\n"
+"Si usted realmente piensa que debería estar disponible, por favor agrégela "
+"al inicio de\n"
+"\n"
+"ase/calculators/aims.py"
+
+#: ../calculator.py:1469
+msgid "VASP parameters"
+msgstr "Parámetros de VASP"
+
+#: ../calculator.py:1475
+msgid "Periodic geometry, unit cell is: \n"
+msgstr "Geometría periódica, la celda unitaria es: \n"
+
+#: ../calculator.py:1527
+msgid ")    Cutoff: "
+msgstr ") radio de corte: "
+
+#: ../calculator.py:1528
+msgid "    Precision: "
+msgstr "    Precisión: "
+
+#: ../calculator.py:1530
+#, python-format
+msgid "k-points x size:  (%.1f, %.1f, %.1f) Å       "
+msgstr "Tamaño de los puntos k:  (%.1f, %.1f, %.1f) Å       "
+
+# Smearing: 
+#: ../calculator.py:1546
+msgid "Smearing: "
+msgstr "Smearing: "
+
+#: ../calculator.py:1548
+msgid " order: "
+msgstr " orden: "
+
+#: ../calculator.py:1550
+msgid " width: "
+msgstr " ancho: "
+
+#: ../calculator.py:1557
+msgid "Self-consistency convergence: "
+msgstr "Convergencia auto-consistente: "
+
+#. run command and location of POTCAR files:
+#: ../calculator.py:1583
+msgid "VASP execution command: "
+msgstr "Comando de ejecución de VASP: "
+
+#: ../calculator.py:1597
+msgid "Import VASP files"
+msgstr "Importar archivos de VASP"
+
+#: ../calculator.py:1599
+msgid "Export VASP files"
+msgstr "Exportar archivos de VASP"
+
+#: ../calculator.py:1810
+msgid "<b>WARNING:</b> cutoff energy is lower than recommended minimum!"
+msgstr ""
+"<b>ADVERTENCIA:</b> ¡La energía de corte es más baja que el mínimo "
+"recomendado! "
+
+#: ../calculator.py:1862
+msgid "Import VASP input files: choose directory ... "
+msgstr "Importando archivos de entrada de VASP: elija directorio ... "
+
+#: ../calculator.py:1877
+msgid "Export VASP input files: choose directory ... "
+msgstr "Exportando archivos de salida de VASP: elija directorio ... "
+
+#: ../calculator.py:1910
+#, python-format
+msgid ""
+"Don't know this keyword: %s\n"
+"Please check!\n"
+"\n"
+"If you really think it should be available, please add it to the top of ase/"
+"calculators/vasp.py."
+msgstr ""
+"No conozco esta palabra clave: %s\n"
+"¡Por favor, revísela!\n"
+"\n"
+"Si usted realmente cree que debería estar disponible, por favor\n"
+"agrégela al inicio del archivo\n"
+"calculators/vasp.py"
+
+#: ../colors.py:24
+msgid "Colors"
+msgstr "Colores"
+
+#. Upper left: Choose how the atoms are colored.
+#: ../colors.py:41
+msgid "Choose how the atoms are colored:"
+msgstr "Elija el color de los átomos:"
+
+#: ../colors.py:43
+msgid "By atomic number, default \"jmol\" colors"
+msgstr "Por número atómico, colores de \"jmol\" por defecto"
+
+#: ../colors.py:45
+msgid "By atomic number, user specified"
+msgstr "Por número atómico, especificado por el usuario"
+
+#: ../colors.py:46
+msgid "By tag"
+msgstr "Por etiqueta"
+
+#: ../colors.py:47
+msgid "By force"
+msgstr "Por fuerza"
+
+#: ../colors.py:48
+msgid "By velocity"
+msgstr "Por velocidad"
+
+#: ../colors.py:49
+msgid "Manually specified"
+msgstr "Especificado manualmente"
+
+#: ../colors.py:50
+msgid "All the same color"
+msgstr "Todos del mismo color"
+
+#. Now fill in the box for additional information in case the force is used.
+#: ../colors.py:60
+msgid "This should not be displayed!"
+msgstr "¡Esto no debería ser desplegado!"
+
+#: ../colors.py:65 ../colors.py:82 ../rotate.py:25
+msgid "Update"
+msgstr "Actualizar"
+
+#: ../colors.py:67 ../colors.py:84
+msgid "Min: "
+msgstr "Mín: "
+
+#: ../colors.py:69 ../colors.py:86
+msgid "  Max: "
+msgstr "  Máx: "
+
+#: ../colors.py:71 ../colors.py:88
+msgid "  Steps: "
+msgstr "  Pasos: "
+
+#: ../colors.py:95
+msgid "Create a color scale:"
+msgstr "Crear una escala de colores:"
+
+#: ../colors.py:98
+msgid "Black - white"
+msgstr "Negro - blanco"
+
+#: ../colors.py:99
+msgid "Black - red - yellow - white"
+msgstr "Negro - rojo - amarillo - blanco"
+
+#: ../colors.py:100
+msgid "Black - green - white"
+msgstr "Negro - verde - blanco"
+
+#: ../colors.py:101
+msgid "Black - blue - cyan"
+msgstr "Negro - azul - cian"
+
+#: ../colors.py:102
+msgid "Hue"
+msgstr "Tonalidad"
+
+#: ../colors.py:103
+msgid "Named colors"
+msgstr "Colores con nombre"
+
+#: ../colors.py:109
+msgid "Create"
+msgstr "Crear"
+
+#: ../colors.py:367
+#, python-format
+msgid "Max force: %.2f (this frame), %.2f (all frames)"
+msgstr "Fuerza máx: %.2f (este cuadro), %.2f (todos los cuadros)"
+
+#: ../colors.py:369
+#, python-format
+msgid "Max force: %.2f."
+msgstr "Fuerza máx: %.2f."
+
+#: ../colors.py:383
+#, python-format
+msgid "Max velocity: %.2f (this frame), %.2f (all frames)"
+msgstr "Velocidad máxima: %.2f (este cuadro), %.2f (todos los cuadros)"
+
+#: ../colors.py:385
+#, python-format
+msgid "Max velocity: %.2f."
+msgstr "Velocidad máxima: %.2f."
+
+#: ../colors.py:426
+msgid "ERROR"
+msgstr "ERROR"
+
+#: ../colors.py:455
+msgid "ERR"
+msgstr "ERR"
+
+#: ../colors.py:542
+msgid "Incorrect color specification"
+msgstr "Especificación de color incorrecta"
+
+#: ../constraints.py:13 ../widgets.py:89
+msgid "Constraints"
+msgstr "Restricciones"
+
+#: ../constraints.py:15 ../constraints.py:18 ../settings.py:17
+#: ../widgets.py:91 ../widgets.py:94
+msgid "Constrain"
+msgstr "Restricción"
+
+#: ../constraints.py:16 ../settings.py:20 ../settings.py:35 ../widgets.py:92
+msgid " selected atoms"
+msgstr "  átomos seleccionados"
+
+#: ../constraints.py:19 ../widgets.py:95
+msgid " immobile atoms:"
+msgstr " átomos inamovibles:"
+
+#: ../constraints.py:21
+msgid "Unconstrain"
+msgstr "Liberar restriccciones"
+
+#: ../constraints.py:22
+msgid " selected atoms:"
+msgstr " átomos seleccionados:"
+
+#: ../constraints.py:24
+msgid "Clear constraints"
+msgstr "Quitar las restricciones"
+
+#: ../constraints.py:26 ../dft.py:29 ../settings.py:53 ../widgets.py:60
+#: ../widgets.py:99
+msgid "Close"
+msgstr "Cerrar"
+
+#: ../crystal.py:16
+msgid ""
+"  Use this dialog to create crystal lattices. First select the structure,\n"
+"  either from a set of common crystal structures, or by space group "
+"description.\n"
+"  Then add all other lattice parameters.\n"
+"\n"
+"  If an experimental crystal structure is available for an atom, you can\n"
+"  look up the crystal type and lattice constant, otherwise you have to "
+"specify it\n"
+"  yourself.  "
+msgstr ""
+"  Utilice este diálogo para crear estructuras cristalinas.\n"
+"  Seleccione primero la estructura, desde un conjunto común\n"
+"  de estructuras cristalinas ó desde la descripción del grupo\n"
+"  espacial.\n"
+"  Luego añada todos los parámetros de red.\n"
+"\n"
+"  Si dispone de una estructura cristalina experimental para un\n"
+"  átomo, puede buscar el tipo de cristal y la constante de red,\n"
+"  de otra manera tendrá que especificarlas."
+
+#: ../crystal.py:33
+#, python-format
+msgid " %(natoms)i atoms: %(symbols)s, Volume: %(volume).3f A<sup>3</sup>"
+msgstr " %(natoms)i átomos: %(symbols)s, Volumen: %(volume).3f A<sup>3</sup>"
+
+# Crear cristal por grupo espacial
+#: ../crystal.py:58
+msgid "Create Bulk Crystal by Spacegroup"
+msgstr "Crear cristal por grupo espacial"
+
+#: ../crystal.py:72
+msgid "Number: 1"
+msgstr "Número: 1"
+
+#: ../crystal.py:73
+msgid "Lattice: "
+msgstr "Red: "
+
+#: ../crystal.py:73
+msgid "\tSpace group: "
+msgstr "\tGrupo espacial: "
+
+# Tamaño: x: 
+#: ../crystal.py:77
+msgid "Size: x: "
+msgstr "Tamaño: x: "
+
+#: ../crystal.py:78 ../crystal.py:138
+msgid "  y: "
+msgstr "  y: "
+
+#: ../crystal.py:79 ../crystal.py:139
+msgid "  z: "
+msgstr "  z: "
+
+#: ../crystal.py:80 ../surfaceslab.py:127 ../surfaceslab.py:129
+msgid " unit cells"
+msgstr " celdas unitarias"
+
+#: ../crystal.py:92 ../crystal.py:96 ../crystal.py:100 ../crystal.py:104
+#: ../crystal.py:108 ../crystal.py:112
+msgid "free"
+msgstr "libre"
+
+#: ../crystal.py:93 ../crystal.py:102
+msgid "equals b"
+msgstr "igual a b"
+
+#: ../crystal.py:94 ../crystal.py:98
+msgid "equals c"
+msgstr "igual a c"
+
+#: ../crystal.py:95 ../crystal.py:99 ../crystal.py:103 ../crystal.py:107
+#: ../crystal.py:111 ../crystal.py:115
+msgid "fixed"
+msgstr "fijo"
+
+#: ../crystal.py:97 ../crystal.py:101
+msgid "equals a"
+msgstr "igual a a"
+
+#: ../crystal.py:105 ../crystal.py:114
+msgid "equals beta"
+msgstr "igual a beta"
+
+#: ../crystal.py:106 ../crystal.py:110
+msgid "equals gamma"
+msgstr "igual a gama"
+
+#: ../crystal.py:109 ../crystal.py:113
+msgid "equals alpha"
+msgstr "igual a alfa"
+
+#: ../crystal.py:119
+msgid "Lattice parameters"
+msgstr "Parámetros de red"
+
+#: ../crystal.py:120
+msgid "\t\ta:\t"
+msgstr "\t\ta:\t"
+
+#: ../crystal.py:121
+msgid "\talpha:\t"
+msgstr "\talfa:\t"
+
+#: ../crystal.py:122
+msgid "\t\tb:\t"
+msgstr "\t\tb:\t"
+
+#: ../crystal.py:123
+msgid "\tbeta:\t"
+msgstr "\tbeta:\t"
+
+#: ../crystal.py:124
+msgid "\t\tc:\t"
+msgstr "\t\tc:\t"
+
+#: ../crystal.py:125
+msgid "\tgamma:\t"
+msgstr "\tgamma:\t"
+
+#: ../crystal.py:126 ../surfaceslab.py:99
+msgid "Get from database"
+msgstr "Obtener desde la base de datos"
+
+#: ../crystal.py:131
+msgid "Basis: "
+msgstr "Base: "
+
+#: ../crystal.py:137
+msgid "  Element:\t"
+msgstr "  Elemento:%t"
+
+#: ../crystal.py:137
+msgid "\tx: "
+msgstr "\tx: "
+
+#: ../crystal.py:157
+msgid "Creating a crystal."
+msgstr "Creando un cristal."
+
+#: ../crystal.py:202
+#, python-format
+msgid "Symbol: %s"
+msgstr "Símbolo: %s"
+
+#: ../crystal.py:207
+#, python-format
+msgid "Number: %s"
+msgstr "Número: %s"
+
+#: ../crystal.py:210
+msgid "Invalid Spacegroup!"
+msgstr "¡Grupo espacial inválido!"
+
+#: ../crystal.py:336 ../crystal.py:339
+msgid "Please specify a consistent set of atoms."
+msgstr "Por favor, especifique un conjunto consistente de átomos."
+
+#: ../crystal.py:348 ../graphene.py:230 ../nanoparticle.py:613
+#: ../nanotube.py:139 ../surfaceslab.py:248
+msgid "No valid atoms."
+msgstr "Los átomos no son válidos."
+
+#: ../crystal.py:465
+msgid "Can't find lattice definition!"
+msgstr "¡No puedo encontrar la definición de red!"
+
+#: ../debug.py:11
+msgid "Debug"
+msgstr "Depurar"
+
+#: ../dft.py:13
+msgid "DFT"
+msgstr "DFT"
+
+#: ../dft.py:19
+msgid "XC-functional: "
+msgstr "Funcional de XC: "
+
+#: ../dft.py:23 ../repeat.py:16
+msgid "Repeat atoms:"
+msgstr "Repetir átomos:"
+
+#: ../energyforces.py:11
+msgid "Output:"
+msgstr "Salida:"
+
+#: ../energyforces.py:41
+msgid "Save output"
+msgstr "Guardar salida"
+
+#: ../energyforces.py:57
+msgid "Potential energy and forces"
+msgstr "Energía potencial y fuerzas"
+
+#: ../energyforces.py:61
+msgid "Calculate potential energy and the force on all atoms"
+msgstr "Calcular la energía potencial y la fuerza en todos los átomos"
+
+#: ../energyforces.py:65
+msgid "Write forces on the atoms"
+msgstr "Escribir las fuerzas en los átomos"
+
+#: ../energyforces.py:82
+msgid "Potential Energy:\n"
+msgstr "Energía potencial:\n"
+
+#: ../energyforces.py:83
+#, python-format
+msgid "  %8.2f eV\n"
+msgstr "  %8.2f eV\n"
+
+#: ../energyforces.py:84
+#, python-format
+msgid ""
+"  %8.4f eV/atom\n"
+"\n"
+msgstr ""
+"  %8.4f eV/átomo\n"
+"\n"
+
+#: ../energyforces.py:86
+msgid "Forces:\n"
+msgstr "Fuerzas:\n"
+
+#: ../execute.py:23
+msgid ""
+"\n"
+"    Global commands work on all frames or only on the current frame\n"
+"    - Assignment of a global variable may not reference a local one\n"
+"    - use 'Current frame' switch to switch off application to all frames\n"
+"    <c>e</c>:\t\ttotal energy of one frame\n"
+"    <c>fmax</c>:\tmaximal force in one frame\n"
+"    <c>A</c>:\tunit cell\n"
+"    <c>E</c>:\t\ttotal energy array of all frames\n"
+"    <c>F</c>:\t\tall forces in one frame\n"
+"    <c>M</c>:\tall magnetic moments\n"
+"    <c>R</c>:\t\tall atomic positions\n"
+"    <c>S</c>:\tall selected atoms (boolean array)\n"
+"    <c>D</c>:\tall dynamic atoms (boolean array)\n"
+"    examples: <c>frame = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"    Atom commands work on each atom (or a selection) individually\n"
+"    - these can use global commands on the RHS of an equation\n"
+"    - use 'selected atoms only' to restrict application of command\n"
+"    <c>x,y,z</c>:\tatomic coordinates\n"
+"    <c>r,g,b</c>:\tatom display color, range is [0..1]\n"
+"    <c>rad</c>:\tatomic radius for display\n"
+"    <c>s</c>:\t\tatom is selected\n"
+"    <c>d</c>:\t\tatom is movable\n"
+"    <c>f</c>:\t\tforce\n"
+"    <c>Z</c>:\tatomic number\n"
+"    <c>m</c>:\tmagnetic moment\n"
+"    examples: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"    Special commands and objects:\n"
+"    <c>sa,cf</c>:\t(un)restrict to selected atoms/current frame\n"
+"    <c>frame</c>:\tframe number\n"
+"    <c>center</c>:\tcenters the system in its existing unit cell\n"
+"    <c>del S</c>:\tdelete selection\n"
+"    <c>CM</c>:\tcenter of mass\n"
+"    <c>ans[-i]</c>:\tith last calculated result\n"
+"    <c>exec file</c>: executes commands listed in file\n"
+"    <c>cov[Z]</c>:(read only): covalent radius of atomic number Z\n"
+"    <c>gui</c>:\tadvanced: ag window python object\n"
+"    <c>img</c>:\tadvanced: ag images object\n"
+"    "
+msgstr ""
+"\n"
+"   Los comandos globales funcionan tanto en todos los cuadros como\n"
+"   en el cuadro actual\n"
+"   -  La asignación de una variable global puede no ser refernciada\n"
+"      a una local.\n"
+"   -  Utilice el interruptor 'Cuadro actual' para apagar la aplicación\n"
+"      a todos los cuadros.\n"
+"      <c>e</c>: energía total de un cuadro\n"
+"      <c>fmáx</c>: fuerza máxima en un cuadro\n"
+"      <c>A</c>: celda unitaria\n"
+"      <c>E</c>: arreglo con las energías totales en todos los cuadros\n"
+"      <c>F</c>: todas las fuerzas en un cuadro\n"
+"      <c>M</c>: todos los momentos magnéticos\n"
+"      <c>R</c>: todas las posiciones atómicas\n"
+"      <c>S</c>: arreglo booleano, todos los átomos seleccionados\n"
+"      <c>D</c>: arreglo booleano, todos los átomos dinámicos\n"
+"      Ejemplos: <c>cuadro = 1</c>, <c>A[0][1] += 4</c>, <c>e-E[-1]</c>\n"
+"\n"
+"      Los comandos atómicos funcionan en una selección o en cada uno de\n"
+"      los átomos.\n"
+"      - Éstos pueden utilizar comandos globales en el lado derecho de\n"
+"        una ecuación.\n"
+"      - Utilice 'Sólo los átomos seleccionados' para restringir la\n"
+"        aplicación del comando.\n"
+"        <c>x,y,z</c>: coordenadas atómicas\n"
+"        <c>r,g,b</c>: color del átomo, el rango es [0..1]\n"
+"        <c>rad</c>: radio atómico a mostrar\n"
+"        <c>s</c>: átomo es seleccionado\n"
+"        <c>d</c>: átomo es movible\n"
+"        <c>f</c>: fuerza\n"
+"        <c>Z</c>: número atómico\n"
+"        <c>m</c>: momento magnético\n"
+"        ejemplos: <c>x -= A[0][0], s = z > 5, Z = 6</c>\n"
+"\n"
+"      Comandos especiales y objetos:\n"
+"      <c>sa,cf</c>:(un)restrict to selected atoms/current frame\n"
+"      <c>cuadro</c>: número del cuadro\n"
+"      <c>centrar</c>: centra el sistema con respecto a su celda unitaria\n"
+"      <c>borra S</c>: borra la selección\n"
+"      <c>CM</c>: centro de masa\n"
+"      <c>ans[-i]</c>: el i-ésimo resultado calculado\n"
+"      <c>exec archivo</c>: ejecuta el comando listado en archivo\n"
+"      <c>cov[Z]</c>:(sólo lectura): radio covalente del número atómico Z\n"
+"      <c>gui</c>:avanzado: objeto de Pythom, ventana de ag\n"
+"      <c>img</c>:avanzado: objeto de imágenes de ag\n"
+"    "
+
+#: ../execute.py:67
+msgid "Expert user mode"
+msgstr "Modo de usuario experto"
+
+#: ../execute.py:80
+msgid "Welcome to the ASE Expert user mode"
+msgstr "Bienvenido al modo de usuario experto de ASE"
+
+#: ../execute.py:87
+msgid "Only selected atoms (sa)   "
+msgstr "Sólo los átomos seleccionados (sa)   "
+
+#: ../execute.py:89
+msgid "Only current frame (cf)  "
+msgstr "Sólo el cuadro actual (cf)  "
+
+#: ../execute.py:99
+msgid ""
+"Global: Use A, D, E, M, N, R, S, n, frame; Atoms: Use a, f, m, s, x, y, z, "
+"Z     "
+msgstr ""
+"Global: utilice los cuadros A, D, E, M, N, R, S y n; Átomos: utilice a, f, "
+"m, s, x, y, z y Z"
+
+#: ../execute.py:198
+#, python-format
+msgid "*** WARNING: file does not exist - %s"
+msgstr "*** ADVERTENCIA: el archivo no existe - %s"
+
+#: ../execute.py:203
+msgid "*** WARNING: No atoms selected to work with"
+msgstr "***ADVERTENCIA: No hay átomos seleccionados para trabajar"
+
+#: ../execute.py:277
+msgid "*** Only working on selected atoms"
+msgstr "*** Trabajando sólo en los átomos seleccionados"
+
+#: ../execute.py:279
+msgid "*** Working on all atoms"
+msgstr "*** Trabajando en todos los átomos"
+
+#: ../execute.py:283
+msgid "*** Only working on current image"
+msgstr "*** Trabajando solamente en la imagen actual"
+
+#: ../execute.py:285
+msgid "*** Working on all images"
+msgstr "*** Trabajando en todas las imágenes"
+
+#: ../execute.py:301
+msgid "Save Terminal text ..."
+msgstr "Guarde texto a Terminal ..."
+
+#: ../graphene.py:15
+msgid ""
+"Set up a graphene sheet or a graphene nanoribbon.  A nanoribbon may\n"
+"optionally be saturated with hydrogen (or another element)."
+msgstr ""
+"Configure una sábana de grafeno o una nanocinta. Opcionalmente,\n"
+"la nanocinta puede ser saturada con hidrógeno u otro elemento."
+
+#: ../graphene.py:30 ../gui.py:298
+msgid "Graphene"
+msgstr "Grafeno"
+
+#. Choose structure
+#. The structure and lattice constant
+#. Choose the surface structure
+#: ../graphene.py:37 ../nanoparticle.py:138 ../surfaceslab.py:78
+msgid "Structure: "
+msgstr "Estructura: "
+
+#: ../graphene.py:39
+msgid "Infinite sheet"
+msgstr "Sábana infinita"
+
+#: ../graphene.py:39
+msgid "Unsaturated ribbon"
+msgstr "Cinta no saturada"
+
+#: ../graphene.py:40
+msgid "Saturated ribbon"
+msgstr "Cinta saturada"
+
+#. Orientation
+#: ../graphene.py:47
+msgid "Orientation: "
+msgstr "Orientación: "
+
+#: ../graphene.py:50
+msgid "zigzag"
+msgstr "Zigzag"
+
+#: ../graphene.py:50
+msgid "armchair"
+msgstr "Sillón"
+
+#: ../graphene.py:66 ../graphene.py:78 ../nanotube.py:46
+msgid "  Bond length: "
+msgstr "  Largo del enlace: "
+
+#. Choose the saturation element and bond length
+#: ../graphene.py:72
+msgid "Saturation: "
+msgstr "Saturación: "
+
+#: ../graphene.py:75
+msgid "H"
+msgstr "H"
+
+#. Size
+#: ../graphene.py:91
+msgid "Width: "
+msgstr "Ancho: "
+
+#: ../graphene.py:92 ../nanotube.py:65
+msgid "  Length: "
+msgstr "  Largo: "
+
+#. Vacuum
+#: ../graphene.py:100
+msgid "Vacuum: "
+msgstr "Vacío: "
+
+#: ../graphene.py:138 ../nanotube.py:96 ../setupwindow.py:32
+msgid "  No element specified!"
+msgstr "  ¡No se especifica el elemento!"
+
+#: ../graphene.py:231 ../nanoparticle.py:614 ../nanotube.py:140
+#: ../pybutton.py:49 ../surfaceslab.py:249
+msgid "You have not (yet) specified a consistent set of parameters."
+msgstr "No ha especificado aún un conjunto consistente de parámetros."
+
+#: ../graphs.py:19
+msgid "Help for plot ..."
+msgstr "Ayuda para graficar ..."
+
+#: ../graphs.py:30 ../graphs.py:33
+msgid "Plot"
+msgstr "Graficar"
+
+#: ../graphs.py:38
+msgid "clear"
+msgstr "Limpiar"
+
+#: ../graphs.py:92
+msgid "Save data to file ... "
+msgstr "Salve los datos a un archivo ..."
+
+#: ../gtkexcepthook.py:117
+msgid "Bug Detected"
+msgstr "Error detectado"
+
+#: ../gtkexcepthook.py:121
+msgid "A programming error has been detected."
+msgstr "Un error de programación ha sido detectado."
+
+#: ../gtkexcepthook.py:124
+msgid ""
+"It probably isn't fatal, but the details should be reported to the "
+"developers nonetheless."
+msgstr ""
+"Probablemente no es fatal. Sin embargo, los detalles deberían\n"
+"ser reportados a los desarrolladores."
+
+#: ../gtkexcepthook.py:140
+msgid "Report..."
+msgstr "Reporte..."
+
+#: ../gtkexcepthook.py:144
+msgid "Details..."
+msgstr "Detalles..."
+
+#: ../gtkexcepthook.py:160
+#, python-format
+msgid ""
+"From: buggy_application\"\n"
+"To: bad_programmer\n"
+"Subject: Exception feedback\n"
+"\n"
+"%s"
+msgstr ""
+"Desde:  buggy_application\"\n"
+"A: bad_programmer\n"
+"Asunto: Retroalimentación de un error\n"
+"\n"
+"%s"
+
+#. Show details...
+#: ../gtkexcepthook.py:173
+msgid "Bug Details"
+msgstr "Detalles del error"
+
+#: ../gui.py:164
+msgid "_File"
+msgstr "_Archivo"
+
+#: ../gui.py:165
+msgid "_Edit"
+msgstr "_Editar"
+
+#: ../gui.py:166
+msgid "_View"
+msgstr "_Ver"
+
+#: ../gui.py:167
+msgid "_Tools"
+msgstr "_Herramientas"
+
+#. TRANSLATORS: Set up (i.e. build) surfaces, nanoparticles, ...
+#: ../gui.py:169
+msgid "_Setup"
+msgstr "_Configurar"
+
+#: ../gui.py:170
+msgid "_Calculate"
+msgstr "_Calcular"
+
+#: ../gui.py:171
+msgid "_Help"
+msgstr "_Ayuda"
+
+#: ../gui.py:172
+msgid "_Open"
+msgstr "_Abrir"
+
+#: ../gui.py:173
+msgid "Create a new file"
+msgstr "Crear un archivo nuevo"
+
+#: ../gui.py:175
+msgid "_New"
+msgstr "_Nuevo"
+
+#: ../gui.py:176
+msgid "New ase.gui window"
+msgstr "Nueva ventana ase.gui"
+
+#: ../gui.py:178
+msgid "_Save"
+msgstr "_Guardar"
+
+#: ../gui.py:179
+msgid "Save current file"
+msgstr "Guardar archivo actual"
+
+#: ../gui.py:181
+msgid "_Quit"
+msgstr "_Salir"
+
+#: ../gui.py:182
+msgid "Quit"
+msgstr "Salir"
+
+#: ../gui.py:184
+msgid "Select _all"
+msgstr "Seleccionar _todo"
+
+#: ../gui.py:187
+msgid "_Invert selection"
+msgstr "_Invertir selección"
+
+#: ../gui.py:190
+msgid "Select _constrained atoms"
+msgstr "Seleccionar los átomos _restringidos"
+
+#: ../gui.py:193
+msgid "Select _immobile atoms"
+msgstr "Seleccionar los átomos _inmóbiles"
+
+#: ../gui.py:196
+msgid "_Copy"
+msgstr "_Copiar"
+
+#: ../gui.py:197
+msgid "Copy current selection and its orientation to clipboard"
+msgstr "Copiar la selección actual y su orientación"
+
+#: ../gui.py:199
+msgid "_Paste"
+msgstr "_Pegar"
+
+#: ../gui.py:200
+msgid "Insert current clipboard selection"
+msgstr "Insertar selección actual"
+
+#: ../gui.py:202
+msgid "_Modify"
+msgstr "_Modificar"
+
+#: ../gui.py:203
+msgid "Change tags, moments and atom types of the selected atoms"
+msgstr ""
+"Cambiar etiquetas, momentos magnéticos y tipo de los átomos seleccionados"
+
+#: ../gui.py:205
+msgid "_Add atoms"
+msgstr "_Añadir átomos"
+
+#: ../gui.py:206
+msgid "Insert or import atoms and molecules"
+msgstr "Insertar o importar átomos y moléculas"
+
+#: ../gui.py:208
+msgid "_Delete selected atoms"
+msgstr "_Borrar átomos seleccionados"
+
+#: ../gui.py:209
+msgid "Delete the selected atoms"
+msgstr "Borrar los átomos seleccionados"
+
+#: ../gui.py:211
+msgid "_First image"
+msgstr "_Primera imagen"
+
+#: ../gui.py:214
+msgid "_Previous image"
+msgstr "_Imagen previa"
+
+#: ../gui.py:217
+msgid "_Next image"
+msgstr "_Próxima imagen"
+
+#: ../gui.py:220
+msgid "_Last image"
+msgstr "Ú_ltima imagen"
+
+#: ../gui.py:223
+msgid "Quick Info ..."
+msgstr "Información rápida ..."
+
+#: ../gui.py:226
+msgid "Repeat ..."
+msgstr "Repetir ..."
+
+#: ../gui.py:229
+msgid "Rotate ..."
+msgstr "Rotar ..."
+
+#: ../gui.py:232
+msgid "Colors ..."
+msgstr "Colores ..."
+
+#. TRANSLATORS: verb
+#: ../gui.py:235
+msgid "Focus"
+msgstr "Enfocar"
+
+#: ../gui.py:238
+msgid "Zoom in"
+msgstr "Ampliar"
+
+#: ../gui.py:241
+msgid "Zoom out"
+msgstr "Alejar"
+
+#: ../gui.py:244
+msgid "Reset View"
+msgstr "Reiniciar la vista"
+
+#: ../gui.py:247
+msgid "Settings ..."
+msgstr "Ajustes ..."
+
+#: ../gui.py:250
+msgid "VMD"
+msgstr "VMD"
+
+#: ../gui.py:253
+msgid "RasMol"
+msgstr "RasMol"
+
+#: ../gui.py:256
+msgid "xmakemol"
+msgstr "xmakemol"
+
+#: ../gui.py:259
+msgid "avogadro"
+msgstr "avogadro"
+
+#: ../gui.py:262
+msgid "Graphs ..."
+msgstr "Gráficos ..."
+
+#: ../gui.py:265
+msgid "Movie ..."
+msgstr "Película ..."
+
+#: ../gui.py:268
+msgid "Expert mode ..."
+msgstr "Modo experto ..."
+
+#: ../gui.py:271
+msgid "Constraints ..."
+msgstr "Restricciones ..."
+
+#: ../gui.py:274
+msgid "Render scene ..."
+msgstr "Dibujar escena ... "
+
+#: ../gui.py:277
+msgid "DFT ..."
+msgstr "DFT ..."
+
+#: ../gui.py:280
+msgid "NE_B"
+msgstr "NE_B"
+
+#: ../gui.py:283
+msgid "B_ulk Modulus"
+msgstr "Módulo de b_ulto"
+
+#: ../gui.py:286
+msgid "_Bulk Crystal"
+msgstr "Cristal en _bulto"
+
+#: ../gui.py:287
+msgid "Create a bulk crystal with arbitrary orientation"
+msgstr "Crear un cristal en bulto con orientación arbitraria"
+
+#: ../gui.py:289
+msgid "_Surface slab"
+msgstr "Trozo de _superficie"
+
+#: ../gui.py:290
+msgid "Create the most common surfaces"
+msgstr "Crear las superficies más comunes"
+
+#: ../gui.py:292
+msgid "_Nanoparticle"
+msgstr "_Nanopartícula"
+
+#: ../gui.py:293
+msgid "Create a crystalline nanoparticle"
+msgstr "Crear una nanoparticula cristalina"
+
+#: ../gui.py:295
+msgid "Nano_tube"
+msgstr "Nano_tubo"
+
+#: ../gui.py:296
+msgid "Create a nanotube"
+msgstr "Crear un nanotubo"
+
+#: ../gui.py:299
+msgid "Create a graphene sheet or nanoribbon"
+msgstr "Crear una sábana de grafeno o una nanocinta"
+
+#: ../gui.py:301
+msgid "Set _Calculator"
+msgstr "Fijar el _calculador"
+
+#: ../gui.py:302
+msgid "Set a calculator used in all calculation modules"
+msgstr "Fijar un calculador utilizado en todos los módulos de cálculo"
+
+#: ../gui.py:304
+msgid "_Energy and Forces"
+msgstr "_Energía y Fuerzas"
+
+#: ../gui.py:305
+msgid "Calculate energy and forces"
+msgstr "Calcular energía y fuerzas"
+
+#: ../gui.py:307
+msgid "Energy _Minimization"
+msgstr "_Minimización de energía"
+
+#: ../gui.py:308
+msgid "Minimize the energy"
+msgstr "Minimize la energía"
+
+#: ../gui.py:310
+msgid "Scale system"
+msgstr "Escale el sistema"
+
+#: ../gui.py:311
+msgid "Deform system by scaling it"
+msgstr "Deforme el sistema escalándolo"
+
+#: ../gui.py:313
+msgid "_About"
+msgstr "_Acerca de ag"
+
+#: ../gui.py:316
+msgid "Webpage ..."
+msgstr "Página web ..."
+
+#: ../gui.py:317
+msgid "Debug ..."
+msgstr "Quitar errores"
+
+#: ../gui.py:319
+msgid "Show _unit cell"
+msgstr "Mostrar la celda _unitaria"
+
+#: ../gui.py:323
+msgid "Show _axes"
+msgstr "Mostrar los _ejes"
+
+#: ../gui.py:327
+msgid "Show _bonds"
+msgstr "Mostrar los _enlaces"
+
+#: ../gui.py:331
+msgid "_Move atoms"
+msgstr "_Mover los átomos"
+
+#: ../gui.py:335
+msgid "_Rotate atoms"
+msgstr "_Rotar los átomos"
+
+#: ../gui.py:339
+msgid "Orien_t atoms"
+msgstr "Orien_tar los átomos"
+
+#: ../gui.py:351
+#, python-format
+msgid "building menus failed: %s"
+msgstr "La construcción de los menús ha fallado: %s"
+
+#: ../gui.py:620 ../gui.py:1016 ../gui.py:1076
+msgid "Open ..."
+msgstr "Abrir ..."
+
+#: ../gui.py:624 ../gui.py:1019
+msgid "<<filename>>"
+msgstr "<<Nombre de archivo>>"
+
+#: ../gui.py:756
+msgid "Add atoms"
+msgstr "Agregar átomos"
+
+#: ../gui.py:759
+msgid "Paste"
+msgstr "Pegar"
+
+#: ../gui.py:765
+msgid "Insert atom or molecule"
+msgstr "Insertar átomo o molécula"
+
+#: ../gui.py:766 ../gui.py:883
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: ../gui.py:767 ../gui.py:884
+msgid "Moment"
+msgstr "Momento magnético"
+
+#: ../gui.py:768
+msgid "Position"
+msgstr "Posición"
+
+#: ../gui.py:793
+msgid "_Load molecule"
+msgstr "_Cargar molécula"
+
+#: ../gui.py:797 ../gui.py:899
+msgid "_OK"
+msgstr "_OK"
+
+#: ../gui.py:801 ../gui.py:903
+msgid "_Cancel"
+msgstr "_Cancelar"
+
+#: ../gui.py:876
+msgid "Modify"
+msgstr "Modificar"
+
+#: ../gui.py:882
+msgid "Atom"
+msgstr "Átomo"
+
+#: ../gui.py:927
+msgid "Confirmation"
+msgstr "Confirmación"
+
+#: ../gui.py:931
+msgid "Delete selected atom?"
+msgid_plural "Delete selected atoms?"
+msgstr[0] "¿Borrar el átomo seleccionado?"
+msgstr[1] "¿Borrar los átomos seleccionados?"
+
+#: ../gui.py:938
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: ../gui.py:1024 ../gui.py:1106
+msgid "Automatic"
+msgstr "Automático"
+
+#: ../gui.py:1025
+msgid "Dacapo netCDF output file"
+msgstr "Archivo de salida Dacapo (.netCDF)"
+
+#: ../gui.py:1026
+msgid "Virtual Nano Lab file"
+msgstr "Archivo Virtual Nano Lab"
+
+#: ../gui.py:1027
+msgid "ASE pickle trajectory"
+msgstr "Trajectoria ASE pickle"
+
+#: ../gui.py:1028 ../gui.py:1119
+msgid "ASE bundle trajectory"
+msgstr "Trajectoria ASE ligada"
+
+#: ../gui.py:1029
+msgid "GPAW text output"
+msgstr "Archivo de salida de texto (GPAW)"
+
+#: ../gui.py:1030
+msgid "CUBE file"
+msgstr "Archivo CUBE"
+
+#: ../gui.py:1031
+msgid "XCrySDen Structure File"
+msgstr "Archivo de estructura XCrySDen"
+
+#: ../gui.py:1032
+msgid "Dacapo text output"
+msgstr "Archivo de salida de texto (Dacapo)"
+
+#: ../gui.py:1033
+msgid "XYZ-file"
+msgstr "Archivo XYZ"
+
+#: ../gui.py:1034
+msgid "VASP POSCAR/CONTCAR file"
+msgstr "Archivo POSCAR/CONTCAR (VASP)"
+
+#: ../gui.py:1035
+msgid "VASP OUTCAR file"
+msgstr "Archivo OUTCAR (VASP)"
+
+#: ../gui.py:1036
+msgid "Protein Data Bank"
+msgstr "Banco de datos de proteínas"
+
+#: ../gui.py:1037
+msgid "CIF-file"
+msgstr "Archivo CIF"
+
+#: ../gui.py:1038
+msgid "FHI-aims geometry file"
+msgstr "Archivo de geometría (FHI-aims)"
+
+#: ../gui.py:1039
+msgid "FHI-aims output file"
+msgstr "Archivo de salida (FHI-aims)"
+
+#: ../gui.py:1040
+msgid "TURBOMOLE coord file"
+msgstr "Archivo de coordenadas (TURBOMOLE)"
+
+#: ../gui.py:1041
+msgid "exciting input"
+msgstr "Archivo de entrada (exciting)"
+
+#: ../gui.py:1042
+msgid "WIEN2k structure file"
+msgstr "Archivo de estructura (WIEN2k)"
+
+#: ../gui.py:1043
+msgid "DftbPlus input file"
+msgstr "Archivo de entrada (DFTBPlus)"
+
+#: ../gui.py:1044
+msgid "ETSF format"
+msgstr "Formato ETSF"
+
+#: ../gui.py:1045 ../gui.py:1117
+msgid "CASTEP geom file"
+msgstr "Archivo de geometría (CASTEP)"
+
+#: ../gui.py:1046
+msgid "CASTEP output file"
+msgstr "Archivo de salida (CASTEP)"
+
+#: ../gui.py:1047
+msgid "CASTEP trajectory file"
+msgstr "Archivo de trajectoria (CASTEP)"
+
+#: ../gui.py:1048
+msgid "DFTBPlus GEN format"
+msgstr "Formato GEN (DFTBPlus)"
+
+#: ../gui.py:1054
+msgid "File type:"
+msgstr "Tipo de archivo:"
+
+#: ../gui.py:1094
+msgid "Save ..."
+msgstr "Guardar ..."
+
+#: ../gui.py:1107
+msgid "XYZ file"
+msgstr "Archivo XYZ"
+
+#: ../gui.py:1108
+msgid "ASE trajectory"
+msgstr "Trajectoria ASE"
+
+#: ../gui.py:1109
+msgid "PDB file"
+msgstr "Archivo PDB"
+
+#: ../gui.py:1110
+msgid "Gaussian cube file"
+msgstr "Archivo cube (Gaussian)"
+
+#: ../gui.py:1111
+msgid "Python script"
+msgstr "Script de Python"
+
+#: ../gui.py:1112
+msgid "VNL file"
+msgstr "Archivo VNL"
+
+#: ../gui.py:1113
+msgid "Portable Network Graphics"
+msgstr "Archivo PNG"
+
+#: ../gui.py:1114
+msgid "Persistence of Vision"
+msgstr "Archivo POV"
+
+#: ../gui.py:1115
+msgid "Encapsulated PostScript"
+msgstr "Archivo EPS"
+
+#: ../gui.py:1116
+msgid "FHI-aims geometry input"
+msgstr "Geometría de entrada (FHI-aims)"
+
+#: ../gui.py:1118
+msgid "VASP geometry input"
+msgstr "Geometría de entrada (VASP)"
+
+#: ../gui.py:1120
+msgid "cif file"
+msgstr "Archivo cif"
+
+#: ../gui.py:1142
+#, python-format
+msgid "Save current image only (#%d)"
+msgstr "Guardar solamente la imagen actual (#%d)"
+
+#: ../gui.py:1146
+msgid "Slice: "
+msgstr "Trozo: "
+
+#: ../gui.py:1147
+msgid "Help for slice ..."
+msgstr "Ayuda para trozar ..."
+
+#: ../gui.py:1159
+msgid "AG INTERNAL ERROR: strange response in Save,"
+msgstr "ERROR INTERNO DE AG: respuesta extraña en guardar,"
+
+#: ../gui.py:1178
+msgid "Unknown output format!"
+msgstr "¡Formato de salida desconocido!"
+
+#: ../gui.py:1179
+#, python-format
+msgid "Use one of: %s"
+msgstr "Use uno de: %s"
+
+#: ../gui.py:1284
+msgid "Not implemented!"
+msgstr "No implementado!"
+
+#: ../gui.py:1285
+msgid "do you really need it?"
+msgstr "¿realmente necesita esto?"
+
+#: ../minimize.py:20
+msgid "Algorithm: "
+msgstr "Algoritmo: "
+
+#: ../minimize.py:25 ../progress.py:67
+msgid "Convergence criterion: F<sub>max</sub> = "
+msgstr "Criterio de convergencia: F<sub>máx</sub> = "
+
+#: ../minimize.py:30 ../progress.py:70
+msgid "Max. number of steps: "
+msgstr "Número máximo de pasos: "
+
+#. Special stuff for MDMin
+#: ../minimize.py:33
+msgid "Pseudo time step: "
+msgstr "Paso de pseudotiempo: "
+
+#: ../minimize.py:54
+msgid "Energy minimization"
+msgstr "Minimización de energía"
+
+#: ../minimize.py:58
+msgid "Minimize the energy with respect to the positions."
+msgstr "Minimize la energía con respecto a las posiciones."
+
+#. Don't catch errors in the function.
+#. Display status message
+#: ../minimize.py:90 ../scaling.py:299
+msgid "Running ..."
+msgstr "Calculando ..."
+
+#. Update display to reflect cancellation of simulation.
+#: ../minimize.py:107
+#, python-format
+msgid "Minimization CANCELLED after %i steps."
+msgstr "Minimización CANCELADO después de %i iteraciones."
+
+#: ../minimize.py:113 ../scaling.py:350
+msgid "Out of memory, consider using LBFGS instead"
+msgstr "No hay más memoria, considere usar el algoritmo LBFGS"
+
+#. Update display to reflect succesful end of simulation.
+#: ../minimize.py:120
+#, python-format
+msgid "Minimization completed in %i steps."
+msgstr "Minimización hecha en %i pasos."
+
+#. self.connect('delete_event', self.exit2)
+#: ../movie.py:14
+msgid "Movie"
+msgstr "Película"
+
+#: ../movie.py:16
+msgid "Image number:"
+msgstr "Imagen número:"
+
+#: ../movie.py:38
+msgid "Play"
+msgstr "Reproducir"
+
+#: ../movie.py:40
+msgid "Stop"
+msgstr "Detener"
+
+#. TRANSLATORS: This function plays an animation forwards and backwards
+#. alternatingly, e.g. for displaying vibrational movement
+#: ../movie.py:44
+msgid "Rock"
+msgstr "Repetir cuadro"
+
+#: ../movie.py:60
+msgid " Frame rate: "
+msgstr "Velocidad del cuadro: "
+
+#: ../movie.py:61
+msgid " Skip frames: "
+msgstr "Saltar los cuadros: "
+
+#: ../nanoparticle.py:19
+msgid ""
+"Create a nanoparticle either by specifying the number of layers, or using "
+"the\n"
+"Wulff construction.  Please press the [Help] button for instructions on how "
+"to\n"
+"specify the directions.\n"
+"WARNING: The Wulff construction currently only works with cubic crystals!\n"
+msgstr ""
+"Crear una nanopartícula especificando el número de capas,\n"
+"ó utilizando la construcción de Wulff. Por favor, presione\n"
+"el boton de ayuda para leer las instrucciones sobre cómo\n"
+"especificar las direcciones.\n"
+"¡ADVERTENCIA: En esta versión, la construcción de Wulff \n"
+"sólo funciona para cristales cúbicos!\n"
+
+#: ../nanoparticle.py:26
+msgid ""
+"\n"
+"The nanoparticle module sets up a nano-particle or a cluster with a given\n"
+"crystal structure.\n"
+"\n"
+"1) Select the element, the crystal structure and the lattice constant(s).\n"
+"   The [Get structure] button will find the data for a given element.\n"
+"\n"
+"2) Choose if you want to specify the number of layers in each direction, or "
+"if\n"
+"   you want to use the Wulff construction.  In the latter case, you must "
+"specify\n"
+"   surface energies in each direction, and the size of the cluster.\n"
+"\n"
+"How to specify the directions:\n"
+"------------------------------\n"
+"\n"
+"First time a direction appears, it is interpreted as the entire family of\n"
+"directions, i.e. (0,0,1) also covers (1,0,0), (-1,0,0) etc.  If one of "
+"these\n"
+"directions is specified again, the second specification overrules that "
+"specific\n"
+"direction.  For this reason, the order matters and you can rearrange the\n"
+"directions with the [Up] and [Down] keys.  You can also add a new "
+"direction,\n"
+"remember to press [Add] or it will not be included.\n"
+"\n"
+"Example: (1,0,0) (1,1,1), (0,0,1) would specify the {100} family of "
+"directions,\n"
+"the {111} family and then the (001) direction, overruling the value given "
+"for\n"
+"the whole family of directions.\n"
+msgstr ""
+"\n"
+"Este módulo crea una nanopartícula o un cúmulo dada una\n"
+"estructura cristalina.\n"
+"\n"
+"1) Seleccione el elemento, la estructura cristalina y la(s)\n"
+"   constante(s) de red. El botón \"Obtener estructura\" \n"
+"   encontrará los datos para el elemento seleccionado.\n"
+"\n"
+"2) Elija si desea especificar el número de capas en cada \n"
+"   dirección, o si desea utilizar la construcción de Wulff.\n"
+"   En el último caso, se debe especificar las energías de \n"
+"   superficie en cada dirección, y el tamaño del cúmulo.\n"
+"\n"
+"Cómo especificar las direcciones:\n"
+"---------------------------------\n"
+"\n"
+"La primera vez una dirección aparece, la cual es interpretada\n"
+"como la familia completa de las direcciones, es decir, (0,0,1)\n"
+"también cubre la dirección (1,0,0), (-1,0,0) etc. Si una de estas\n"
+"direcciones es especificada nuevamente, la segunda especificación\n"
+"reemplaza esa dirección en específico. Debido a esto, el orden\n"
+"importa y se puede rearreglar la dirección con los botones Arriba y\n"
+"Abajo. También se puede añadir una nueva dirección, recuerde presionar\n"
+"el botón Añadir o ésta no será incluida.\n"
+"\n"
+"Ejemplo: (1,0,0) (1,1,1), (0,0,1) especificará la familia {100} de\n"
+"direcciones, la familia {111} y luego la dirección (001), \n"
+"sobreescribiendo el valor dado por toda la familia de direcciones.\n"
+
+#. Structures:  Abbreviation, name, 4-index (boolean), two lattice const (bool), factory
+#: ../nanoparticle.py:83
+msgid "Face centered cubic (fcc)"
+msgstr "Cúbico centrado en las caras (fcc)"
+
+#: ../nanoparticle.py:84
+msgid "Body centered cubic (bcc)"
+msgstr "Cúbico centrado en el cuerpo (bcc)"
+
+#: ../nanoparticle.py:85
+msgid "Simple cubic (sc)"
+msgstr "Cúbico simple (sc)"
+
+#: ../nanoparticle.py:86
+msgid "Hexagonal closed-packed (hcp)"
+msgstr "Empacamiento hexagonal cerrado (hcp)"
+
+#: ../nanoparticle.py:87
+msgid "Graphite"
+msgstr "Grafito"
+
+#: ../nanoparticle.py:116
+msgid "Nanoparticle"
+msgstr "Nanopartícula"
+
+#. Choose the element
+#. Choose the element and bond length
+#. Choose the element
+#: ../nanoparticle.py:126 ../nanotube.py:40 ../surfaceslab.py:69
+msgid "Element: "
+msgstr "Elemento: "
+
+#: ../nanoparticle.py:130
+msgid "Get structure"
+msgstr "Obtener la estructura"
+
+#: ../nanoparticle.py:154
+msgid "Lattice constant:  a ="
+msgstr "Constante de red: a ="
+
+#. Choose specification method
+#: ../nanoparticle.py:171
+msgid "Method: "
+msgstr "Método: "
+
+#: ../nanoparticle.py:173
+msgid "Layer specification"
+msgstr "Especificación de capas"
+
+#: ../nanoparticle.py:173
+msgid "Wulff construction"
+msgstr "Construcción de Wulff"
+
+#: ../nanoparticle.py:193
+msgid "Dummy placeholder object"
+msgstr "Objeto marcador de posición ficticia"
+
+#: ../nanoparticle.py:195
+msgid "Add new direction:"
+msgstr "Agregar nueva dirección:"
+
+#: ../nanoparticle.py:211
+msgid "Add"
+msgstr "Agregar"
+
+#: ../nanoparticle.py:219
+msgid "Set all directions to default values"
+msgstr "Fijar en todas las direcciones los valores por defecto"
+
+#: ../nanoparticle.py:227
+msgid "Particle size: "
+msgstr "Tamaño de la partícula: "
+
+#: ../nanoparticle.py:228 ../nanoparticle.py:265 ../progress.py:196
+msgid "Number of atoms: "
+msgstr "Número de átomos: "
+
+#: ../nanoparticle.py:233
+msgid "Volume: "
+msgstr "Volumen: "
+
+#: ../nanoparticle.py:238
+msgid "ų"
+msgstr "ų"
+
+#: ../nanoparticle.py:243
+msgid "Rounding: If exact size is not possible, choose the size"
+msgstr "Redondear: si el tamaño exacto no es posible, elegir el tamaño"
+
+#: ../nanoparticle.py:246
+msgid "above  "
+msgstr "sobre  "
+
+#: ../nanoparticle.py:247
+msgid "below  "
+msgstr "abajo  "
+
+#: ../nanoparticle.py:248
+msgid "closest  "
+msgstr "más cercano  "
+
+#: ../nanoparticle.py:251
+msgid "Smaller"
+msgstr "Mas pequeño"
+
+#: ../nanoparticle.py:252
+msgid "Larger"
+msgstr "Más largo"
+
+#: ../nanoparticle.py:267
+msgid "   Approx. diameter: "
+msgstr "   Diámetro aproximado: "
+
+#: ../nanoparticle.py:271
+msgid "Information about the created cluster:"
+msgstr "Información sobre el cluster creado:"
+
+#. Buttons
+#: ../nanoparticle.py:277 ../nanotube.py:75
+msgid "Creating a nanoparticle."
+msgstr "Creando una nanopartícula."
+
+#: ../nanoparticle.py:284
+msgid "Automatic Apply"
+msgstr "Aplicar automáticamente"
+
+#: ../nanoparticle.py:332
+msgid "Up"
+msgstr "Arriba"
+
+#: ../nanoparticle.py:337
+msgid "Down"
+msgstr "Abajo"
+
+#: ../nanoparticle.py:342
+msgid "Delete"
+msgstr "Borrar"
+
+#: ../nanoparticle.py:383
+msgid "Surface energies (as energy/area, NOT per atom):"
+msgstr "Energía de superficie (se reporta energía por área, NO por átomo):"
+
+#: ../nanoparticle.py:389
+msgid "Number of layers:"
+msgstr "Número de capas:"
+
+#: ../nanoparticle.py:418
+msgid "At least one index must be non-zero"
+msgstr "Al menos un índice debe ser distinto de cero"
+
+#: ../nanoparticle.py:421
+msgid "Invalid hexagonal indices"
+msgstr "Índices hexagonales inválidos"
+
+#: ../nanoparticle.py:476 ../surfaceslab.py:218
+msgid "Invalid element."
+msgstr "Elemento inválido."
+
+#: ../nanoparticle.py:486
+msgid "Unsupported or unknown structure"
+msgstr "Estructura no soportada o desconocida"
+
+#: ../nanoparticle.py:603
+#, python-format
+msgid "%.1f Å"
+msgstr "%.1f Å"
+
+#: ../nanotube.py:14
+msgid ""
+"Set up a Carbon nanotube by specifying the (n,m) roll-up vector.\n"
+"Please note that m <= n.\n"
+"\n"
+"Nanotubes of other elements can be made by specifying the element\n"
+"and bond length."
+msgstr ""
+
+#: ../nanotube.py:33
+msgid "Nanotube"
+msgstr "Nanotubo"
+
+#. Choose the structure.
+#: ../nanotube.py:57
+msgid "Select roll-up vector (n,m) and tube length:"
+msgstr "Seleccione vector de roll-up (n,m) y largo del tubo:"
+
+#: ../progress.py:25
+msgid "Progress"
+msgstr "Progreso"
+
+#: ../progress.py:32
+msgid "Scaling deformation:"
+msgstr "Escala de deformación:"
+
+#: ../progress.py:38
+#, python-format
+msgid "Step number %s of %s."
+msgstr "Paso número %s de %s."
+
+#. Minimization progress frame
+#. Box containing frame and spacing
+#: ../progress.py:53
+msgid "Energy minimization:"
+msgstr "Minimización de energía:"
+
+#: ../progress.py:60
+msgid "Step number: "
+msgstr "Paso número: "
+
+#: ../progress.py:62
+msgid "F<sub>max</sub>: "
+msgstr "F<sub>máx</sub>: "
+
+#: ../progress.py:102
+msgid "unknown"
+msgstr "desconocido"
+
+#: ../progress.py:179
+msgid "Status: "
+msgstr "Estado: "
+
+#: ../progress.py:181
+msgid "Iteration: "
+msgstr "Iteración: "
+
+#: ../progress.py:184
+msgid "log<sub>10</sub>(change):"
+msgstr "log<sub>10</sub> (cambio):"
+
+#: ../progress.py:187
+msgid "Wave functions: "
+msgstr "Funciones de onda: "
+
+#: ../progress.py:189
+msgid "Density: "
+msgstr "Densidad: "
+
+#: ../progress.py:191
+msgid "Energy: "
+msgstr "Energía: "
+
+#: ../progress.py:194
+msgid "GPAW version: "
+msgstr "Versión de GPAW: "
+
+#: ../progress.py:197
+msgid "N/A"
+msgstr "No disponible"
+
+#: ../progress.py:198
+msgid "Memory estimate: "
+msgstr "Memoria estimada: "
+
+#: ../progress.py:233
+msgid "No info"
+msgstr "No hay información"
+
+#: ../progress.py:243
+msgid "Initializing"
+msgstr "Iniciando"
+
+#: ../progress.py:244
+msgid "Positions:"
+msgstr "Posiciones:"
+
+#: ../progress.py:248
+msgid "Starting calculation"
+msgstr "Comenzando cálculo"
+
+#: ../progress.py:285
+msgid "unchanged"
+msgstr "sin cambios"
+
+#: ../progress.py:295
+msgid "Self-consistency loop"
+msgstr "Bucle de auto consistencia"
+
+#: ../progress.py:300
+msgid "Calculating forces"
+msgstr "Calculando fuerzas"
+
+#: ../progress.py:301
+msgid " (converged)"
+msgstr " (convergido)"
+
+#: ../pybutton.py:37
+msgid "Python"
+msgstr "Python"
+
+#: ../pybutton.py:48
+msgid "No Python code"
+msgstr "No es código de Python"
+
+#: ../pybutton.py:52
+#, python-format
+msgid ""
+"\n"
+"Title: %(title)s\n"
+"Time: %(time)s\n"
+msgstr ""
+"\n"
+"Título: %(title)s\n"
+"Tiempo:  %(time)s\n"
+
+#: ../pybutton.py:61
+msgid "ag: Python code"
+msgstr "ag: código en Python"
+
+#: ../pybutton.py:65
+msgid "Information:"
+msgstr "Información:"
+
+#: ../pybutton.py:72
+msgid "Python code:"
+msgstr "Código en Python:"
+
+#: ../quickinfo.py:9
+msgid "Single image loaded."
+msgstr "Una imagen cargada."
+
+#: ../quickinfo.py:10
+#, python-format
+msgid "Image %d loaded (0 - %d)."
+msgstr "Imagen %d cargada (0 - %d)."
+
+#: ../quickinfo.py:11
+msgid "Unit cell is fixed."
+msgstr "La celda unitaria está fija."
+
+#: ../quickinfo.py:12
+msgid "Unit cell varies."
+msgstr "La celda unitaria varía."
+
+#: ../quickinfo.py:14
+#, python-format
+msgid ""
+"%s\n"
+"\n"
+"Number of atoms: %d.\n"
+"\n"
+"Unit cell:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+msgstr ""
+"%s\n"
+"\n"
+"Número de átomos: %d.\n"
+"\n"
+"Celda unitaria:\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"  %8.3f  %8.3f  %8.3f\n"
+"%s\n"
+
+#: ../quickinfo.py:29
+msgid "Quick Info"
+msgstr "Información rápida"
+
+#: ../quickinfo.py:33
+msgid "No atoms loaded."
+msgstr "No hay átomos seleccionados."
+
+#: ../render.py:14
+msgid ""
+"    Textures can be used to highlight different parts of\n"
+"    an atomic structure. This window applies the default\n"
+"    texture to the entire structure and optionally\n"
+"    applies a different texture to subsets of atoms that\n"
+"    can be selected using the mouse.\n"
+"    An alternative selection method is based on a boolean\n"
+"    expression in the entry box provided, using the\n"
+"    variables x, y, z, or Z. For example, the expression\n"
+"    Z == 11 and x > 10 and y > 10 \n"
+"    will mark all sodium atoms with x or coordinates \n"
+"    larger than 10. In either case, the button labeled\n"
+"    `Create new texture from selection` will enable\n"
+"    to change the attributes of the current selection. \n"
+"    "
+msgstr ""
+"    Las texturas pueden ser utilizadas para destacar diferentes partes\n"
+"    de una estructura atómica. Esta ventana aplica la textura por defecto\n"
+"    a la estructura completa. Opcionalmente, aplica una textura distinta\n"
+"    a subconjuntos de átomos, los cuales pueden ser seleccionados "
+"utilizando\n"
+"    el ratón.\n"
+"    Además, en esta versión de ASE, se implementa un método de selección \n"
+"    alternativo, el cual está basado en expresiones booleanas. Estas se "
+"pueden\n"
+"    fijar en la caja de entrada, utilizando las variables x, y, z ó Z. Por\n"
+"    ejemplo, la expresión\n"
+"    Z == 11 and x > 10 and y > 10\n"
+"    marcará todos los átomos de sodio con x o coordenadas mayores que 10.\n"
+"    En cualquier caso, el botón 'Crear nueva estructura desde la selección'\n"
+"    activará los cambios a los atributos de la selección actual.\n"
+"    "
+
+#: ../render.py:32
+msgid "Render current view in povray ... "
+msgstr "Dibujar vista actual en povray ... "
+
+#: ../render.py:36
+#, python-format
+msgid "Rendering %d atoms."
+msgstr "Dibujando %d átomos."
+
+#: ../render.py:41
+msgid "Render constraints"
+msgstr "Restricciones del dibujo"
+
+#: ../render.py:44
+msgid "Width"
+msgstr "Ancho"
+
+#: ../render.py:45
+msgid "     Height"
+msgstr "     Altura"
+
+#: ../render.py:52
+msgid "Render unit cell"
+msgstr "Dibujar celda unitaria"
+
+#: ../render.py:61
+msgid "Line width"
+msgstr "Ancho de la línea"
+
+#: ../render.py:63
+msgid "Angstrom           "
+msgstr "Angstrom           "
+
+#: ../render.py:73
+msgid "Set"
+msgstr "Fijar"
+
+#: ../render.py:75
+msgid "Output basename: "
+msgstr "Nombre base para el archivo de salida: "
+
+#: ../render.py:77
+msgid "               Filename: "
+msgstr "               Nombre de archivo: "
+
+#: ../render.py:88
+msgid " Default texture for atoms: "
+msgstr " Textura por defecto para los átomos: "
+
+#: ../render.py:89
+msgid "    transparency: "
+msgstr "    transparencia: "
+
+#: ../render.py:90
+msgid "Define atom selection for new texture:"
+msgstr "Definir al selección del átomo para la nueva textura:"
+
+#: ../render.py:92
+msgid "Select"
+msgstr "Seleccionar"
+
+#: ../render.py:96
+msgid "Create new texture from selection"
+msgstr "Crear nueva textura desde selección"
+
+#: ../render.py:98
+msgid "Help on textures"
+msgstr "Ayuda en texturas"
+
+#: ../render.py:111
+msgid "Camera type: "
+msgstr "Tipo de cámara: "
+
+#: ../render.py:112
+msgid "     Camera distance"
+msgstr "     Distancia de la cámara"
+
+#: ../render.py:113
+msgid "Render current frame"
+msgstr "Dibujar el cuadro actual"
+
+#: ../render.py:117
+#, python-format
+msgid "Render all %d frames"
+msgstr "Dibujar todos los %d cuadros"
+
+#: ../render.py:122
+msgid "Transparent background"
+msgstr "Fondo transparente"
+
+#: ../render.py:125
+msgid "Run povray       "
+msgstr "Ejecutar povray       "
+
+#: ../render.py:128
+msgid "Keep povray files       "
+msgstr "Mantener los archivos povray       "
+
+#: ../render.py:131
+msgid "Show output window"
+msgstr "Mostrar ventana de salida"
+
+#: ../render.py:212
+msgid "  transparency: "
+msgstr "  transparencia: "
+
+#: ../render.py:218
+msgid ""
+"Can not create new texture! Must have some atoms selected to create a new "
+"material!"
+msgstr ""
+"¡No se puede crear la nueva textura! ¡Se debe seleccionar algunos átomos "
+"para crear un nuevo material!"
+
+#: ../repeat.py:14
+msgid "Repeat"
+msgstr "Repetir"
+
+#: ../repeat.py:21
+msgid "Set unit cell"
+msgstr "Fijar la celda unitaria"
+
+#: ../rotate.py:15
+msgid "Rotate"
+msgstr "Rotar"
+
+#: ../rotate.py:17
+msgid "Rotation angles:"
+msgstr "Ángulos de rotación:"
+
+#: ../rotate.py:27
+msgid ""
+"Note:\n"
+"You can rotate freely\n"
+"with the mouse, by holding\n"
+"down mouse button 2."
+msgstr ""
+"Nota:\n"
+"Usted puede rotar libremente\n"
+"con el ratón, presionando el\n"
+"botón número 2 del ratón."
+
+#: ../scaling.py:49
+msgid "Homogeneous scaling"
+msgstr "Escala homogénea"
+
+#: ../scaling.py:59
+msgid "3D deformation   "
+msgstr "Deformación en tres dimensiones   "
+
+#: ../scaling.py:60
+msgid "2D deformation   "
+msgstr "Deformación en dos dimensiones   "
+
+#: ../scaling.py:61
+msgid "1D deformation   "
+msgstr "Deformación en una dimensión   "
+
+#: ../scaling.py:64
+msgid "Bulk"
+msgstr "Bulto"
+
+#: ../scaling.py:66
+msgid "xy-plane"
+msgstr "plano xy"
+
+#: ../scaling.py:68
+msgid "xz-plane"
+msgstr "plano xz"
+
+#: ../scaling.py:70
+msgid "yz-plane"
+msgstr "plano yz"
+
+#: ../scaling.py:72
+msgid "x-axis"
+msgstr "eje x"
+
+#: ../scaling.py:74
+msgid "y-axis"
+msgstr "eje y"
+
+#: ../scaling.py:76
+msgid "z-axis"
+msgstr "eje z"
+
+#: ../scaling.py:89
+msgid "Allow deformation along non-periodic directions."
+msgstr "Permitir deformaciones a lo largo de direcciones no periódicas."
+
+#. Parameters for the deformation
+#: ../scaling.py:94
+msgid "Deformation:"
+msgstr "Deformación:"
+
+#: ../scaling.py:100
+msgid "Maximal scale factor: "
+msgstr "Factor de escala máximo: "
+
+#: ../scaling.py:103
+msgid "Scale offset: "
+msgstr "Compensación de escala: "
+
+#: ../scaling.py:106
+msgid "Number of steps: "
+msgstr "Número de pasos: "
+
+#: ../scaling.py:107
+msgid "Only positive deformation"
+msgstr "Sólo deformaciones positivas"
+
+#. Atomic relaxations
+#: ../scaling.py:112
+msgid "Atomic relaxations:"
+msgstr "Relajaciones atómicas:"
+
+#: ../scaling.py:116
+msgid "On   "
+msgstr "Encendido   "
+
+#: ../scaling.py:117
+msgid "Off"
+msgstr "Apagado"
+
+#. Results
+#: ../scaling.py:128
+msgid "Results:"
+msgstr "Resultados:"
+
+#: ../scaling.py:130
+msgid "Keep original configuration"
+msgstr "Mantener la configuración original"
+
+#: ../scaling.py:132
+msgid "Load optimal configuration"
+msgstr "Cargar la configuración óptima"
+
+#: ../scaling.py:134
+msgid "Load all configurations"
+msgstr "Cargar todas las configuraciones"
+
+#: ../scaling.py:143
+msgid "Strain\t\tEnergy [eV]"
+msgstr "Energía de deformación [eV]"
+
+#: ../scaling.py:144
+msgid "Fit:"
+msgstr "Ajuste:"
+
+#: ../scaling.py:148
+msgid "2nd"
+msgstr "Segundo"
+
+#: ../scaling.py:149
+msgid "3rd"
+msgstr "Tercero"
+
+#: ../scaling.py:153
+msgid "Order of fit: "
+msgstr "Grado del ajuste: "
+
+#. Update display to reflect cancellation of simulation.
+#: ../scaling.py:346
+msgid "Calculation CANCELLED."
+msgstr "Cálculo CANCELADO."
+
+#. Update display to reflect succesful end of simulation.
+#: ../scaling.py:357
+msgid "Calculation completed."
+msgstr "Cálculo terminado."
+
+#: ../scaling.py:380
+msgid "No trustworthy minimum: Old configuration kept."
+msgstr "El mínimo no es confiable: se mantiene la configuración anterior."
+
+#: ../scaling.py:420
+#, python-format
+msgid ""
+"Insufficent data for a fit\n"
+"(only %i data points)\n"
+msgstr ""
+"Datos insuficientes para un ajuste\n"
+"(sólo hay %i puntos)\n"
+
+#: ../scaling.py:424
+msgid ""
+"REVERTING TO 2ND ORDER FIT\n"
+"(only 3 data points)\n"
+"\n"
+msgstr ""
+"VOLVIENDO A UN AJUSTE DE SEGUNDO ORDEN\n"
+"(sólo con 3 puntos)\n"
+"\n"
+
+#: ../scaling.py:440
+msgid "No minimum found!"
+msgstr "¡No se encontró el mínimo!"
+
+#: ../scaling.py:454
+msgid ""
+"\n"
+"WARNING: Minimum is outside interval\n"
+msgstr ""
+"\n"
+"ADVERTENCIA: El mínimo está fuera del intervalo\n"
+
+#: ../scaling.py:455
+msgid "It is UNRELIABLE!\n"
+msgstr "¡Esto NO es confiable!\n"
+
+#: ../settings.py:16
+msgid "Constraints:"
+msgstr "Restricciones:"
+
+#: ../settings.py:19
+msgid "release"
+msgstr "Soltar"
+
+#: ../settings.py:23
+msgid "Constrain immobile atoms"
+msgstr "Restringir los átomos inmóbiles"
+
+#: ../settings.py:25
+msgid "Clear all constraints"
+msgstr "Eliminar todas las restricciones"
+
+#: ../settings.py:31
+msgid "Visibility:"
+msgstr "Visibilidad:"
+
+#: ../settings.py:32
+msgid "Hide"
+msgstr "Esconder"
+
+#: ../settings.py:34
+msgid "show"
+msgstr "Mostrar"
+
+#: ../settings.py:38
+msgid "View all atoms"
+msgstr "Ver todos los átomos"
+
+#: ../settings.py:44
+msgid "Miscellaneous:"
+msgstr "Misceláneos:"
+
+#: ../settings.py:47
+msgid "Scale atomic radii:"
+msgstr "Radio de escala atómica:"
+
+#. A close button
+#: ../settings.py:52
+msgid "\n"
+msgstr "\n"
+
+#: ../setupwindow.py:51
+msgid "No crystal structure data"
+msgstr "No existen los datos de estructura cristalina"
+
+#: ../setupwindow.py:62
+msgid "  ERROR: Invalid element!"
+msgstr "  ERROR: ¡elemento inválido!"
+
+#: ../simulation.py:24
+msgid " (rerun simulation)"
+msgstr " (recalcular la simulación)"
+
+#: ../simulation.py:25
+msgid " (continue simulation)"
+msgstr " (continuar simulación)"
+
+#: ../simulation.py:27
+msgid "Select starting configuration:"
+msgstr "Seleccione la configuración inicial:"
+
+#: ../simulation.py:32
+#, python-format
+msgid "There are currently %i configurations loaded."
+msgstr "Actualmente hay %i configuraciones cargadas."
+
+# Elegir cual será utilizada como la configuración inicial
+#: ../simulation.py:36
+msgid "Choose which one to use as the initial configuration"
+msgstr "Elegir cual será utilizada como la configuración inicial"
+
+#: ../simulation.py:40
+#, python-format
+msgid "The first configuration %s."
+msgstr "La primera configuración %s."
+
+#: ../simulation.py:43
+msgid "Configuration number "
+msgstr "Configuración número "
+
+#: ../simulation.py:49
+#, python-format
+msgid "The last configuration %s."
+msgstr "La última configuración %s."
+
+#: ../simulation.py:85
+msgid "Run"
+msgstr "Calcular"
+
+#: ../simulation.py:105
+msgid "No calculator: Use Calculate/Set Calculator on the menu."
+msgstr "No hay un calculador. Use Calcular/Fijar Calculador en el menú."
+
+#: ../status.py:37 ../status.py:39
+msgid "Tip for status box ..."
+msgstr "Consejo para la ventana de estado ..."
+
+#. TRANSLATORS: mom refers to magnetic moment
+#: ../status.py:63
+#, python-format
+msgid " tag=%(tag)s mom=%(mom)1.2f"
+msgstr " etiqueta=%(tag)s momento magnético=%(mom)1.2f"
+
+#: ../status.py:104
+msgid "dihedral"
+msgstr "diedral"
+
+#: ../surfaceslab.py:14
+msgid ""
+"  Use this dialog to create surface slabs.  Select the element by\n"
+"writing the chemical symbol or the atomic number in the box.  Then\n"
+"select the desired surface structure.  Note that some structures can\n"
+"be created with an othogonal or a non-orthogonal unit cell, in these\n"
+"cases the non-orthogonal unit cell will contain fewer atoms.\n"
+"\n"
+"  If the structure matches the experimental crystal structure, you can\n"
+"look up the lattice constant, otherwise you have to specify it\n"
+"yourself."
+msgstr ""
+"Use esta ventana para crear un trozo de superficie. Seleccione el\n"
+"elemento escribiendo el símbolo químico ó el número atómico en la\n"
+"caja. Luego, seleccione la estructura de la superficie deseada. Note\n"
+"que algunas estructuras pueden ser creadas con una celda unitaria or-\n"
+"togonal u no ortogonal. En estos casos, la celda unitaria no ortogonal\n"
+"contendrá menos átomos.\n"
+"\n"
+"Si la estructura coincide con la estructura cristalina experimental, usted\n"
+"podrá buscar la constante de red en la base de datos de ASE. En otro caso, \n"
+"tendrá que especificarla manualmente."
+
+#. Name, structure, orthogonal, support-nonorthogonal, function
+#: ../surfaceslab.py:26
+msgid "FCC(100)"
+msgstr "FCC(100)"
+
+#: ../surfaceslab.py:26 ../surfaceslab.py:27 ../surfaceslab.py:28
+#: ../surfaceslab.py:30
+msgid "fcc"
+msgstr "fcc"
+
+#: ../surfaceslab.py:27
+msgid "FCC(110)"
+msgstr "FCC(110)"
+
+#: ../surfaceslab.py:28
+msgid "FCC(111) non-orthogonal"
+msgstr "FCC (111) no ortogonal"
+
+#: ../surfaceslab.py:30
+msgid "FCC(111) orthogonal"
+msgstr "FCC (111) ortogonal"
+
+#: ../surfaceslab.py:31
+msgid "BCC(100)"
+msgstr "BCC(100)"
+
+#: ../surfaceslab.py:31 ../surfaceslab.py:32 ../surfaceslab.py:34
+#: ../surfaceslab.py:35 ../surfaceslab.py:37
+msgid "bcc"
+msgstr "bcc"
+
+#: ../surfaceslab.py:32
+msgid "BCC(110) non-orthogonal"
+msgstr "BCC (110) no ortogonal"
+
+#: ../surfaceslab.py:34
+msgid "BCC(110) orthogonal"
+msgstr "BCC (110) ortogonal"
+
+#: ../surfaceslab.py:35
+msgid "BCC(111) non-orthogonal"
+msgstr "BCC (111) no ortogonal"
+
+#: ../surfaceslab.py:37
+msgid "BCC(111) orthogonal"
+msgstr "BCC (111) ortogonal"
+
+#: ../surfaceslab.py:38
+msgid "HCP(0001) non-orthogonal"
+msgstr "HCP (0001) no ortogonal"
+
+#: ../surfaceslab.py:38 ../surfaceslab.py:40 ../surfaceslab.py:41
+msgid "hcp"
+msgstr "hcp"
+
+#: ../surfaceslab.py:40
+msgid "HCP(0001) orthogonal"
+msgstr "HCP (0001) ortogonal"
+
+#: ../surfaceslab.py:41
+msgid "HCP(10-10) orthogonal"
+msgstr "HCP (10-10) ortogonal"
+
+#: ../surfaceslab.py:43
+msgid "DIAMOND(100) orthogonal"
+msgstr "Diamante (100) ortogonal"
+
+#: ../surfaceslab.py:43 ../surfaceslab.py:45
+msgid "diamond"
+msgstr "diamante"
+
+#: ../surfaceslab.py:45
+msgid "DIAMOND(111) non-orthogonal"
+msgstr "Diamante (111) no ortogonal"
+
+#: ../surfaceslab.py:60
+msgid "Surface"
+msgstr "Superficie"
+
+#: ../surfaceslab.py:90
+msgid "Lattice constant: "
+msgstr "Constante de red: "
+
+#: ../surfaceslab.py:97
+msgid "a:"
+msgstr "a:"
+
+#: ../surfaceslab.py:109
+#, python-format
+msgid "(%.1f %% of ideal)"
+msgstr "(%.1f %% de ideal)"
+
+#: ../surfaceslab.py:126
+msgid "Size: \tx: "
+msgstr "Tamaño en\tx:"
+
+#: ../surfaceslab.py:128
+msgid "\t\ty: "
+msgstr "\t\ty: "
+
+#: ../surfaceslab.py:130
+msgid "      \t\tz: "
+msgstr "      \t\tz: "
+
+#: ../surfaceslab.py:131
+msgid " layers,  "
+msgstr " capas,  "
+
+#: ../surfaceslab.py:132
+msgid " Å vacuum"
+msgstr " vacío en Å"
+
+#: ../surfaceslab.py:133
+msgid "\t\tNo size information yet."
+msgstr "\t\tNo hay información sobre el tamaño aún."
+
+#. Buttons
+#: ../surfaceslab.py:142
+msgid "Creating a surface slab."
+msgstr "Creando un trozo de superficie."
+
+#: ../surfaceslab.py:212
+#, python-format
+msgid "%i atoms."
+msgstr "Átomos %i."
+
+#: ../surfaceslab.py:224
+msgid "No structure specified!"
+msgstr "¡No se especificó la estructura!"
+
+#: ../surfaceslab.py:233
+#, python-format
+msgid "%(struct)s lattice constant unknown for %(element)s."
+msgstr ""
+"La constante de red %(struct)s es desconocida para el elemento %(element)s."
+
+#: ../widgets.py:53 ../widgets.py:80
+msgid "Help"
+msgstr "Ayuda"
+
+#: ../widgets.py:97
+msgid "Clear constraint"
+msgstr "Borrar restricción"
+
+#~ msgid "  %8.3f, %8.3f, %8.3f eV/Å\n"
+#~ msgstr "  %8.3f, %8.3f, %8.3f eV/Å\n"
+
+#~ msgid "%s (a=%.3f Å)"
+#~ msgstr "%s (a=%.3f Å)"
+
+#~ msgid "  %s: %s, Z=%i, %s"
+#~ msgstr "  %s: %s, Z=%i, %s"
+
+#~ msgid " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+#~ msgstr " #%d %s (%s): %.3f Å, %.3f Å, %.3f Å "
+
+#~ msgid " %s-%s: %.3f Å"
+#~ msgstr " %s-%s: %.3f Å"
+
+#~ msgid " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+#~ msgstr " %s-%s-%s: %.1f°, %.1f°, %.1f°"
+
+#~ msgid "c:"
+#~ msgstr "c:"
+
+#~ msgid "FILE"
+#~ msgstr "ARCHIVO"
+
+#~ msgid "%prog [options] [file[, file2, ...]]"
+#~ msgstr "%prog [opciones] [archivo[, archivo2, ...]]"
+
+#~ msgid "NUMBER"
+#~ msgstr "NÚMERO"
+
+#~ msgid "I"
+#~ msgstr "I"
+
+#~ msgid "Examples: \"-R -90x\", \"-R 90z,-30x\"."
+#~ msgstr "Ejemplos: \"-R -90x\", \"-R 90z,-30x\"."
+
+#~ msgid "EXPR"
+#~ msgstr "EXPR"
+
+#~ msgid "N"
+#~ msgstr "N"
diff --git a/ase/gui/po/makefile b/ase/gui/po/makefile
new file mode 100644
index 0000000..2429d04
--- /dev/null
+++ b/ase/gui/po/makefile
@@ -0,0 +1,44 @@
+# This makefile is used to update templates for internationalization ("i18n")
+# of ag.
+
+# INSTRUCTIONS
+# ------------
+#
+# To update existing templates with strings from the latest python files, 
+# just run 'make'.
+#
+# To create a translation template for a new language (e.g. de_DE.UTF8), run:
+#     LANG=de_DE.UTF8 make init
+# 
+
+# List of files calling gettext.
+TRANSLATIONFILES=../ag.py ../calculator.py ../colors.py ../constraints.py ../crystal.py ../debug.py ../dft.py ../energyforces.py ../execute.py ../graphene.py ../graphs.py ../gtkexcepthook.py ../gui.py ../minimize.py ../movie.py ../nanoparticle.py ../nanotube.py ../progress.py ../pybutton.py ../quickinfo.py ../render.py ../repeat.py ../rotate.py ../scaling.py ../settings.py ../setupwindow.py ../simulation.py ../status.py ../surfaceslab.py ../widgets.py
+
+i18n: ag.pot update-po compile
+
+# This will update the English template (ag.pot) with English messages from 
+# the Python source code.
+ag.pot: ${TRANSLATIONFILES}
+	xgettext --add-comments --language=Python --keyword=_ --output=ag.pot ${TRANSLATIONFILES}
+
+# This will create an empty translation file ($LANG.po, where $LANG is
+# an environment variable) from the English template ag.pot.
+# The po-file header will have to be edited afterwards.
+init: ag.pot
+	mkdir -p ${LANG}/LC_MESSAGES
+	msginit --locale=${LANG} --input=ag.pot --output-file=${LANG}/LC_MESSAGES/ag.po
+
+# This will merge new/changed English strings from the template ag.pot
+# into all existing translations (*.po), maximally
+# reusing the existing translations.
+update-po: ag.pot
+	for FILE in $(wildcard */LC_MESSAGES/ag.po); do \
+	  msgmerge --update $$FILE ag.pot ;\
+	done
+
+# This will compile all translations (*.po) into binary files in gettext's
+# search directory.
+compile: */LC_MESSAGES/ag.po
+	for DIR in $(wildcard */LC_MESSAGES/); do \
+	  msgfmt -cv --output-file=$$DIR/ag.mo $$DIR/ag.po ;\
+	done
diff --git a/ase/gui/progress.py b/ase/gui/progress.py
new file mode 100644
index 0000000..7cd1dc4
--- /dev/null
+++ b/ase/gui/progress.py
@@ -0,0 +1,357 @@
+# encoding: utf-8
+
+import gtk
+from gettext import gettext as _
+import numpy as np
+from ase.gui.widgets import pack, oops, AseGuiCancelException
+import sys
+import re
+import time
+
+
+class DummyProgressIndicator:
+    def begin(self, **kwargs):
+        pass
+
+    def end(self):
+        pass
+
+class DefaultProgressIndicator(gtk.Window):
+    "Window for reporting progress."
+    waittime = 3  # Time (in sec) after which a progress bar appears.
+    updatetime = 0.1   # Minimum time (in sec) between updates of the progress bars.
+    def __init__(self):
+        gtk.Window.__init__(self)
+        self.set_title(_("Progress"))
+        self.globalbox = gtk.VBox()
+        self.nextupdate = 0
+        self.fmax_max = 1.0
+        
+        # Scaling deformation progress frame
+        self.scalebox = gtk.VBox()
+        self.scaleframe = gtk.Frame(_("Scaling deformation:"))
+        vbox = gtk.VBox()
+        self.scaleframe.add(vbox)
+        pack(self.scalebox, [self.scaleframe])
+        pack(self.scalebox, gtk.Label(""))
+
+        self.label_scale_stepno_format = _("Step number %s of %s.")
+        self.label_scale_stepno = gtk.Label(
+            self.label_scale_stepno_format % ("-" , "-"))
+        pack(vbox, [self.label_scale_stepno])
+        self.scale_progress = gtk.ProgressBar()
+        self.scale_progress.modify_bg(gtk.STATE_PRELIGHT,
+                                      gtk.gdk.color_parse('#00AA00'))
+        pack(vbox, [self.scale_progress])
+
+        vbox.show()
+        self.scaleframe.show()
+        self.globalbox.pack_start(self.scalebox)
+        
+        # Minimization progress frame
+        self.minbox = gtk.VBox()  # Box containing frame and spacing
+        self.minframe = gtk.Frame(_("Energy minimization:"))
+        vbox = gtk.VBox()         # Box containing the frames content.
+        self.minframe.add(vbox)
+        pack(self.minbox, [self.minframe])
+        pack(self.minbox, gtk.Label(""))
+        
+        self.label_min_stepno = gtk.Label("-")
+        pack(vbox, [gtk.Label(_("Step number: ")), self.label_min_stepno])
+        lbl = gtk.Label()
+        lbl.set_markup(_("F<sub>max</sub>: "))
+        self.minimize_progress = gtk.ProgressBar()
+        pack(vbox, [lbl, self.minimize_progress])
+        self.label_min_fmax = gtk.Label("-")
+        lbl = gtk.Label()
+        lbl.set_markup(_("Convergence criterion: F<sub>max</sub> = "))
+        pack(vbox, [lbl, self.label_min_fmax])
+        self.label_min_maxsteps = gtk.Label("-")
+        pack(vbox, [gtk.Label(_("Max. number of steps: ")),
+                    self.label_min_maxsteps])
+        
+        vbox.show()
+        self.minframe.show()
+        self.globalbox.pack_start(self.minbox)
+        self.globalbox.show()
+        self.add(self.globalbox)
+
+        # Make the cancel button
+        self.cancelbut = gtk.Button(stock=gtk.STOCK_CANCEL)
+        self.cancelbut.connect('clicked', self.cancel)
+        pack(self.globalbox, [self.cancelbut], end=True, bottom=True)
+        
+    def begin(self, mode=None, algo=None, fmax=None, steps=None,
+              scalesteps=None):
+        self.mode = mode
+        # Hide all mode-specific boxes
+        self.scalebox.hide()
+        self.minbox.hide()
+        # Activate any relevant box
+        if mode == "scale" or mode == "scale/min":
+            self.scalesteps = int(scalesteps)
+            self.scalebox.show()
+            self.set_scale_progress(0, init=True)
+        if mode == "min" or mode == "scale/min":
+            # It is a minimization.
+            self.minbox.show()
+            self.label_min_stepno.set_text("-")
+            self.label_min_fmax.set_text("%.3f" % (fmax,))
+            self.label_min_maxsteps.set_text(str(int(steps)))
+            self.minimize_progress.set_fraction(0)
+            self.minimize_progress.set_text(_("unknown"))
+        # Record starting time
+        self.starttime = time.time()
+        self.active = None  # Becoming active
+        self.raisecancelexception = False
+        
+    def end(self):
+        self.hide()
+        self.active = False
+
+    def activity(self):
+        "Register that activity occurred."
+        if self.active is None and time.time() > self.starttime + self.waittime:
+            # This has taken so long that a progress bar is needed.
+            self.show()
+            self.active = True
+        # Allow GTK to update display
+        if self.active:
+            while gtk.events_pending(): 
+                gtk.main_iteration()
+        if self.raisecancelexception:
+            self.cancelbut.set_sensitive(True)
+            raise AseGuiCancelException
+
+    def cancel(self, widget):
+        print "CANCEL pressed."
+        # We cannot raise the exception here, as this function is
+        # called by the GTK main loop.
+        self.raisecancelexception = True
+        self.cancelbut.set_sensitive(False)
+
+    def set_scale_progress(self, step, init=False):
+        "Set the step number in scaling deformation."
+        self.label_scale_stepno.set_text(
+            self.label_scale_stepno_format % (step, self.scalesteps))
+        percent = 1.0 * step / self.scalesteps
+        self.scale_progress.set_fraction(percent)
+        self.scale_progress.set_text("%i%%" % (round(100*percent),))
+        if not init:
+            self.activity()
+        
+    def logger_write(self, line):
+        if time.time() > self.nextupdate:
+            if self.mode == "min" or self.mode == "scale/min":
+                # Update the minimization progress bar.
+                w = line.split()
+                fmax = float(w[-1])
+                step = w[1]
+                if fmax > self.fmax_max:
+                    self.fmax_max = np.ceil(fmax)
+                self.minimize_progress.set_fraction(fmax / self.fmax_max)
+                self.minimize_progress.set_text(w[-1])
+                self.label_min_stepno.set_text(step)
+            else:
+                raise RuntimeError(
+                    "ProgressIndicator.logger_write called unexpectedly")
+            self.activity()
+            self.nextupdate = time.time() + self.updatetime
+            
+    def get_logger_stream(self):
+        return LoggerStream(self)
+
+
+class GpawProgressIndicator(DefaultProgressIndicator):
+    "Window for reporting GPAW progress."
+
+    def __init__(self):
+        DefaultProgressIndicator.__init__(self)
+
+        # GPAW progress frame
+        self.gpawframe = gtk.Frame("GPAW progress:")
+        vbox = self.gpawvbox = gtk.VBox()
+        self.gpawframe.add(vbox)
+        self.table = gtk.Table(1, 2)
+        self.tablerows = 0
+        pack(vbox, self.table)
+        self.status = gtk.Label("-")
+        self.tablepack([gtk.Label(_("Status: ")), self.status])
+        self.iteration = gtk.Label("-")
+        self.tablepack([gtk.Label(_("Iteration: ")), self.iteration])
+        self.tablepack([gtk.Label("")])
+        lbl = gtk.Label()
+        lbl.set_markup(_("log<sub>10</sub>(change):"))
+        self.tablepack([gtk.Label(""), lbl])
+        self.wfs_progress = gtk.ProgressBar()
+        self.tablepack([gtk.Label(_("Wave functions: ")), self.wfs_progress])
+        self.dens_progress = gtk.ProgressBar()
+        self.tablepack([gtk.Label(_("Density: ")), self.dens_progress])
+        self.energy_progress = gtk.ProgressBar()
+        self.tablepack([gtk.Label(_("Energy: ")), self.energy_progress])
+        self.tablepack([gtk.Label("")])
+        self.versionlabel = gtk.Label("")
+        self.tablepack([gtk.Label(_("GPAW version: ")), self.versionlabel])
+        self.natomslabel = gtk.Label("")
+        self.tablepack([gtk.Label(_("Number of atoms: ")), self.natomslabel])
+        self.memorylabel = gtk.Label(_("N/A"))
+        self.tablepack([gtk.Label(_("Memory estimate: ")), self.memorylabel])
+        self.globalbox.pack_start(self.gpawframe)
+        self.gpawframe.show()
+
+        vbox.show()
+        self.active = False
+
+    def tablepack(self, widgets):
+        self.tablerows += 1
+        self.table.resize(self.tablerows, 2)
+        for i, w in enumerate(widgets):
+            self.table.attach(w, i, i+1, self.tablerows-1, self.tablerows)
+            if hasattr(w, "set_alignment"):
+                w.set_alignment(0, 0.5)
+            w.show()
+            
+    def begin(self, **kwargs):
+        DefaultProgressIndicator.begin(self, **kwargs)
+        # Set GPAW specific stuff.
+        self.active = True
+        self.oldenergy = None
+        self.poscount = None
+        self.reset_gpaw_bars()
+        # With GPAW, all calculations are slow: Show progress window
+        # immediately.
+        self.show()
+        while gtk.events_pending():
+            gtk.main_iteration()
+
+    def reset_gpaw_bars(self):
+        for lbl in (self.status, self.iteration):
+            lbl.set_text("-")
+        for bar in (self.wfs_progress, self.dens_progress,
+                    self.energy_progress):
+            bar.set_fraction(0.0)
+            bar.set_text(_("No info"))
+
+    def gpaw_write(self, txt):
+        #if not self.active:
+        #    self.begin()
+        sys.stdout.write(txt)
+        versearch = re.search("\|[ |_.]+([0-9]+\.[0-9]+\.[0-9]+)", txt)
+        if versearch:
+            # Starting a gpaw calculation.
+            self.versionlabel.set_text(versearch.group(1))
+            self.status.set_text(_("Initializing"))
+        elif txt.startswith(_("Positions:")):
+            # Start counting atoms
+            self.poscount = True
+            self.reset_gpaw_bars()
+            self.status.set_text(_("Starting calculation"))
+            self.oldenergy = None
+        elif txt.strip() == "":
+            # Stop counting atoms
+            self.poscount = False
+        elif self.poscount:
+            # Count atoms.
+            w = txt.split()
+            assert(len(w) == 5)
+            self.natoms = int(w[0]) + 1
+            self.natomslabel.set_text(str(self.natoms))
+        elif txt.startswith("iter:"):
+            # Found iteration line.
+            wfs = txt[self.wfs_idx:self.density_idx].strip()
+            dens = txt[self.density_idx:self.energy_idx].strip()
+            energy = txt[self.energy_idx:self.fermi_idx].strip()
+            if wfs:
+                p = fraction(float(wfs), -9.0)
+                self.wfs_progress.set_fraction(p)
+                self.wfs_progress.set_text(wfs)
+            if dens:
+                p = fraction(float(dens), -4.0)
+                self.dens_progress.set_fraction(p)
+                self.dens_progress.set_text(dens)
+            if energy:
+                if self.oldenergy is None:
+                    self.oldenergy = float(energy)
+                else:
+                    de = abs(self.oldenergy - float(energy))
+                    self.oldenergy = float(energy)
+                    if de > 1e-10:
+                        de = np.log10(de/self.natoms)
+                        p = fraction(de, -3.0)
+                        self.energy_progress.set_fraction(p)
+                        self.energy_progress.set_text("%.1f" % de)
+                    else:
+                        self.energy_progress.set_fraction(1)
+                        self.energy_progress.set_text(_("unchanged"))
+            words = txt.split()
+            self.iteration.set_text(words[1])
+        elif (-1 < txt.find("WFS") < txt.find("Density") < txt.find("Energy")
+               < txt.find("Fermi")):
+            # Found header of convergence table
+            self.wfs_idx = txt.find("WFS")
+            self.density_idx = txt.find("Density")
+            self.energy_idx = txt.find("Energy")
+            self.fermi_idx = txt.find("Fermi")
+            self.status.set_text(_("Self-consistency loop"))
+            self.iteration.set_text("0")
+        elif txt.find("Converged After") != -1:
+            # SCF loop has converged.
+            words = txt.split()
+            self.status.set_text(_("Calculating forces"))
+            self.iteration.set_text(words[2] + _(" (converged)"))
+        elif -1 < txt.find("Calculator") < txt.find("MiB"):
+            # Memory estimate
+            words = txt.split()
+            self.memorylabel.set_text(words[1]+" "+words[2])
+        self.activity()
+
+    def get_gpaw_stream(self):
+        return GpawStream(self)
+
+        
+
+class LoggerStream:
+    "A file-like object feeding minimizer logs to GpawProgressWindow."
+    def __init__(self, progresswindow):
+        self.window = progresswindow
+        
+    def write(self, txt):
+        self.window.logger_write(txt)
+
+    def flush(self):
+        pass
+    
+        
+class GpawStream:
+    "A file-like object feeding GPAWs txt file to GpawProgressWindow."
+    def __init__(self, progresswindow):
+        self.window = progresswindow
+        
+    def write(self, txt):
+        if txt == "":
+            return
+        endline = txt[-1] == '\n'
+        if endline:
+            txt = txt[:-1]
+        lines = txt.split("\n")
+        if endline:
+            for l in lines:
+                self.window.gpaw_write(l+'\n')
+        else:
+            for l in lines[:-1]:
+                self.window.gpaw_write(l+'\n')
+            self.window.gpaw_write(lines[-1])
+
+    def flush(self):
+        pass
+
+def fraction(value, maximum):
+    p = value/maximum
+    if p < 0.0:
+        return 0.0
+    elif p > 1.0:
+        return 1.0
+    else:
+        return p
+    
+    
diff --git a/ase/gui/pybutton.py b/ase/gui/pybutton.py
new file mode 100644
index 0000000..55c70eb
--- /dev/null
+++ b/ase/gui/pybutton.py
@@ -0,0 +1,81 @@
+
+"""pybutton.py - a button for displaying Python code.
+
+This module defines two classes, together they implement a button that
+a module can use to display Python.
+
+PyButton
+--------
+
+PyButton is a gkt.Button with the label 'Python'.  When pressed, it
+opens a PyWindow displaying some Python code, or an error message if
+no Python code is ready.
+
+The script is stored in the attribute .python, it is the
+responsability of the owning object to keep this attribute up to date:
+when pressing the Apply button would result in a sensible
+configuration being created, the python attribute must be set to a
+string creating this code.  When pressing Apply would cause an error,
+the python attribute must be set to None.
+
+PyWindow
+--------
+
+Displays the Python code.  This object is created by the PyButton
+object when needed.
+"""
+
+import gtk
+from gettext import gettext as _
+import time
+from ase.gui.widgets import oops, pack
+
+
+class PyButton(gtk.Button):
+    "A button for displaying Python code."
+    def __init__(self, title):
+        gtk.Button.__init__(self, _("Python"))
+        self.title = title
+        self.python = None
+        self.connect_after('clicked', self.run)
+
+    def run(self, *args):
+        "The method called when the button is clicked."
+        if self.python:
+            now = time.ctime()
+            win = PyWindow(self.title, now, self.python)
+        else:
+            oops(_("No Python code"),
+                 _("You have not (yet) specified a "
+                   "consistent set of parameters."))
+
+fr1_template = _("""
+Title: %(title)s
+Time: %(time)s
+""")
+
+class PyWindow(gtk.Window):
+    "A window displaying Python code."
+    def __init__(self, title, time, code):
+        gtk.Window.__init__(self)
+        self.set_title(_("ag: Python code"))
+        vbox = gtk.VBox()
+        lbl = gtk.Label(fr1_template % dict(title=title, time=time))
+        lbl.set_alignment(0.0, 0.5)
+        fr = gtk.Frame(_("Information:"))
+        fr.add(lbl)
+        pack(vbox, fr)
+        txtbuf = gtk.TextBuffer()
+        txtbuf.set_text(code)
+        txtview = gtk.TextView(txtbuf)
+        txtview.set_editable(False)
+        fr = gtk.Frame(_("Python code:"))
+        fr.add(txtview)
+        fr.set_label_align(0.0, 0.5)
+        pack(vbox, fr)
+        but =  gtk.Button(stock=gtk.STOCK_OK)
+        but.connect('clicked', lambda x: self.destroy())
+        pack(vbox, [but], end=True, bottom=True)
+        self.add(vbox)
+        self.show_all()
+        
diff --git a/ase/gui/quickinfo.py b/ase/gui/quickinfo.py
new file mode 100644
index 0000000..36393a2
--- /dev/null
+++ b/ase/gui/quickinfo.py
@@ -0,0 +1,67 @@
+# encoding: utf-8
+
+"Module for displaying information about the system."
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack
+
+singleimage = _("Single image loaded.")
+multiimage = _("Image %d loaded (0 - %d).")
+ucconst = _("Unit cell is fixed.")
+ucvaries = _("Unit cell varies.")
+
+format = _("""\
+%s
+
+Number of atoms: %d.
+
+Unit cell:
+  %8.3f  %8.3f  %8.3f
+  %8.3f  %8.3f  %8.3f
+  %8.3f  %8.3f  %8.3f
+%s
+""")
+
+class QuickInfo(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_title(_("Quick Info"))
+        vbox = gtk.VBox()
+        images = gui.images
+        if images.natoms < 1:
+            txt = _("No atoms loaded.")
+        else:
+            (nimg, natoms, three) = images.P.shape
+            assert three == 3
+            img = gui.frame
+            uc = images.A[img]
+            if nimg > 1:
+                equal = True
+                for i in range(nimg):
+                    equal = equal and (uc == images.A[i]).all()
+                if equal:
+                    uctxt = ucconst
+                else:
+                    uctxt = ucvaries
+            else:
+                uctxt = ""
+            if nimg == 1:
+                imgtxt = singleimage
+            else:
+                imgtxt = multiimage % (img, nimg-1)
+            txt = format % ((imgtxt, natoms) + tuple(uc.flat) + (uctxt,))
+        label = gtk.Label(txt)
+        pack(vbox, [label])
+        but = gtk.Button(stock=gtk.STOCK_CLOSE)
+        but.connect('clicked', self.close)
+        pack(vbox, [but], end=True)
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def close(self, *args):
+        self.destroy()
+
+    
diff --git a/ase/gui/render.py b/ase/gui/render.py
new file mode 100644
index 0000000..e7c85f3
--- /dev/null
+++ b/ase/gui/render.py
@@ -0,0 +1,326 @@
+#!/usr/bin/env python
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, Help, oops
+from ase.io.pov import write_pov
+from ase.gui.status import formula
+from os.path import basename
+from os import system
+import numpy as np
+
+class Render(gtk.Window):
+    finish_list = ['ase2','ase3','glass','simple','pale','intermediate','vmd','jmol']
+    cameras = ['orthographic','perspective','ultra_wide_angle']
+    selection_info_txt = _("""\
+    Textures can be used to highlight different parts of
+    an atomic structure. This window applies the default
+    texture to the entire structure and optionally
+    applies a different texture to subsets of atoms that
+    can be selected using the mouse.
+    An alternative selection method is based on a boolean
+    expression in the entry box provided, using the
+    variables x, y, z, or Z. For example, the expression
+    Z == 11 and x > 10 and y > 10 
+    will mark all sodium atoms with x or coordinates 
+    larger than 10. In either case, the button labeled
+    `Create new texture from selection` will enable
+    to change the attributes of the current selection. 
+    """)
+    def __init__(self, gui):
+        self.gui = gui
+        gtk.Window.__init__(self)
+        self.set_title(_('Render current view in povray ... '))
+        vbox = gtk.VBox()
+        vbox.set_border_width(5)
+        self.natoms = self.gui.images.natoms
+        pack(vbox, [gtk.Label(_("Rendering %d atoms.") % self.natoms)])
+        self.size = [gtk.Adjustment(self.gui.width, 1, 9999, 50),
+                     gtk.Adjustment(self.gui.height, 1, 9999, 50)]
+        self.width = gtk.SpinButton(self.size[0], 0, 0)
+        self.height = gtk.SpinButton(self.size[1], 0, 0)
+        self.render_constraints = gtk.CheckButton(_("Render constraints"))
+        self.render_constraints.set_sensitive(not self.gui.images.dynamic.all())
+        self.render_constraints.connect("toggled",self.toggle_render_lines)
+        pack(vbox, [gtk.Label(_("Width")), self.width,
+                    gtk.Label(_("     Height")), self.height,
+                    gtk.Label("       "),self.render_constraints])
+        self.width.connect('value-changed',self.change_width,"")
+        self.height.connect('value-changed',self.change_height,"")
+        self.sizeratio = gui.width/float(gui.height)
+        self.line_width = gtk.SpinButton(gtk.Adjustment(0.07,0.01,9.99,0.01), 0, 0)
+        self.line_width.set_digits(3)
+        self.render_cell = gtk.CheckButton(_("Render unit cell"))
+        if self.gui.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell').get_active():
+            self.render_cell.set_active(True)
+        else:
+            self.render_cell.set_active(False)
+            self.render_cell.set_sensitive(False)
+        self.render_cell.connect("toggled",self.toggle_render_lines)
+        have_lines = (not self.gui.images.dynamic.all()) or self.render_cell.get_active()
+        self.line_width.set_sensitive(have_lines)
+        pack(vbox, [gtk.Label(_("Line width")),
+                    self.line_width,
+                    gtk.Label(_("Angstrom           ")),
+                    self.render_cell])
+        pack(vbox, [gtk.Label("")])
+        filename = gui.window.get_title()
+        len_suffix = len(filename.split('.')[-1])+1
+        if len(filename) > len_suffix:
+            filename = filename[:-len_suffix]
+        self.basename = gtk.Entry(max = 30)
+        self.basename.connect("activate",self.set_outputname,"")
+        self.basename.set_text(basename(filename))
+        set_name = gtk.Button(_("Set"))
+        set_name.connect("clicked",self.set_outputname,"")
+        pack(vbox,[gtk.Label(_("Output basename: ")), self.basename,set_name])
+        self.outputname = gtk.Label("")
+        pack(vbox,[gtk.Label(_("               Filename: ")),self.outputname])
+        pack(vbox,[gtk.Label("")])
+        self.tbox = gtk.VBox()
+        self.tbox.set_border_width(10)
+        self.default_texture = gtk.combo_box_new_text()
+        for t in self.finish_list:
+            self.default_texture.append_text(t)
+        self.default_texture.set_active(1)
+        self.default_transparency = gtk.Adjustment(0,0.0,1.0,0.01)
+        self.transparency = gtk.SpinButton(self.default_transparency, 0, 0)
+        self.transparency.set_digits(2)
+        pack(self.tbox,[gtk.Label(_(" Default texture for atoms: ")), self.default_texture,
+                        gtk.Label(_("    transparency: ")),self.transparency])
+        pack(self.tbox,[gtk.Label(_("Define atom selection for new texture:"))])
+        self.texture_selection = gtk.Entry(max = 50)
+        self.texture_select_but = gtk.Button(_("Select"))
+        self.texture_selection.connect("activate",self.select_texture,"")
+        self.texture_select_but.connect("clicked",self.select_texture,"")
+        pack(self.tbox,[self.texture_selection, self.texture_select_but])
+        self.create_texture = gtk.Button(_("Create new texture from selection"))
+        self.create_texture.connect("clicked",self.material_from_selection,"")
+        self.selection_help_but = gtk.Button(_("Help on textures"))
+        self.selection_help_but.connect("clicked",self.selection_help,"")
+        self.materials = []
+        pack(self.tbox,[self.create_texture,
+                        gtk.Label("       "), self.selection_help_but])
+        pack(vbox,[self.tbox])
+        pack(vbox,[gtk.Label("")])
+        self.camera_style = gtk.combo_box_new_text()
+        for c in self.cameras:
+            self.camera_style.append_text(c)
+        self.camera_style.set_active(0)
+        self.camera_distance = gtk.SpinButton(gtk.Adjustment(50.0,-99.0,99.0,1.0), 0, 0)
+        self.camera_distance.set_digits(1)
+        pack(vbox,[gtk.Label(_("Camera type: ")),self.camera_style,
+                   gtk.Label(_("     Camera distance")),self.camera_distance])
+        self.single_frame = gtk.RadioButton(None,_("Render current frame"))
+        self.nimages = self.gui.images.nimages
+        self.iframe = self.gui.frame
+        self.movie = gtk.RadioButton(self.single_frame,
+                                     _("Render all %d frames") % self.nimages)
+        self.movie.connect("toggled",self.set_movie)
+        self.movie.set_sensitive(self.nimages > 1)
+        self.set_outputname()
+        pack(vbox,[self.single_frame,gtk.Label("   "),self.movie])
+        self.transparent = gtk.CheckButton(_("Transparent background"))
+        self.transparent.set_active(True)
+        pack(vbox,[self.transparent])
+        self.run_povray = gtk.CheckButton(_("Run povray       "))
+        self.run_povray.set_active(True)
+        self.run_povray.connect("toggled",self.toggle_run_povray,"")
+        self.keep_files = gtk.CheckButton(_("Keep povray files       "))
+        self.keep_files.set_active(False)
+        self.keep_files_status = True
+        self.window_open = gtk.CheckButton(_("Show output window"))
+        self.window_open.set_active(True)
+        self.window_open_status = True 
+        pack(vbox,[self.run_povray,self.keep_files,self.window_open])
+        pack(vbox,[gtk.Label("")])
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', lambda widget: self.destroy())
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', self.ok)
+        close_but = gtk.Button(stock=gtk.STOCK_CLOSE)
+        close_but.connect('clicked', lambda widget: self.destroy())
+        butbox = gtk.HButtonBox()
+        butbox.pack_start(cancel_but, 0, 0)
+        butbox.pack_start(ok_but, 0, 0)
+        butbox.pack_start(close_but, 0, 0)
+        butbox.show_all()
+        pack(vbox, [butbox], end=True, bottom=True)
+        self.add(vbox)
+        vbox.show()
+        self.show()
+
+    def change_width(self, *args):
+        self.height.set_value(self.width.get_value()*self.gui.height/float(self.gui.width))
+
+    def change_height(self, *args):
+        self.width.set_value(self.height.get_value()*self.gui.width/float(self.gui.height))
+
+    def toggle_render_lines(self, *args):
+        self.line_width.set_sensitive(self.render_cell.get_active()
+                                      or self.render_constraints.get_active())
+
+    def set_outputname(self, *args):
+        movie_index = ''
+        self.iframe = self.gui.frame
+        if self.movie.get_active():
+            while len(movie_index) + len(str(self.iframe)) <  len(str(self.nimages)):
+                movie_index += '0'
+            movie_index = '.' + movie_index + str(self.iframe)
+        name = self.basename.get_text() + movie_index + '.pov'
+        self.outputname.set_text(name)
+
+    def get_selection(self):
+        selection = np.zeros(self.natoms, bool)
+        text = self.texture_selection.get_text()
+        if len(self.texture_selection.get_text()) == 0:
+            text = 'False'
+        code = compile(text,'render.py', 'eval')
+        for n in range(self.natoms):
+            Z = self.gui.images.Z[n]
+            x, y, z = self.gui.images.P[self.iframe][n]
+            selection[n] = eval(code)
+        return selection
+
+    def select_texture(self,*args):
+        self.iframe = self.gui.frame
+        self.gui.images.selected = self.get_selection()
+        self.gui.set_frame(self.iframe)
+
+    def material_from_selection(self,*args):
+        box_selection = self.get_selection()
+        selection = self.gui.images.selected.copy()
+        if selection.any():
+            Z = []
+            for n in range(len(selection)):
+                if selection[n]:
+                    Z += [self.gui.images.Z[n]]
+            name = formula(Z)
+            if (box_selection == selection).all():
+                name += ': ' + self.texture_selection.get_text()
+            texture_button = gtk.combo_box_new_text()
+            for t in self.finish_list:
+                texture_button.append_text(t)
+            texture_button.set_active(1)
+            transparency = gtk.Adjustment(0,0.0,1.0,0.01)
+            transparency_spin = gtk.SpinButton(transparency, 0, 0)
+            transparency_spin.set_digits(2)
+            delete_button = gtk.Button(stock=gtk.STOCK_DELETE)
+            alignment = delete_button.get_children()[0]
+            index = len(self.materials)
+            delete_button.connect("clicked",self.delete_material,{"n":index})
+            self.materials += [[True,selection,texture_button,
+                                gtk.Label(_("  transparency: ")),transparency_spin,
+                                gtk.Label("   "),delete_button,gtk.Label()]]
+            self.materials[-1][-1].set_markup("    "+name)
+            pack(self.tbox,[self.materials[-1][2],self.materials[-1][3],self.materials[-1][4],
+                            self.materials[-1][5],self.materials[-1][6],self.materials[-1][7]])
+        else:
+            oops(_("Can not create new texture! Must have some atoms selected to create a new material!"))
+
+    def delete_material(self, button, index, *args):
+        n = index["n"]
+        self.materials[n][0] = False
+        self.materials[n][1] = np.zeros(self.natoms, bool)
+        self.materials[n][2].destroy()
+        self.materials[n][3].destroy()
+        self.materials[n][4].destroy()
+        self.materials[n][5].destroy()
+        self.materials[n][6].destroy()
+        self.materials[n][7].destroy()
+
+    def set_movie(self, *args):
+        if self.single_frame.get_active() and self.run_povray.get_active():
+            self.window_open.set_active(self.window_open_status)
+            self.window_open.set_sensitive(True)
+        else:
+            if self.run_povray.get_active():
+                self.window_open_status = self.window_open.get_active()
+            self.window_open.set_active(False)
+            self.window_open.set_sensitive(False)
+        self.set_outputname()        
+        
+    def toggle_run_povray(self, *args):
+        if self.run_povray.get_active():
+            self.keep_files.set_active(self.keep_files_status)
+            self.keep_files.set_sensitive(True)
+            if self.single_frame.get_active():
+                self.window_open.set_active(self.window_open_status)
+                self.window_open.set_sensitive(True)
+        else:
+            self.keep_files_status = self.keep_files.get_active()
+            self.keep_files.set_active(True)
+            self.keep_files.set_sensitive(False)
+            if self.single_frame.get_active():
+                self.window_open_status = self.window_open.get_active()
+            self.window_open.set_active(False)
+            self.window_open.set_sensitive(False)
+
+    def selection_help(self,*args):
+        Help(self.selection_info_txt)
+        
+    def set_textures(self):
+        textures =  self.natoms*[self.finish_list[self.default_texture.get_active()]]
+        for mat in self.materials:
+            sel = mat[1]
+            t = self.finish_list[mat[2].get_active()]
+            if mat[0]:
+                for n, val in enumerate(sel):
+                    if val:
+                        textures[n] = t
+        return textures
+
+    def get_colors(self):
+        colors = self.gui.get_colors(rgb = True)
+        for n in range(self.natoms):
+            colors[n] = list(colors[n]) + [0,self.default_transparency.value]
+        for mat in self.materials:
+            sel   = mat[1]
+            trans = mat[4].get_value()
+            for n, val in enumerate(sel):
+                if val:
+                    colors[n][4] = trans
+        return colors
+        
+    def ok(self, *args):
+        print "Rendering povray image(s): "
+        scale = self.gui.scale*self.height.get_value()/self.gui.height
+        bbox = np.empty(4)
+        size = np.array([self.width.get_value(), self.height.get_value()]) / scale
+        bbox[0:2] = np.dot(self.gui.center, self.gui.axes[:, :2]) - size / 2
+        bbox[2:] = bbox[:2] + size
+        povray_settings = {'run_povray'     : self.run_povray.get_active(),
+                           'bbox'           : bbox,
+                           'rotation'       : self.gui.axes, 
+                           'show_unit_cell' : self.render_cell.get_active(),
+                           'display'        : self.window_open.get_active(),
+                           'transparent'    : self.transparent.get_active(),
+                           'camera_type'    : self.cameras[self.camera_style.get_active()],
+                           'camera_dist'    : self.camera_distance.get_value(),
+                           'canvas_width'   : self.width.get_value(),
+                           'celllinewidth'  : self.line_width.get_value(),
+                           'textures'       : self.set_textures(),
+                           'exportconstraints' : self.render_constraints.get_active()}
+        if self.single_frame.get_active():
+            frames = [self.gui.frame]
+        else:
+            frames = range(self.nimages)
+        initial_frame = self.gui.frame
+        for frame in frames:
+            self.gui.set_frame(frame)
+            povray_settings['colors'] = self.get_colors()
+            atoms = self.gui.images.get_atoms(frame)
+            self.set_outputname()        
+            filename = self.outputname.get_text()
+            print " | Writing files for image", filename, "..."
+            write_pov(filename,
+                      atoms,
+                      radii = self.gui.images.r,
+                      **povray_settings)
+            if not self.keep_files.get_active():
+                print " | Deleting temporary file ", filename
+                system("rm "+filename)
+                filename = filename[:-4] + '.ini'
+                print " | Deleting temporary file ", filename
+                system("rm "+filename)
+        self.gui.set_frame(initial_frame)
+        self.set_outputname()        
diff --git a/ase/gui/repeat.py b/ase/gui/repeat.py
new file mode 100644
index 0000000..786ba97
--- /dev/null
+++ b/ase/gui/repeat.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+import gtk
+from math import sqrt
+from gettext import gettext as _
+
+import numpy as np
+
+from ase.gui.widgets import pack, Help
+
+
+class Repeat(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_title(_('Repeat'))
+        vbox = gtk.VBox()
+        pack(vbox, gtk.Label(_('Repeat atoms:')))
+        self.repeat = [gtk.Adjustment(r, 1, 9, 1) for r in gui.images.repeat]
+        pack(vbox, [gtk.SpinButton(r, 0, 0) for r in self.repeat])
+        for r in self.repeat:
+            r.connect('value-changed', self.change)
+        button = pack(vbox, gtk.Button(_('Set unit cell')))
+        button.connect('clicked', self.set_unit_cell)
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def change(self, adjustment):
+        self.gui.images.repeat_images([int(r.value) for r in self.repeat])
+        self.gui.repeat_colors([int(r.value) for r in self.repeat])
+        self.gui.set_coordinates()
+        return True
+        
+    def set_unit_cell(self, button):
+        self.gui.images.A *= self.gui.images.repeat.reshape((3, 1))
+        self.gui.images.E *= self.gui.images.repeat.prod()
+        self.gui.images.repeat = np.ones(3, int)
+        for r in self.repeat:
+            r.value = 1
+        self.gui.set_coordinates()
+        
diff --git a/ase/gui/rot_tools.py b/ase/gui/rot_tools.py
new file mode 100644
index 0000000..4dbdb95
--- /dev/null
+++ b/ase/gui/rot_tools.py
@@ -0,0 +1,48 @@
+# Gives the rotation matrix which rotates theta degrees about
+# vecU
+
+#  Generates the rotation matrix that rotate theta degrees about the vecU
+def rotate_about_vec(vecU, theta):
+    import numpy as np
+    vecU = np.array(vecU)
+    vecU = vecU / (sum(vecU ** 2) ** 0.5)
+    ux, uy, uz = vecU
+    st = np.sin(theta)
+    ct = np.cos(theta)
+    mat = np.array([[ux ** 2 + ct * (1 - ux ** 2), 
+                     ux * uy * (1 - ct) - uz * st, 
+                     uz * ux * (1 - ct) + uy * st],
+                    [ux * uy * (1 - ct) + uz * st, 
+                     uy ** 2 + ct * (1 - uy ** 2),
+                     uy * uz * (1 - ct) - ux * st],
+                    [uz * ux * (1 - ct) - uy * st,
+                     uy * uz * (1 - ct) + ux * st,
+                     uz ** 2 + ct * (1 - uz **2)]])
+    return (mat)
+
+# Generates the rotation matrix which rotates aVec into intoVec
+def rotate_vec_into_newvec(aVec, intoVec):
+    def length(v):
+        return((sum(v ** 2)) ** 0.5)
+
+    import numpy as np
+    from math import acos
+    fac = 1.0
+    aVec = np.array(aVec)
+    intoVec = np.array(intoVec)
+    nor = np.cross(aVec, intoVec)
+    if length(nor) == 0:
+        nor = np.array([1, 0, 0])
+    nor = nor / length(nor)
+    theta = acos(np.dot(aVec, intoVec) / (length(aVec) * length(intoVec)))
+    if np.dot(aVec, intoVec) < 0:
+        theta = theta + np.pi
+        fac = -1
+    return(fac * rotate_about_vec(nor, theta))
+
+# Applies the rotation matrix to the vector and returns the rotated vector
+def rotate_vec (rot_mat, vec):
+    import numpy as np
+    rot_vec = np.dot(rot_mat, vec)
+
+    return (rot_vec)
diff --git a/ase/gui/rotate.py b/ase/gui/rotate.py
new file mode 100644
index 0000000..168ae59
--- /dev/null
+++ b/ase/gui/rotate.py
@@ -0,0 +1,47 @@
+import numpy as np
+import gtk
+from gettext import gettext as _
+
+from ase.gui.widgets import pack
+from ase.utils import rotate, irotate
+
+
+class Rotate(gtk.Window):
+    update = True
+    
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        angles = irotate(gui.axes)
+        self.set_title(_('Rotate'))
+        vbox = gtk.VBox()
+        pack(vbox, gtk.Label(_('Rotation angles:')))
+        self.rotate = [gtk.Adjustment(value=a, lower=-360, upper=360,
+                                      step_incr=1, page_incr=10)
+                       for a in angles]
+        pack(vbox, [gtk.SpinButton(a, climb_rate=0, digits=1)
+                    for a in self.rotate])
+        for r in self.rotate:
+            r.connect('value-changed', self.change)
+        button = pack(vbox, gtk.Button(_('Update')))
+        button.connect('clicked', self.update_angles)
+        pack(vbox, gtk.Label(_('Note:\nYou can rotate freely\n'
+                               'with the mouse, by holding\n'
+                               'down mouse button 2.')))
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+    def change(self, adjustment):
+        if self.update:
+            x, y, z = [float(a.value) for a in self.rotate]
+            self.gui.axes = rotate('%fx,%fy,%fz' % (x, y, z))
+            self.gui.set_coordinates()
+        return True
+        
+    def update_angles(self, button):
+        angles = irotate(self.gui.axes)
+        self.update = False
+        for r, a in zip(self.rotate, angles):
+            r.value = a
+        self.update = True
diff --git a/ase/gui/scaling.py b/ase/gui/scaling.py
new file mode 100644
index 0000000..3bbdd89
--- /dev/null
+++ b/ase/gui/scaling.py
@@ -0,0 +1,460 @@
+# encoding: utf-8
+
+"Module for homogeneous deformation and calculations of elastic constants."
+
+import gtk
+from gettext import gettext as _
+from ase.gui.simulation import Simulation
+from ase.gui.minimize import MinimizeMixin
+from ase.gui.energyforces import OutputFieldMixin
+from ase.gui.widgets import oops, pack, AseGuiCancelException
+import ase
+import numpy as np
+
+scaling_txt = """\
+This module is intended for calculating elastic constants by homogeneously
+deforming a system."""
+
+help_txt = """
+The homogeneous scaling module changes the size of a system by stretching it
+along on or more directions.  Small amounts of deformation can be used to
+calculate elastic constants, large amounts to simulate plastic deformation.
+
+You will have to choose along which axis/axes the deformation is done.  Usually,
+it only makes sense to deform along axes with periodic boundary conditions.  The
+<b>amount of deformation</b> is set in the Deformation frame.  A scale factor of
+e.g. 0.01 means that the system is changed incrementally from being 1% smaller
+than the initial configuration to 1% larger.  The offset alters this so it is
+not symmetric around 0% deformation.  A check-box can disable the negative
+deformation (compression).
+
+<b>'Atomic relaxations'</b> means that the individual atoms are allowed to move
+relative to the unit cell.  This is done by performing an energy minimization
+for each configuration.  You will have to choose the algorithm and minimization
+parameters.
+
+During the deformation, a number of steps is taken, with different system sizes.
+You can choose to load all configurations into the main window as a movie, to
+only load the configuration with the lowest energy, or to keep the original
+configuration loaded.  <b>Important:</b> If you repeat the calculation by
+pressing [Run] a second time, the starting configuration will have changed
+unless you keep the original configuration.
+"""
+
+class HomogeneousDeformation(Simulation, MinimizeMixin, OutputFieldMixin):
+    "Window for homogeneous deformation and elastic constants."
+    use_scrollbar = True
+    def __init__(self, gui):
+        Simulation.__init__(self, gui)
+        self.set_title(_("Homogeneous scaling"))
+        self.scaling_is_ready = False
+        vbox = gtk.VBox()
+        self.packtext(vbox, scaling_txt)
+        self.packimageselection(vbox, txt1="", txt2="")
+        self.start_radio_nth.set_active(True)
+        pack(vbox, gtk.Label(""))
+
+        # Radio buttons for choosing deformation mode.
+        tbl = gtk.Table(4,3)
+        for i, l in enumerate([_('3D deformation   '), 
+                               _('2D deformation   '), 
+                               _('1D deformation   ')]):
+            lbl = gtk.Label(l)
+            tbl.attach(lbl, i, i+1, 0, 1)
+        self.radio_bulk = gtk.RadioButton(None, _("Bulk"))
+        tbl.attach(self.radio_bulk, 0, 1, 1, 2)
+        self.radio_xy = gtk.RadioButton(self.radio_bulk, _("xy-plane"))
+        tbl.attach(self.radio_xy, 1, 2, 1, 2)
+        self.radio_xz = gtk.RadioButton(self.radio_bulk, _("xz-plane"))
+        tbl.attach(self.radio_xz, 1, 2, 2, 3)
+        self.radio_yz = gtk.RadioButton(self.radio_bulk, _("yz-plane"))
+        tbl.attach(self.radio_yz, 1, 2, 3, 4)
+        self.radio_x = gtk.RadioButton(self.radio_bulk, _("x-axis"))
+        tbl.attach(self.radio_x, 2, 3, 1, 2)
+        self.radio_y = gtk.RadioButton(self.radio_bulk, _("y-axis"))
+        tbl.attach(self.radio_y, 2, 3, 2, 3)
+        self.radio_z = gtk.RadioButton(self.radio_bulk, _("z-axis"))
+        tbl.attach(self.radio_z, 2, 3, 3, 4)
+        tbl.show_all()
+        pack(vbox, [tbl])
+        self.deformtable = [
+            (self.radio_bulk, (1,1,1)),
+            (self.radio_xy, (1,1,0)),
+            (self.radio_xz, (1,0,1)),
+            (self.radio_yz, (0,1,1)),
+            (self.radio_x, (1,0,0)),
+            (self.radio_y, (0,1,0)),
+            (self.radio_z, (0,0,1))]
+        self.allow_non_pbc = gtk.CheckButton(
+            _("Allow deformation along non-periodic directions."))
+        pack(vbox, [self.allow_non_pbc])
+        self.allow_non_pbc.connect('toggled', self.choose_possible_deformations)
+
+        # Parameters for the deformation
+        framedef = gtk.Frame(_("Deformation:"))
+        vbox2 = gtk.VBox()
+        vbox2.show()
+        framedef.add(vbox2)
+        self.max_scale = gtk.Adjustment(0.010, 0.001, 10.0, 0.001)
+        max_scale_spin = gtk.SpinButton(self.max_scale, 10.0, 3)
+        pack(vbox2, [gtk.Label(_("Maximal scale factor: ")), max_scale_spin])
+        self.scale_offset = gtk.Adjustment(0.0, -10.0, 10.0, 0.001)
+        self.scale_offset_spin = gtk.SpinButton(self.scale_offset, 10.0, 3)
+        pack(vbox2, [gtk.Label(_("Scale offset: ")), self.scale_offset_spin])
+        self.nsteps = gtk.Adjustment(5, 3, 1000, 1)
+        nsteps_spin = gtk.SpinButton(self.nsteps, 1, 0)
+        pack(vbox2, [gtk.Label(_("Number of steps: ")), nsteps_spin])
+        self.pull = gtk.CheckButton(_("Only positive deformation"))
+        pack(vbox2, [self.pull])
+        self.pull.connect('toggled', self.pull_toggled)
+        
+        # Atomic relaxations
+        framerel = gtk.Frame(_("Atomic relaxations:"))
+        vbox2 = gtk.VBox()
+        vbox2.show()
+        framerel.add(vbox2)
+        self.radio_relax_on = gtk.RadioButton(None, _("On   "))
+        self.radio_relax_off = gtk.RadioButton(self.radio_relax_on, _("Off"))
+        self.radio_relax_off.set_active(True)
+        pack(vbox2, [self.radio_relax_on, self.radio_relax_off])
+        self.make_minimize_gui(vbox2)
+        for r in (self.radio_relax_on, self.radio_relax_off):
+            r.connect("toggled", self.relax_toggled)
+        self.relax_toggled()
+        pack(vbox, [framedef, gtk.Label(" "), framerel])
+        pack(vbox, gtk.Label(""))
+        
+        # Results
+        pack(vbox, [gtk.Label(_("Results:"))])
+        self.radio_results_keep = gtk.RadioButton(
+            None, _("Keep original configuration"))
+        self.radio_results_optimal = gtk.RadioButton(
+            self.radio_results_keep, _("Load optimal configuration"))
+        self.radio_results_all =  gtk.RadioButton(
+            self.radio_results_optimal, _("Load all configurations"))
+        self.radio_results_keep.set_active(True)
+        pack(vbox, [self.radio_results_keep])
+        pack(vbox, [self.radio_results_optimal])
+        pack(vbox, [self.radio_results_all])
+
+        # Output field
+        #label = gtk.Label("Strain\t\tEnergy [eV]\n")
+        outframe = self.makeoutputfield(None, 
+                                        heading=_("Strain\t\tEnergy [eV]"))
+        fitframe = gtk.Frame(_("Fit:"))
+        vbox2 = gtk.VBox()
+        vbox2.show()
+        fitframe.add(vbox2)
+        self.radio_fit_2 = gtk.RadioButton(None, _("2nd"))
+        self.radio_fit_3 = gtk.RadioButton(self.radio_fit_2, _("3rd"))
+        self.radio_fit_2.connect("toggled", self.change_fit)
+        self.radio_fit_3.connect("toggled", self.change_fit)
+        self.radio_fit_3.set_active(True)
+        pack(vbox2, [gtk.Label(_("Order of fit: ")), self.radio_fit_2,
+                     self.radio_fit_3])
+        pack(vbox2, [gtk.Label("")])
+        scrwin = gtk.ScrolledWindow()
+        scrwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.fit_output = gtk.TextBuffer()
+        txtview = gtk.TextView(self.fit_output)
+        txtview.set_editable(False)
+        scrwin.add(txtview)
+        scrwin.show_all()
+        self.fit_win = scrwin
+        vbox2.pack_start(scrwin, True, True, 0)
+        hbox = gtk.HBox(homogeneous=True)
+        for w in [outframe, fitframe]:
+            hbox.pack_start(w)
+            w.show()
+        pack(vbox, hbox)    
+        pack(vbox, gtk.Label(""))
+
+        # Status field
+        self.status_label = gtk.Label("")
+        pack(vbox, [self.status_label])
+
+        # Activate the right deformation buttons
+        self.choose_possible_deformations(first=True)
+
+        # Run buttons etc.
+        self.makebutbox(vbox, helptext=help_txt)
+        vbox.show()
+        if self.use_scrollbar:
+            self.scrwin = gtk.ScrolledWindow() 
+            self.scrwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 
+            self.scrwin.add_with_viewport(vbox) 
+            self.scrwin.show() 
+            self.add(self.scrwin) 
+            self.scaling_is_ready = True
+            self.set_reasonable_size() 
+        else:
+            self.add(vbox)
+        self.show()
+        self.gui.register_vulnerable(self)
+        
+    def set_reasonable_size(self, resize=False): 
+        if not self.use_scrollbar or not self.scaling_is_ready:
+            return
+        x, y = self.scrwin.get_children()[0].size_request() 
+        x += self.scrwin.get_vscrollbar().size_request()[0] + 5
+        y += self.scrwin.get_hscrollbar().size_request()[1] + 5
+        print x,y 
+        if resize: 
+            xdef, ydef = self.get_default_size() 
+            xnow, ynow = self.get_size() 
+            if xdef == xnow and ydef == ynow: 
+                # The user has not changed the size.  Resize should be OK 
+                self.resize(x,y) 
+                self.set_default_size(x,y) 
+        else: 
+            self.set_default_size(x,y) 
+                  
+    def min_algo_specific(self, *args):
+        MinimizeMixin.min_algo_specific(self, *args)
+        self.set_reasonable_size(resize=True)
+                  
+    def choose_possible_deformations(self, widget=None, first=False):
+        """Turn on sensible radio buttons.
+
+        Only radio buttons corresponding to deformations in directions
+        with periodic boundary conditions should be turned on.
+        """
+        if self.setup_atoms():
+            pbc = self.atoms.get_pbc()
+        else:
+            pbc = np.array([False, False, False], bool)
+        if (pbc == [True, True, True]).all():
+            self.allow_non_pbc.set_active(False)
+            self.allow_non_pbc.set_sensitive(False)
+        else:
+            self.allow_non_pbc.set_sensitive(True)
+        if self.allow_non_pbc.get_active():
+            pbc = [True, True, True]  #All is allowed
+            self.radio_relax_off.set_active(True)
+            self.radio_relax_on.set_sensitive(False)
+        else:
+            self.radio_relax_on.set_sensitive(True)
+        for radio, requirement in self.deformtable:
+            ok = True
+            for i in range(3):
+                if requirement[i] and not pbc[i]:
+                    ok = False
+            radio.set_sensitive(ok)
+            if first and ok:
+                # The first acceptable choice, choose it to prevent
+                # inconsistent state.
+                radio.set_active(True)
+                first = False
+
+    def relax_toggled(self, *args):
+        "Turn minimization widgets on or off."
+        state = self.radio_relax_on.get_active()
+        for widget in (self.algo, self.fmax_spin, self.steps_spin):
+            widget.set_sensitive(state)
+
+    def pull_toggled(self, *args):
+        "When positive def. only, the scale offset is turned off."
+        self.scale_offset_spin.set_sensitive(not self.pull.get_active())
+        
+    def notify_atoms_changed(self):
+        "When atoms have changed, check for the number of images."
+        self.setupimageselection()
+        self.choose_possible_deformations()
+        self.set_reasonable_size(resize=True)
+
+    def get_deformation_axes(self):
+        """Return which axes the user wants to deform along."""
+        for but, deform in self.deformtable:
+            if but.get_active():
+                return np.array(deform)
+        # No deformation chosen!
+        oops("No deformation chosen: Please choose a deformation mode.")
+        return False
+            
+    def run(self, *args):
+        """Make the deformation."""
+        self.output.set_text("")
+        if not self.setup_atoms():
+            return
+        deform_axes = self.get_deformation_axes()
+        if deform_axes is False:
+            return  #Nothing to do!
+
+        # Prepare progress bar
+        if self.radio_relax_on.get_active():
+            fmax = self.fmax.value
+            mininame = self.minimizers[self.algo.get_active()]
+            self.begin(mode="scale/min", algo=mininame, fmax=fmax,
+                       steps=self.steps.value, scalesteps=self.nsteps.value)
+        else:
+            self.begin(mode="scale", scalesteps=self.nsteps.value)
+        try:
+            logger_func = self.gui.simulation['progress'].get_logger_stream
+        except (KeyError, AttributeError):
+            logger = None
+        else:
+            logger = logger_func()  # Don't catch errors in the function.
+
+        # Display status message
+        self.status_label.set_text(_("Running ..."))
+        self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                    gtk.gdk.color_parse('#AA0000'))
+        while gtk.events_pending():
+            gtk.main_iteration()
+
+        # Do the scaling
+        scale = self.max_scale.value
+        if self.pull.get_active():
+            steps = np.linspace(0, scale, self.nsteps.value)
+        else:
+            steps = np.linspace(-scale, scale, self.nsteps.value)
+            steps += self.scale_offset.value
+        undef_cell = self.atoms.get_cell()
+        results = []
+        #txt = "Strain\t\tEnergy [eV]\n"
+        txt = ""
+        # If we load all configurations, prepare it.
+        if self.radio_results_all.get_active():
+            self.prepare_store_atoms()
+
+        stored_atoms = False
+        try:
+            # Now, do the deformation
+            for i, d in enumerate(steps):
+                deformation = np.diag(1.0 + d * deform_axes)
+                self.atoms.set_cell(np.dot(undef_cell, deformation),
+                                    scale_atoms=True)
+                if self.gui.simulation.has_key('progress'):
+                    self.gui.simulation['progress'].set_scale_progress(i)
+                if self.radio_relax_on.get_active():
+                    algo = getattr(ase.optimize, mininame)
+                    if mininame == "MDMin":
+                        minimizer = algo(self.atoms, logfile=logger,
+                                         dt=self.mdmin_dt.value)
+                    else:
+                        minimizer = algo(self.atoms, logfile=logger)
+                    minimizer.run(fmax=fmax, steps=self.steps.value)
+                e = self.atoms.get_potential_energy()
+                results.append((d, e))
+                txt = txt + ("%.5f\t\t%.5f\n" % (d, e))
+                self.output.set_text(txt)
+                if self.radio_results_all.get_active():
+                    self.store_atoms()
+                    stored_atoms = True
+        except AseGuiCancelException:
+            # Update display to reflect cancellation of simulation.
+            self.status_label.set_text(_("Calculation CANCELLED."))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#AA4000'))
+        except MemoryError:
+            self.status_label.set_text(_("Out of memory, consider using "
+                                         "LBFGS instead"))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#AA4000'))
+            
+        else:
+            # Update display to reflect succesful end of simulation.
+            self.status_label.set_text(_("Calculation completed."))
+            self.status_label.modify_fg(gtk.STATE_NORMAL,
+                                        gtk.gdk.color_parse('#007700'))
+                     
+        if results:
+            self.do_fit(np.array(results))
+            if self.radio_results_optimal.get_active():
+                if self.minimum_ok:
+                    deformation = np.diag(1.0 + self.x0 * deform_axes)
+                    self.atoms.set_cell(np.dot(undef_cell, deformation),
+                                        scale_atoms=True)
+                    if self.radio_relax_on.get_active():
+                        if self.gui.simulation.has_key('progress'):
+                            self.gui.simulation['progress'].set_scale_progress(
+                                len(steps))
+                        algo = getattr(ase.optimize, mininame)
+                        minimizer = algo(self.atoms, logfile=logger)
+                        minimizer.run(fmax=fmax, steps=self.steps.value)
+                    # Store the optimal configuration.
+                    self.prepare_store_atoms()
+                    self.store_atoms()
+                    stored_atoms = True
+                else:
+                    oops(_("No trustworthy minimum: Old configuration kept."))
+            self.activate_output()
+            if stored_atoms:
+                self.gui.notify_vulnerable()
+        self.end()    
+            
+        # If we store all configurations: Open movie window and energy graph
+        if stored_atoms and self.gui.images.nimages > 1:
+            self.gui.movie()
+            assert not np.isnan(self.gui.images.E[0])
+            if not self.gui.plot_graphs_newatoms():
+                expr = 'i, e - E[-1]'            
+                self.gui.plot_graphs(expr=expr)
+            # Continuations should use the best image
+            nbest = np.argmin(np.array(results)[:,1])
+            self.start_nth_adj.value = nbest
+            
+
+    def change_fit(self, widget):
+        "Repeat the fitting if the order is changed."
+        # It may be called both for the button being turned on and the
+        # one being turned off.  But we only want to call do_fit once.
+        # And only if there are already cached results (ie. if the
+        # order is changed AFTER the calculation is done).
+        if widget.get_active() and getattr(self, "results", None) is not None:
+            self.do_fit(None)
+                
+    def do_fit(self, results):
+        "Fit the results to a polynomial"
+        if results is None:
+            results = self.results  # Use cached results
+        else:
+            self.results = results  # Keep for next time
+        self.minimum_ok = False
+        if self.radio_fit_3.get_active():
+            order = 3
+        else:
+            order = 2
+            
+        if len(results) < 3:
+            txt = (_("Insufficent data for a fit\n(only %i data points)\n")
+                   % (len(results),) )
+            order = 0
+        elif len(results) == 3 and order == 3:
+            txt = _("REVERTING TO 2ND ORDER FIT\n(only 3 data points)\n\n")
+            order = 2
+        else:
+            txt = ""
+
+        if order > 0:
+            fit0 = np.poly1d(np.polyfit(results[:,0], results[:,1], order))
+            fit1 = np.polyder(fit0, 1)
+            fit2 = np.polyder(fit1, 1)
+
+            x0 = None
+            for t in np.roots(fit1):
+                if fit2(t) > 0:
+                    x0 = t
+                    break
+            if x0 is None:
+                txt = txt + _("No minimum found!")
+            else:
+                e0 = fit0(x0)
+                e2 = fit2(x0)
+                txt += "E = "
+                if order == 3:
+                    txt += "A(x - x0)³ + "
+                txt += "B(x - x0)² + C\n\n"
+                txt += "B = %.5g eV\n" % (e2,)
+                txt += "C = %.5g eV\n" % (e0,)
+                txt += "x0 = %.5g\n" % (x0,)
+                lowest = self.scale_offset.value - self.max_scale.value
+                highest = self.scale_offset.value + self.max_scale.value
+                if x0 < lowest or x0 > highest:
+                    txt += _("\nWARNING: Minimum is outside interval\n")
+                    txt += _("It is UNRELIABLE!\n")
+                else:
+                    self.minimum_ok = True
+                    self.x0 = x0
+        self.fit_output.set_text(txt)
+        
diff --git a/ase/gui/settings.py b/ase/gui/settings.py
new file mode 100644
index 0000000..c170fb9
--- /dev/null
+++ b/ase/gui/settings.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+import gtk
+from ase.gui.widgets import pack
+from gettext import gettext as _
+
+class Settings(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.set_title('Settings')
+        self.gui = gui
+        vbox = gtk.VBox()
+
+        # Constraints
+        a = pack(vbox, gtk.Label())
+        a.set_markup('<span size="larger" underline="single">'
+                     '%s</span>' % _('Constraints:'))
+        a, b = pack(vbox, [gtk.Button(_('Constrain')),
+                           gtk.Label('/'),
+                           gtk.Button(_('release')),                        
+                           gtk.Label(_(' selected atoms'))])[::2]
+        a.connect('clicked', self.constrain_selected)
+        b.connect('clicked', self.release_selected)
+        a = pack(vbox, gtk.Button(_('Constrain immobile atoms')))
+        a.connect('clicked', self.immobile)
+        a = pack(vbox, gtk.Button(_('Clear all constraints')))
+        a.connect('clicked', self.clear_constraints)
+
+        # Visibility
+        a = pack(vbox, gtk.Label())
+        a.set_markup('\n<span size="larger" underline="single">'
+                     '%s</span>' % _('Visibility:'))
+        a, b = pack(vbox, [gtk.Button(_('Hide')),
+                           gtk.Label('/'),
+                           gtk.Button(_('show')),
+                           gtk.Label(_(' selected atoms'))])[::2]
+        a.connect('clicked', self.hide_selected)
+        b.connect('clicked', self.show_selected)
+        a = pack(vbox, gtk.Button(_('View all atoms')))
+        a.connect('clicked', self.view_all)
+
+        # Miscellaneous
+        a = pack(vbox, gtk.Label())
+        a.set_markup('\n<span size="larger" underline="single">'
+                     '%s</span>' % _('Miscellaneous:'))
+        self.scale = gtk.Adjustment(value=.89, lower=0.2, upper=2.0,
+                                    step_incr=0.1, page_incr=0.5)
+        pack(vbox, [gtk.Label(_('Scale atomic radii:')),
+                    gtk.SpinButton(self.scale, climb_rate=0, digits=2)])
+        self.scale.connect('value-changed', self.scale_radii)
+
+        # A close button
+        pack(vbox, gtk.Label(_('\n')))
+        close = pack(vbox, gtk.Button(_('Close')))
+        close.connect('clicked', lambda widget: self.destroy())
+
+        # Add elements and show frame
+        self.add(vbox)
+        vbox.show()
+        self.show()
+
+    def scale_radii(self, adjustment):
+        self.gui.images.set_radii(float(self.scale.value))
+        self.gui.draw()
+        return True
+
+    def hide_selected(self, button):
+        self.gui.images.visible[self.gui.images.selected] = False
+        self.gui.draw()
+
+    def show_selected(self, button):
+        self.gui.images.visible[self.gui.images.selected] = True
+        self.gui.draw()
+
+    def view_all(self, button):
+        self.gui.images.visible[:] = True
+        self.gui.draw()
+
+    def constrain_selected(self, button):
+        self.gui.images.dynamic[self.gui.images.selected] = False
+        self.gui.draw()
+
+    def release_selected(self, button):
+        self.gui.images.dynamic[self.gui.images.selected] = True
+        self.gui.draw()
+
+    def immobile(self, button):
+        self.gui.images.set_dynamic()
+        self.gui.draw()
+
+    def clear_constraints(self, button):
+        self.gui.images.dynamic[:] = True
+        self.gui.draw()
diff --git a/ase/gui/setupwindow.py b/ase/gui/setupwindow.py
new file mode 100644
index 0000000..cad85ce
--- /dev/null
+++ b/ase/gui/setupwindow.py
@@ -0,0 +1,65 @@
+# encoding: utf-8
+"""setupwindow.py - Window base class for setup modules.
+"""
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops
+import ase
+
+class SetupWindow(gtk.Window):
+    "Base class for ase.gui setup windows."
+    # __init__ inherited from gtk.Window
+
+    def packtext(self, vbox, text, label=None):
+        "Pack an text frame into the window."
+        pack(vbox, gtk.Label(""))
+        txtframe = gtk.Frame(label)
+        txtlbl = gtk.Label(text)
+        txtframe.add(txtlbl)
+        txtlbl.show()
+        pack(vbox, txtframe)
+        pack(vbox, gtk.Label(""))
+
+    def update_element(self, *args):
+        "Called when a new element may have been entered."
+        # Assumes the element widget is self.element and that a label
+        # to keep updated is self.elementinfo.  The chemical symbol is
+        # placed in self.legalelement - or None if the element is
+        # invalid.
+        elem = self.element.get_text()
+        if not elem:
+            self.invalid_element(_("  No element specified!"))
+            return False
+        try:
+            z = int(elem)
+        except ValueError:
+            # Probably a symbol
+            try:
+                z = ase.data.atomic_numbers[elem]
+            except KeyError:
+                self.invalid_element()
+                return False
+        try:
+            symb = ase.data.chemical_symbols[z]
+        except KeyError:
+            self.invalid_element()
+            return False
+        name = ase.data.atomic_names[z]
+        ref = ase.data.reference_states[z]
+        if ref is None:
+            struct = _("No crystal structure data")
+        else:
+            struct = ref['symmetry']
+            if struct == 'fcc' or struct == 'bcc':
+                struct = "%s (a=%.3f Å)" % (struct, ref['a'])
+        
+        txt = "  %s: %s, Z=%i, %s" % (name, symb, z, struct)
+        self.elementinfo.set_text(txt)
+        self.legal_element = symb
+        return True
+        
+    def invalid_element(self, txt=_("  ERROR: Invalid element!")):
+        self.legal_element = False
+        self.elementinfo.set_text(txt)
+
diff --git a/ase/gui/simulation.py b/ase/gui/simulation.py
new file mode 100644
index 0000000..7408877
--- /dev/null
+++ b/ase/gui/simulation.py
@@ -0,0 +1,146 @@
+"Base class for simulation windows"
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import oops, pack, help
+from ase import Atoms
+from ase.constraints import FixAtoms
+
+class Simulation(gtk.Window):
+    def __init__(self, gui):
+        gtk.Window.__init__(self)
+        self.gui = gui
+
+    def packtext(self, vbox, text, label=None):
+        "Pack an text frame into the window."
+        pack(vbox, gtk.Label(""))
+        txtframe = gtk.Frame(label)
+        txtlbl = gtk.Label(text)
+        txtframe.add(txtlbl)
+        txtlbl.show()
+        pack(vbox, txtframe)
+        pack(vbox, gtk.Label(""))
+
+    def packimageselection(self, outerbox, txt1=_(" (rerun simulation)"),
+                           txt2=_(" (continue simulation)")):
+        "Make the frame for selecting starting config if more than one."
+        self.startframe = gtk.Frame(_("Select starting configuration:"))
+        pack(outerbox, [self.startframe])
+        vbox = gtk.VBox()
+        self.startframe.add(vbox)
+        vbox.show()
+        self.numconfig_format = _("There are currently %i "
+                                  "configurations loaded.")
+        self.numconfig_label = gtk.Label("")
+        pack(vbox, [self.numconfig_label])
+        lbl = gtk.Label(_("Choose which one to use as the "
+                          "initial configuration"))
+        pack(vbox, [lbl])
+        self.start_radio_first = gtk.RadioButton(
+            None, _("The first configuration %s.") % txt1)
+        pack(vbox, [self.start_radio_first])
+        self.start_radio_nth = gtk.RadioButton(self.start_radio_first,
+                                               _("Configuration number "))
+        self.start_nth_adj = gtk.Adjustment(0, 0, 1, 1)
+        self.start_nth_spin = gtk.SpinButton(self.start_nth_adj, 0, 0)
+        self.start_nth_spin.set_sensitive(False)
+        pack(vbox, [self.start_radio_nth, self.start_nth_spin])
+        self.start_radio_last = gtk.RadioButton(self.start_radio_first,
+            _("The last configuration %s.") % txt2)
+        self.start_radio_last.set_active(True)
+        pack(vbox, self.start_radio_last)
+        self.start_radio_nth.connect("toggled", self.start_radio_nth_toggled)
+        self.setupimageselection()
+        
+    def start_radio_nth_toggled(self, widget):
+        self.start_nth_spin.set_sensitive(self.start_radio_nth.get_active())
+
+    def setupimageselection(self):
+        "Decide if the start image selection frame should be shown."
+        n = self.gui.images.nimages
+        if n <= 1:
+            self.startframe.hide()
+        else:
+            self.startframe.show()
+            if self.start_nth_adj.value >= n:
+                self.start_nth_adj.value = n-1
+            self.start_nth_adj.upper = n-1
+            self.numconfig_label.set_text(self.numconfig_format % (n,))
+
+    def getimagenumber(self):
+        "Get the image number selected in the start image frame."
+        nmax = self.gui.images.nimages
+        if nmax <= 1:
+            return 0
+        elif self.start_radio_first.get_active():
+            return 0
+        elif self.start_radio_nth.get_active():
+            return self.start_nth_adj.value
+        else:
+            assert self.start_radio_last.get_active()
+            return nmax-1
+
+    def makebutbox(self, vbox, helptext=None):
+        self.buttons = gtk.HButtonBox()
+        runbut = gtk.Button(_("Run"))
+        runbut.connect('clicked', self.run)
+        closebut = gtk.Button(stock=gtk.STOCK_CLOSE)
+        closebut.connect('clicked', lambda x: self.destroy())
+        for w in (runbut, closebut):
+            self.buttons.pack_start(w, 0, 0)
+            w.show()
+        if helptext:
+            helpbut = [help(helptext)]
+        else:
+            helpbut = []
+        pack(vbox, helpbut + [self.buttons], end=True, bottom=True)
+
+    def setup_atoms(self):
+        self.atoms = self.get_atoms()
+        if self.atoms is None:
+            return False
+        try:
+            self.calculator = self.gui.simulation['calc']
+        except KeyError:
+            oops(_("No calculator: Use Calculate/Set Calculator on the menu."))
+            return False
+        self.atoms.set_calculator(self.calculator())
+        return True
+    
+    def get_atoms(self):
+        "Make an atoms object from the active image"
+        images = self.gui.images
+        if images.natoms < 1:
+            oops(_("No atoms present"))
+            return None
+        n = self.getimagenumber()
+        natoms = len(images.P[n]) / images.repeat.prod()
+        constraint = None
+        if not images.dynamic.all():
+            constraint = FixAtoms(mask=1-images.dynamic)
+        return Atoms(positions=images.P[n,:natoms],
+                     symbols=images.Z[:natoms],
+                     cell=images.A[n],
+                     magmoms=images.M[n,:natoms],
+                     tags=images.T[n,:natoms],
+                     pbc=images.pbc,
+                     constraint=constraint)
+
+    def begin(self, **kwargs):
+        if self.gui.simulation.has_key('progress'):
+            self.gui.simulation['progress'].begin(**kwargs)
+
+    def end(self):
+        if self.gui.simulation.has_key('progress'):
+            self.gui.simulation['progress'].end()
+
+    def prepare_store_atoms(self):
+        "Informs the gui that the next configuration should be the first."
+        self.gui.prepare_new_atoms()
+        self.count_steps = 0
+        
+    def store_atoms(self):
+        "Observes the minimization and stores the atoms in the gui."
+        self.gui.append_atoms(self.atoms)
+        self.count_steps += 1
+
diff --git a/ase/gui/status.py b/ase/gui/status.py
new file mode 100644
index 0000000..1a2aee9
--- /dev/null
+++ b/ase/gui/status.py
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+
+from math import sqrt, pi, acos
+
+import gtk
+import numpy as np
+
+from ase.data import chemical_symbols as symbols
+from ase.data import atomic_names as names
+from ase.gui.widgets import pack
+from gettext import gettext as _
+
+def formula(Z):
+    hist = {}
+    for z in Z:
+        if z in hist:
+            hist[z] += 1
+        else:
+            hist[z] = 1
+    text = ''
+    Z = hist.keys()
+    Z.sort()
+    for z in Z:
+        text += symbols[z]
+        n = hist[z]
+        if n > 1:
+            text += '<sub>%d</sub>' % n
+    return text
+
+class Status:
+    def __init__(self, vbox):
+        self.eventbox = gtk.EventBox()
+        self.label = gtk.Label()
+        self.eventbox.add(self.label)
+        self.label.show()
+        if gtk.pygtk_version < (2, 12):
+            self.set_tip(self.eventbox, _('Tip for status box ...'))
+        else:
+            self.eventbox.set_tooltip_text(_('Tip for status box ...'))
+        pack(vbox, self.eventbox)
+        self.ordered_indices = []
+
+    def status(self):
+        # use where here:  XXX
+        indices = np.arange(self.images.natoms)[self.images.selected]
+        ordered_indices = self.images.selected_ordered
+        n = len(indices)
+        self.nselected = n
+        
+        if n == 0:
+            self.label.set_text('')
+            return
+
+        Z = self.images.Z[indices]
+        R = self.R[indices]
+
+        if n == 1:
+            tag = self.images.T[self.frame,indices][0]
+            mom = self.images.M[self.frame][indices]
+            text = (u' #%d %s (%s): %.3f Å, %.3f Å, %.3f Å ' %
+                    ((indices[0], names[Z[0]], symbols[Z[0]]) + tuple(R[0])))
+            # TRANSLATORS: mom refers to magnetic moment
+            text += _(' tag=%(tag)s mom=%(mom)1.2f') % dict(tag=tag, mom=mom)
+        elif n == 2:
+            D = R[0] - R[1]
+            d = sqrt(np.dot(D, D))
+            text = u' %s-%s: %.3f Å' % (symbols[Z[0]], symbols[Z[1]], d)
+        elif n == 3:
+            d = []
+            for c in range(3):
+                D = R[c] - R[(c + 1) % 3]
+                d.append(np.dot(D, D))
+            a = []
+            for c in range(3):
+                t1 = 0.5 * (d[c] + d[(c + 1) % 3] - d[(c + 2) % 3])
+                t2 = sqrt(d[c] * d[(c + 1) % 3])
+                try:
+                    t3 = acos(t1 / t2)
+                except ValueError:
+                    if t1 > 0:
+                        t3 = 0
+                    else:
+                        t3 = pi
+                a.append(t3 * 180 / pi)
+            text = (u' %s-%s-%s: %.1f°, %.1f°, %.1f°' %
+                    tuple([symbols[z] for z in Z] + a))
+        elif len(ordered_indices) == 4:
+            R = self.R[ordered_indices]
+            Z = self.images.Z[ordered_indices]
+            a    = R[1]-R[0]
+            b    = R[2]-R[1]
+            c    = R[3]-R[2]
+            bxa  = np.cross(b,a)
+            bxa /= np.sqrt(np.vdot(bxa,bxa))
+            cxb  = np.cross(c,b)
+            cxb /= np.sqrt(np.vdot(cxb,cxb))
+            angle = np.vdot(bxa,cxb)
+            if angle < -1: angle = -1
+            if angle >  1: angle =  1
+            angle = np.arccos(angle)
+            if (np.vdot(bxa,c)) > 0: angle = 2*np.pi-angle
+            angle = angle*180.0/np.pi
+            text = (u'%s %s->%s->%s->%s: %.1f°'
+                    % tuple([_('dihedral')] + [symbols[z] for z in Z]+[angle]))
+        else:
+            text = ' ' + formula(Z)
+            
+        self.label.set_markup(text)
+        
+if __name__ == '__main__':
+    import os
+    os.system('python gui.py')
diff --git a/ase/gui/surfaceslab.py b/ase/gui/surfaceslab.py
new file mode 100644
index 0000000..2d94423
--- /dev/null
+++ b/ase/gui/surfaceslab.py
@@ -0,0 +1,258 @@
+# encoding: utf-8
+"""surfaceslab.py - Window for setting up surfaces
+"""
+
+import gtk
+from gettext import gettext as _
+from ase.gui.widgets import pack, cancel_apply_ok, oops
+from ase.gui.pybutton import PyButton
+from ase.gui.setupwindow import SetupWindow
+import ase.lattice.surface as _surf
+import ase
+import numpy as np
+
+introtext = _("""\
+  Use this dialog to create surface slabs.  Select the element by
+writing the chemical symbol or the atomic number in the box.  Then
+select the desired surface structure.  Note that some structures can
+be created with an othogonal or a non-orthogonal unit cell, in these
+cases the non-orthogonal unit cell will contain fewer atoms.
+
+  If the structure matches the experimental crystal structure, you can
+look up the lattice constant, otherwise you have to specify it
+yourself.""")
+
+# Name, structure, orthogonal, support-nonorthogonal, function
+surfaces = [(_('FCC(100)'), _('fcc'), True, False, _surf.fcc100),
+            (_('FCC(110)'), _('fcc'), True, False, _surf.fcc110),
+            (_('FCC(111) non-orthogonal'), _('fcc'), False, True, 
+             _surf.fcc111),
+            (_('FCC(111) orthogonal'), _('fcc'), True, True, _surf.fcc111),
+            (_('BCC(100)'), _('bcc'), True, False, _surf.bcc100),
+            (_('BCC(110) non-orthogonal'), _('bcc'), False, True, 
+             _surf.bcc110),
+            (_('BCC(110) orthogonal'), _('bcc'), True, True, _surf.bcc110),
+            (_('BCC(111) non-orthogonal'), _('bcc'), False, True, 
+             _surf.bcc111),
+            (_('BCC(111) orthogonal'), _('bcc'), True, True, _surf.bcc111),
+            (_('HCP(0001) non-orthogonal'), _('hcp'), False, True, 
+             _surf.hcp0001),
+            (_('HCP(0001) orthogonal'), _('hcp'), True, True, _surf.hcp0001),
+            (_('HCP(10-10) orthogonal'), _('hcp'), True, False, 
+             _surf.hcp10m10),
+            (_('DIAMOND(100) orthogonal'), _('diamond'), True, False,
+             _surf.diamond100),
+            (_('DIAMOND(111) non-orthogonal'), _('diamond'), False, True,
+             _surf.diamond111),
+            ]
+
+py_template = """
+from ase.lattice.surface import %(func)s
+
+atoms = %(func)s(symbol='%(symbol)s', size=%(size)s,
+    a=%(a).3f, vacuum=%(vacuum).3f%(orthoarg)s)
+"""
+
+class SetupSurfaceSlab(SetupWindow):
+    """Window for setting up a surface."""
+    def __init__(self, gui):
+        SetupWindow.__init__(self)
+        self.set_title(_("Surface"))
+        self.atoms = None
+
+        vbox = gtk.VBox()
+
+        # Intoductory text
+        self.packtext(vbox, introtext)
+             
+        # Choose the element
+        label = gtk.Label(_("Element: "))
+        element = gtk.Entry(max=3)
+        self.element = element
+        self.elementinfo = gtk.Label("")
+        pack(vbox, [label, element, self.elementinfo])
+        self.element.connect('activate', self.update)
+        self.legal_element = False
+        
+        # Choose the surface structure
+        label = gtk.Label(_("Structure: "))
+        self.structchoice = gtk.combo_box_new_text()
+        self.surfinfo = {}
+        for s in surfaces:
+            assert len(s) == 5
+            self.structchoice.append_text(s[0])
+            self.surfinfo[s[0]] = s
+        pack(vbox, [label, self.structchoice])
+        self.structchoice.connect('changed', self.update)
+
+        # Choose the lattice constant
+        tbl = gtk.Table(2, 3)
+        label = gtk.Label(_("Lattice constant: "))
+        tbl.attach(label, 0, 1, 0, 1)
+        vbox2 = gtk.VBox()          # For the non-HCP stuff
+        self.vbox_hcp = gtk.VBox()  # For the HCP stuff.
+        self.lattice_const = gtk.Adjustment(3.0, 0.0, 1000.0, 0.01)
+        lattice_box = gtk.SpinButton(self.lattice_const, 10.0, 3)
+        lattice_box.numeric = True
+        pack(vbox2, [gtk.Label(_("a:")), lattice_box, gtk.Label(_("Å"))])
+        tbl.attach(vbox2, 1, 2, 0, 1)
+        lattice_button = gtk.Button(_("Get from database"))
+        tbl.attach(lattice_button, 2, 3, 0, 1)
+        # HCP stuff
+        self.hcp_ideal = (8.0/3)**(1.0/3)
+        self.lattice_const_c = gtk.Adjustment(self.lattice_const.value * self.hcp_ideal,
+                                              0.0, 1000.0, 0.01)
+        lattice_box_c = gtk.SpinButton(self.lattice_const_c, 10.0, 3)
+        lattice_box_c.numeric = True
+        pack(self.vbox_hcp, [gtk.Label("c:"), 
+                             lattice_box_c, gtk.Label("Å")])
+        self.hcp_c_over_a_format = "c/a: %.3f " + _("(%.1f %% of ideal)")
+        self.hcp_c_over_a_label = gtk.Label(self.hcp_c_over_a_format % \
+                                                (self.hcp_ideal, 100.0))
+        pack(self.vbox_hcp, [self.hcp_c_over_a_label])
+        tbl.attach(self.vbox_hcp, 1, 2, 1, 2)
+        tbl.show_all()
+        pack(vbox, [tbl])
+        self.lattice_const.connect('value-changed', self.update)
+        self.lattice_const_c.connect('value-changed', self.update)
+        lattice_button.connect('clicked', self.get_lattice_const)
+        pack(vbox, gtk.Label(""))
+
+        # System size
+        self.size = [gtk.Adjustment(1, 1, 100, 1) for i in range(3)]
+        buttons = [gtk.SpinButton(s, 0, 0) for s in self.size]
+        self.vacuum = gtk.Adjustment(10.0, 0, 100.0, 0.1)
+        vacuum_box = gtk.SpinButton(self.vacuum, 0.0, 1)
+        pack(vbox, [gtk.Label(_("Size: \tx: ")), buttons[0],
+                    gtk.Label(_(" unit cells"))])
+        pack(vbox, [gtk.Label(_("\t\ty: ")), buttons[1],
+                    gtk.Label(_(" unit cells"))])
+        pack(vbox, [gtk.Label(_("      \t\tz: ")), buttons[2],
+                    gtk.Label(_(" layers,  ")),
+                    vacuum_box, gtk.Label(_(" Å vacuum"))])
+        self.nosize = _("\t\tNo size information yet.")
+        self.sizelabel = gtk.Label(self.nosize)
+        pack(vbox, [self.sizelabel])
+        for s in self.size:
+            s.connect('value-changed', self.update)
+        self.vacuum.connect('value-changed', self.update)
+        pack(vbox, gtk.Label(""))
+
+        # Buttons
+        self.pybut = PyButton(_("Creating a surface slab."))
+        self.pybut.connect('clicked', self.update)
+        buts = cancel_apply_ok(cancel=lambda widget: self.destroy(),
+                               apply=self.apply,
+                               ok=self.ok)
+        pack(vbox, [self.pybut, buts], end=True, bottom=True)
+        
+        self.add(vbox)
+        vbox.show()
+        self.show()
+        self.gui = gui
+
+        # Hide the HCP stuff to begin with.
+        self.vbox_hcp.hide_all()
+
+    # update_element inherited from SetupWindow
+
+    def update(self, *args):
+        "Called when something has changed."
+        struct = self.structchoice.get_active_text()
+        if struct:
+            structinfo = self.surfinfo[struct]
+            if structinfo[1] == 'hcp':
+                self.vbox_hcp.show_all()
+                ca = self.lattice_const_c.value / self.lattice_const.value
+                self.hcp_c_over_a_label.set_text(self.hcp_c_over_a_format %
+                                                 (ca, 100 * ca / self.hcp_ideal))
+            else:
+                self.vbox_hcp.hide_all()
+        # Abort if element or structure is invalid
+        if not (self.update_element() and struct):
+            self.sizelabel.set_text(self.nosize)
+            self.atoms = None
+            self.pybut.python = None
+            return False
+        # Make the atoms
+        assert self.legal_element
+        kw = {}
+        kw2 = {}
+        if structinfo[3]:  # Support othogonal keyword?
+            kw['orthogonal'] = structinfo[2]
+            kw2['orthoarg'] = ', orthogonal=' + str(kw['orthogonal'])
+        else:
+            kw2['orthoarg'] = ''
+        kw2['func'] = structinfo[4].__name__
+        kw['symbol'] = self.legal_element
+        kw['size'] = [int(s.value) for s in self.size]
+        kw['a'] = self.lattice_const.value
+        kw['vacuum'] = self.vacuum.value
+        # Now create the atoms
+        try:
+            self.atoms = structinfo[4](**kw)
+        except ValueError, e:
+            # The values were illegal - for example some size
+            # constants must be even for some structures.
+            self.pybut.python = None
+            self.atoms = None
+            self.sizelabel.set_text(str(e).replace(".  ", ".\n"))
+            return False
+        kw2.update(kw)
+        self.pybut.python = py_template % kw2
+        # Find the heights of the unit cell
+        h = np.zeros(3)
+        uc = self.atoms.get_cell()
+        for i in range(3):
+            norm = np.cross(uc[i-1], uc[i-2])
+            norm /= np.sqrt(np.dot(norm, norm))
+            h[i] = np.abs(np.dot(norm, uc[i]))
+        natoms = len(self.atoms)
+        txt = ("\t\t%.2f Å x %.2f Å x %.2f Å,  %s"
+               % (h[0], h[1], h[2], _('%i atoms.') % natoms))
+        self.sizelabel.set_text(txt)
+        return True
+    
+    def get_lattice_const(self, *args):
+        if not self.update_element():
+            oops(_("Invalid element."))
+            return
+        z = ase.atomic_numbers[self.legal_element]
+        ref = ase.data.reference_states[z]
+        surface = self.structchoice.get_active_text()
+        if not surface:
+            oops(_("No structure specified!"))
+            return
+        struct = self.surfinfo[surface][1]
+        if ref is None or ref['symmetry'] != struct:
+            from ase.data.alternatives import alternative_structures
+            alt = alternative_structures[z]
+            if alt and alt['symmetry'] == struct:
+                ref = alt
+            else:
+                oops(_('%(struct)s lattice constant unknown for %(element)s.')
+                     % dict(struct=struct.upper(), element=self.legal_element))
+        
+        a = ref['a']
+        self.lattice_const.set_value(a)
+        if struct == 'hcp':
+            c = ref['c/a'] * a
+            self.lattice_const_c.set_value(c)
+
+    def apply(self, *args):
+        self.update()
+        if self.atoms is not None:
+            self.gui.new_atoms(self.atoms)
+            return True
+        else:
+            oops(_("No valid atoms."),
+                 _("You have not (yet) specified "
+                   "a consistent set of parameters."))
+            return False
+
+    def ok(self, *args):
+        if self.apply():
+            self.destroy()
+            
+
+        
diff --git a/ase/gui/view.py b/ase/gui/view.py
new file mode 100644
index 0000000..e4e35aa
--- /dev/null
+++ b/ase/gui/view.py
@@ -0,0 +1,735 @@
+#!/usr/bin/env python
+
+# Emacs: treat this as -*- python -*-
+
+import os
+import gtk
+import tempfile
+from math import cos, sin, sqrt, atan
+from os.path import basename
+
+import numpy as np
+
+from ase.data.colors import jmol_colors
+from ase.gui.repeat import Repeat
+from ase.gui.rotate import Rotate
+from ase.gui.render import Render
+from ase.gui.colors import ColorWindow
+from ase.utils import rotate
+
+class View:
+    def __init__(self, vbox, rotations):
+        self.colormode = 'jmol'  # The default colors
+        self.nselected = 0
+        self.light_green_markings = 0
+        self.axes = rotate(rotations)
+        # this is a hack, in order to be able to toggle menu actions off/on
+        # without getting into an infinte loop
+        self.menu_change = 0
+    
+        self.atoms_to_rotate = None
+        
+        self.drawing_area = gtk.DrawingArea()
+        self.drawing_area.set_size_request(450, 450)
+        self.drawing_area.connect('button_press_event', self.press)
+        self.drawing_area.connect('button_release_event', self.release)
+        self.drawing_area.connect('motion-notify-event', self.move)
+        # Signals used to handle backing pixmap:
+        self.drawing_area.connect('expose_event', self.expose_event)
+        self.drawing_area.connect('configure_event', self.configure_event)
+        self.drawing_area.set_events(gtk.gdk.BUTTON_PRESS_MASK |
+                                     gtk.gdk.BUTTON_RELEASE_MASK |
+                                     gtk.gdk.BUTTON_MOTION_MASK |
+                                     gtk.gdk.POINTER_MOTION_HINT_MASK)
+        vbox.pack_start(self.drawing_area)
+        self.drawing_area.show()
+        self.configured = False
+        self.frame = None
+        
+    def set_coordinates(self, frame=None, focus=None):
+        if frame is None:
+            frame = self.frame
+        self.make_box()
+        self.bind(frame)
+        n = self.images.natoms
+        self.X = np.empty((n + len(self.B1) + len(self.bonds), 3))
+        #self.X[n:] = np.dot(self.B1, self.images.A[frame])
+        #self.B = np.dot(self.B2, self.images.A[frame])
+        self.set_frame(frame, focus=focus, init=True)
+
+    def set_frame(self, frame=None, focus=False, init=False):
+        if frame is None:
+            frame = self.frame
+
+        n = self.images.natoms
+
+        if self.frame > self.images.nimages:
+            self.frame = self.images.nimages - 1
+        
+        if init or frame != self.frame:
+            A = self.images.A
+            nc = len(self.B1)
+            nb = len(self.bonds)
+            
+            if init or (A[frame] != A[self.frame]).any():
+                self.X[n:n + nc] = np.dot(self.B1, A[frame])
+                self.B = np.empty((nc + nb, 3))
+                self.B[:nc] = np.dot(self.B2, A[frame])
+
+            if nb > 0:
+                P = self.images.P[frame]
+                Af = self.images.repeat[:, np.newaxis] * A[frame]
+                a = P[self.bonds[:, 0]]
+                b = P[self.bonds[:, 1]] + np.dot(self.bonds[:, 2:], Af) - a
+                d = (b**2).sum(1)**0.5
+                r = 0.65 * self.images.r
+                x0 = (r[self.bonds[:, 0]] / d).reshape((-1, 1))
+                x1 = (r[self.bonds[:, 1]] / d).reshape((-1, 1))
+                self.X[n + nc:] = a + b * x0
+                b *= 1.0 - x0 - x1
+                b[self.bonds[:, 2:].any(1)] *= 0.5
+                self.B[nc:] = self.X[n + nc:] + b
+
+            filenames = self.images.filenames
+            filename = filenames[frame]
+            if self.frame is None or filename != filenames[self.frame] or filename is None:
+                if filename is None:
+                    filename = 'ase.gui'
+            filename = basename(filename)
+            self.window.set_title(filename)
+
+        self.frame = frame
+        self.X[:n] = self.images.P[frame]
+        self.R = self.X[:n]
+        if focus:
+            self.focus()
+        else:
+            self.draw()
+        
+    def set_colors(self):
+        self.colormode = 'jmol'
+        self.set_jmol_colors()
+
+    def set_jmol_colors(self):
+        self.colors = [None] * (len(jmol_colors) + 1)
+        self.colordata = []
+        new = self.drawing_area.window.new_gc
+        alloc = self.colormap.alloc_color
+        for z in self.images.Z:
+            if self.colors[z] is None:
+                c, p, k = jmol_colors[z]
+                self.colors[z] = new(alloc(int(65535 * c),
+                                           int(65535 * p),
+                                           int(65535 * k)))
+        hasfound = {}
+        for z in self.images.Z:
+            if z not in hasfound:
+                hasfound[z] = True
+                self.colordata.append([z, jmol_colors[z]])
+                
+    def plot_cell(self):
+        V = self.images.A[0]
+        R1 = []
+        R2 = []
+        for c in range(3):
+            v = V[c]
+            d = sqrt(np.dot(v, v))
+            n = max(2, int(d / 0.3))
+            h = v / (2 * n - 1)
+            R = np.arange(n)[:, None] * (2 * h)
+            for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]:
+                R1.append(R + i * V[(c + 1) % 3] + j * V[(c + 2) % 3])
+                R2.append(R1[-1] + h)
+        return np.concatenate(R1), np.concatenate(R2)
+
+    def make_box(self):
+        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell'
+                                  ).get_active():
+            self.B1 = self.B2 = np.zeros((0, 3))
+            return
+        
+        V = self.images.A[0]
+        nn = []
+        for c in range(3):
+            v = V[c]
+            d = sqrt(np.dot(v, v))
+            n = max(2, int(d / 0.3))
+            nn.append(n)
+        self.B1 = np.zeros((2, 2, sum(nn), 3))
+        self.B2 = np.zeros((2, 2, sum(nn), 3))
+        n1 = 0
+        for c, n in enumerate(nn):
+            n2 = n1 + n
+            h = 1.0 / (2 * n - 1)
+            R = np.arange(n) * (2 * h)
+
+            for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]:
+                self.B1[i, j, n1:n2, c] = R
+                self.B1[i, j, n1:n2, (c + 1) % 3] = i
+                self.B1[i, j, n1:n2, (c + 2) % 3] = j
+            self.B2[:, :, n1:n2] = self.B1[:, :, n1:n2]
+            self.B2[:, :, n1:n2, c] += h
+            n1 = n2
+        self.B1.shape = (-1, 3)
+        self.B2.shape = (-1, 3)
+
+    def bind(self, frame):
+        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds'
+                                  ).get_active():
+            self.bonds = np.empty((0, 5), int)
+            return
+        
+        from ase.atoms import Atoms
+        from ase.calculators.neighborlist import NeighborList
+        nl = NeighborList(self.images.r * 1.5, skin=0, self_interaction=False)
+        nl.update(Atoms(positions=self.images.P[frame],
+                        cell=(self.images.repeat[:, np.newaxis] *
+                              self.images.A[frame]),
+                        pbc=self.images.pbc))
+        nb = nl.nneighbors + nl.npbcneighbors
+        self.bonds = np.empty((nb, 5), int)
+        if nb == 0:
+            return
+        
+        n1 = 0
+        for a in range(self.images.natoms):
+            indices, offsets = nl.get_neighbors(a)
+            n2 = n1 + len(indices)
+            self.bonds[n1:n2, 0] = a
+            self.bonds[n1:n2, 1] = indices
+            self.bonds[n1:n2, 2:] = offsets
+            n1 = n2
+
+        i = self.bonds[:n2, 2:].any(1)
+        self.bonds[n2:, 0] = self.bonds[i, 1]
+        self.bonds[n2:, 1] = self.bonds[i, 0]
+        self.bonds[n2:, 2:] = -self.bonds[i, 2:]
+
+    def toggle_show_unit_cell(self, action):
+        self.set_coordinates()
+        
+    def reset_tools_modes(self):
+        dummy = self.menu_change
+        self.menu_change = 1
+        self.atoms_to_rotate = None
+        for c_mode in ['Rotate', 'Orient', 'Move']:
+              self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).set_active(False)
+        self.light_green_markings = 0
+        self.menu_change = 0        
+        self.draw()
+        
+                      
+    def toggle_mode(self, mode):
+        self.menu_change = 1
+        i_sum = 0
+        for c_mode in ['Rotate', 'Orient', 'Move']:
+            i_sum += self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).get_active() 
+        if i_sum == 0 or (i_sum == 1 and sum(self.images.selected) == 0):
+            self.reset_tools_modes()
+            return()
+            
+        if i_sum == 2:
+            try:
+                self.images.selected = self.atoms_to_rotate_0.copy()
+            except:
+                self.atoms_to_rotate_0 = self.images.selected.copy()
+        if i_sum == 1:
+            self.atoms_to_rotate_0 = self.images.selected.copy()
+
+        for c_mode in ['Rotate', 'Orient', 'Move']:
+            if c_mode != mode:
+                  self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).set_active(False) 
+        
+        if self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % mode).get_active():
+            self.atoms_to_rotate_0 = self.images.selected.copy()
+            for i in range(len(self.images.selected)):
+               self.images.selected[i] = False
+            self.light_green_markings = 1
+        else:
+            try: 
+                atr = self.atoms_to_rotate_0
+                for i in range(len(self.images.selected)):
+                    self.images.selected[i] = atr[i]
+            except:
+                pass                
+                
+        self.menu_change = 0
+        self.draw()
+                      
+    def toggle_move_mode(self, action):
+        """
+        Toggles the move mode, where the selected atoms can be moved with the arrow
+        keys and pg up/dn. If the shift key is pressed, the movement will be reduced.
+        
+        The movement will be relative to the current rotation of the coordinate system.
+        
+        The implementation of the move mode is found in the gui.scroll
+        """
+        if not (self.menu_change):
+            self.toggle_mode('Move')
+
+    def toggle_rotate_mode(self, action):
+        """
+        Toggles the rotate mode, where the selected atoms can be rotated with the arrow keys
+        and pg up/dn. If the shift key is pressed, the rotation angle will be reduced.
+        
+        The atoms to be rotated will be marked with light green - and the COM of the selected
+        atoms will be used as the COM of the rotation. This can be changed while rotating the
+        selected atoms.
+        
+        If only two atoms are seleceted, and the number of atoms to be rotated is different from
+        two, the selected atoms will define the axis of rotation.
+        
+        The implementation of the rotate mode is found in the gui.scroll
+        """
+        if not (self.menu_change):
+            self.toggle_mode('Rotate')
+                
+    def toggle_orient_mode(self, action):
+        """
+        Toggle the orientation mode - the orientation of the atoms will be changed
+        according to the arrow keys selected.
+        
+        If nothing is selected, standard directions are x, y and z
+        if two atoms are selected, the standard directions are along their displacement vector
+        if three atoms are selected, the orientation is changed according to the normal of these
+        three vectors.
+        """
+        if not (self.menu_change):
+            self.toggle_mode('Orient')
+        self.orient_normal = np.array([1.0, 0.0, 0.0])
+        sel_pos = []
+        for i, j in enumerate(self.atoms_to_rotate_0):
+            if j: 
+                sel_pos.append(self.R[i])
+        if len(sel_pos) == 2:
+            self.orient_normal = sel_pos[0] - sel_pos[1]
+        if len(sel_pos) == 3:
+            v1 = sel_pos[1] - sel_pos[0]
+            v2 = sel_pos[1] - sel_pos[2]
+            self.orient_normal = np.cross(v1, v2)
+        self.orient_normal /= sum(self.orient_normal ** 2) ** 0.5
+            
+    def toggle_show_axes(self, action):
+        self.draw()
+
+    def toggle_show_bonds(self, action):
+        self.set_coordinates()
+
+    def repeat_window(self, menuitem):
+        self.reset_tools_modes()
+        Repeat(self)
+
+    def rotate_window(self, menuitem):
+        Rotate(self)
+
+    def colors_window(self, menuitem):
+        ColorWindow(self)
+
+    def focus(self, x=None):
+        if (self.images.natoms == 0 and not
+            self.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell').get_active()):
+            self.scale = 1.0
+            self.center = np.zeros(3)
+            self.draw()
+            return
+        
+        P = np.dot(self.X, self.axes)
+        n = self.images.natoms
+        P[:n] -= self.images.r[:, None]
+        P1 = P.min(0) 
+        P[:n] += 2 * self.images.r[:, None]
+        P2 = P.max(0)
+        self.center = np.dot(self.axes, (P1 + P2) / 2)
+        S = 1.3 * (P2 - P1)
+        if S[0] * self.height < S[1] * self.width:
+            self.scale = self.height / S[1]
+        else:
+            self.scale = self.width / S[0]
+        self.draw()
+
+    def reset_view(self, menuitem):
+        self.axes = rotate('0.0x,0.0y,0.0z')
+        self.set_coordinates()
+        self.focus(self)
+
+    def get_colors(self, rgb = False):
+        Z = self.images.Z
+        if rgb:
+            # create a shape that is equivalent to self.colors,
+            # but contains rgb data instead gtk.gdk.GCX11 objects
+            colarray = [None] * max(len(jmol_colors)+1,len(self.colordata))
+            for z, c in self.colordata:
+                colarray[z] = c
+        else:
+            colarray = self.colors
+        if self.colormode == 'jmol' or self.colormode == 'atno':
+            colors = np.array(colarray)[Z]
+        elif self.colormode == 'tags':
+            colors = np.array(colarray)[self.images.T[self.frame]]
+        elif self.colormode == 'force':
+            F = self.images.F[self.frame]
+            F = np.sqrt(((F*self.images.dynamic[:,np.newaxis])**2).sum(axis=-1))  # The absolute force
+            nF = (F - self.colormode_force_data[0]) * self.colormode_force_data[1]
+            nF = np.clip(nF.astype(int), 0, len(self.colors)-1)
+            colors = np.array(colarray)[nF]
+        elif self.colormode == 'velocity':
+            V = self.images.V[self.frame]
+            V = np.sqrt((V*V).sum(axis=-1))  # The absolute velocity
+            nV = (V - self.colormode_velocity_data[0]) * self.colormode_velocity_data[1]
+            nV = np.clip(nV.astype(int), 0, len(self.colors)-1)
+            colors = np.array(colarray)[nV]
+        elif self.colormode == 'manual':
+            colors = colarray
+        elif self.colormode == 'same':
+            colors = [colarray[0]] * self.images.natoms
+        else:
+            raise RuntimeError('Unknown color mode: %s' % (self.colormode,))
+        return colors
+
+    def repeat_colors(self, repeat):
+        natoms = self.images.natoms
+        if self.colormode == 'manual':
+            a0 = 0
+            colors = self.colors
+            colordata = self.colordata
+            for i0 in range(repeat[0]):
+                for i1 in range(repeat[1]):
+                    for i2 in range(repeat[2]):
+                        a1 = a0 + natoms
+                        colors[a0:a1] = self.colors[:natoms]
+                        colordata[a0:a1] = self.colordata[:natoms]
+                        a0 = a1
+            self.colors = colors
+            self.colordata = colordata
+
+    def my_arc(self, gc, fill, j, X, r, n, A):
+   
+        if self.images.shapes is not None:
+            rx = (self.images.shapes[j, 0]).round().astype(int)
+            ry = (self.images.shapes[j, 1]).round().astype(int)
+            rz = (self.images.shapes[j, 2]).round().astype(int)
+            circle = rx == ry and ry == rz
+        else:
+            circle = True
+
+        if not circle:
+            Q = self.images.Q[j]
+            X2d = np.array([X[j][0], X[j][1]])
+            Ellipsoid = np.array([[1. / (rx*rx), 0, 0],
+                                  [0, 1. / (ry*ry), 0],
+                                  [0, 0, 1. / (rz*rz)]
+                                  ])
+            # Ellipsoid rotatet by quaternion as Matrix X' = R X R_transpose
+            El_r = np.dot(Q.rotation_matrix(),
+                          np.dot(Ellipsoid, 
+                                 np.transpose(Q.rotation_matrix())))
+            # Ellipsoid rotated by quaternion and axes as 
+            # Matrix X' =  R_axes X' R_axes
+            El_v = np.dot(np.transpose(self.axes), np.dot(El_r, self.axes))
+            # Projection of rotatet ellipsoid on xy plane
+            El_p = Ell = np.array([
+                    [El_v[0][0] - El_v[0][2] * El_v[0][2] / El_v[2][2],
+                     El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2]],
+                    [El_v[0][1] - El_v[0][2] * El_v[1][2] / El_v[2][2],
+                     El_v[1][1] - El_v[1][2] * El_v[1][2] / El_v[2][2]]
+                    ])
+            # diagonal matrix der Ellipse gibt halbachsen
+            El_p_diag = np.linalg.eig(El_p)
+            # Winkel mit dem Ellipse in xy gedreht ist aus 
+            # eigenvektor der diagonal matrix
+            phi = atan(El_p_diag[1][0][1] / El_p_diag[1][0][0])
+            tupl = []
+            alpha = np.array(range(20)) *2* np.pi /20
+            El_xy = np.array([sqrt(1. / (El_p_diag[0][0])) *
+                              np.cos(alpha)*np.cos(phi) 
+                              - sqrt(1./(El_p_diag[0][1])) * 
+                              np.sin(alpha) * np.sin(phi),
+                              sqrt(1./(El_p_diag[0][0])) * 
+                              np.cos(alpha)*np.sin(phi)
+                              + sqrt(1./(El_p_diag[0][1])) * 
+                              np.sin(alpha) * np.cos(phi)])
+
+            tupl = (El_xy.transpose() * self.scale + 
+                    X[j][:2]).round().astype(int)
+            # XXX there must be a better way
+            tupl = [tuple(i) for i in tupl]
+
+            return self.pixmap.draw_polygon( gc, fill, tupl)
+        
+        dx = dy = (2 * r).round().astype(int)
+        rj = dx[j]
+        
+        return self.pixmap.draw_arc(gc, fill, A[j, 0], A[j, 1], rj, rj, 
+                                    0, 23040)
+
+    def draw(self, status=True):
+        self.pixmap.draw_rectangle(self.white_gc, True, 0, 0,
+                                   self.width, self.height)
+        axes = self.scale * self.axes * (1, -1, 1)
+        offset = (np.dot(self.center, axes) -
+                  (0.5 * self.width, 0.5 * self.height, 0))
+        X = np.dot(self.X, axes) - offset
+        n = self.images.natoms
+        self.indices = X[:, 2].argsort()
+        if self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds').get_active():
+            r = self.images.r * (0.65 * self.scale)
+        else:
+            r = self.images.r * self.scale
+        P = self.P = X[:n, :2]
+        A = (P - r[:, None]).round().astype(int)
+        X1 = X[n:, :2].round().astype(int)
+        X2 = (np.dot(self.B, axes) - offset).round().astype(int)
+        d = (2 * r).round().astype(int)
+
+        selected_gc = self.selected_gc
+        colors = self.get_colors()
+        arc = self.pixmap.draw_arc
+        line = self.pixmap.draw_line
+        black_gc = self.black_gc
+        dynamic = self.images.dynamic
+        selected = self.images.selected
+        visible = self.images.visible
+        for a in self.indices:
+            if a < n:
+                ra = d[a]
+                if visible[a]:
+                    self.my_arc(colors[a], True, a, X, r, n, A)
+                if  self.light_green_markings and self.atoms_to_rotate_0[a]:
+                    arc(self.green, False, A[a, 0] + 2, A[a, 1] + 2,
+                        ra - 4, ra - 4, 0, 23040)
+
+                if not dynamic[a]:
+                    R1 = int(0.14644 * ra)
+                    R2 = int(0.85355 * ra)
+                    line(black_gc,
+                         A[a, 0] + R1, A[a, 1] + R1,
+                         A[a, 0] + R2, A[a, 1] + R2)
+                    line(black_gc,
+                         A[a, 0] + R2, A[a, 1] + R1,
+                         A[a, 0] + R1, A[a, 1] + R2)
+                if selected[a]:
+                    self.my_arc(selected_gc, False, a, X, r, n, A)
+                elif visible[a]:
+                    self.my_arc(black_gc, False, a, X, r, n, A)
+            else:
+                a -= n
+                line(black_gc, X1[a, 0], X1[a, 1], X2[a, 0], X2[a, 1])
+
+        if self.ui.get_widget('/MenuBar/ViewMenu/ShowAxes').get_active():
+            self.draw_axes()
+
+        if self.images.nimages > 1:
+            self.draw_frame_number()
+            
+        self.drawing_area.window.draw_drawable(self.white_gc, self.pixmap,
+                                               0, 0, 0, 0,
+                                               self.width, self.height)
+
+        if status:
+            self.status()
+
+    def draw_axes(self):
+        from ase.quaternions import Quaternion
+        q = Quaternion().from_matrix(self.axes)
+        L = np.zeros((10, 2, 3))
+        L[:3, 1] = self.axes * 15
+        L[3:5] = self.axes[0] * 20
+        L[5:7] = self.axes[1] * 20
+        L[7:] = self.axes[2] * 20
+        L[3:, :, :2] += (((-4, -5), (4,  5)), ((-4,  5), ( 4, -5)), 
+                         ((-4,  5), (0,  0)), ((-4, -5), ( 4,  5)), 
+                         ((-4,  5), (4,  5)), (( 4,  5), (-4, -5)), 
+                         ((-4, -5), (4, -5)))
+        L = L.round().astype(int)
+        L[:, :, 0] += 20
+        L[:, :, 1] = self.height - 20 - L[:, :, 1]
+        line = self.pixmap.draw_line
+        colors = ([self.black_gc] * 3 +
+                  [self.red] * 2 + [self.green] * 2 + [self.blue] * 3)
+        for i in L[:, 1, 2].argsort():
+            (a, b), (c, d) = L[i, :, :2]
+            line(colors[i], a, b, c, d)
+
+    digits = np.array(((1, 1, 1, 1, 1, 1, 0),
+                       (0, 1, 1, 0, 0, 0, 0),
+                       (1, 0, 1, 1, 0, 1, 1),
+                       (1, 1, 1, 1, 0, 0, 1),
+                       (0, 1, 1, 0, 1, 0, 1),
+                       (1, 1, 0, 1, 1, 0, 1),
+                       (1, 1, 0, 1, 1, 1, 1),
+                       (0, 1, 1, 1, 0, 0, 0),
+                       (1, 1, 1, 1, 1, 1, 1),
+                       (0, 1, 1, 1, 1, 0, 1)), bool)
+
+    bars = np.array(((0, 2, 1, 2),
+                     (1, 2, 1, 1),
+                     (1, 1, 1, 0),
+                     (1, 0, 0, 0),
+                     (0, 0, 0, 1),
+                     (0, 1, 0, 2),
+                     (0, 1, 1, 1))) * 5
+    
+    def draw_frame_number(self):
+        n = str(self.frame)
+        x = self.width - 3 - 8 * len(n)
+        y = self.height - 27
+        color = self.black_gc
+        line = self.pixmap.draw_line
+        for c in n:
+            bars = View.bars[View.digits[int(c)]]
+            for a, b, c, d in bars:
+                line(color, a + x, b + y, c + x, d + y)
+            x += 8
+        
+    def release(self, drawing_area, event):
+        if event.button != 1:
+            return
+
+        selected = self.images.selected
+        selected_ordered = self.images.selected_ordered
+
+        if event.time < self.t0 + 200:  # 200 ms
+            d = self.P - self.xy
+            hit = np.less((d**2).sum(1), (self.scale * self.images.r)**2)
+            for a in self.indices[::-1]:
+                if a < self.images.natoms and hit[a]:
+                    if event.state & gtk.gdk.CONTROL_MASK:
+                        selected[a] = not selected[a]
+                        if selected[a]: 
+                            selected_ordered += [a]
+                        elif len(selected_ordered) > 0:
+                            if selected_ordered[-1] == a:
+                                selected_ordered = selected_ordered[:-1]
+                            else:
+                                selected_ordered = []
+                    else:
+                        selected[:] = False
+                        selected[a] = True
+                        selected_ordered = [a]
+                    break
+            else:
+                selected[:] = False
+                selected_ordered = []
+            self.draw()
+        else:
+            A = (event.x, event.y)
+            C1 = np.minimum(A, self.xy)
+            C2 = np.maximum(A, self.xy)
+            hit = np.logical_and(self.P > C1, self.P < C2)
+            indices = np.compress(hit.prod(1), np.arange(len(hit)))
+            if not (event.state & gtk.gdk.CONTROL_MASK):
+                selected[:] = False
+            selected[indices] = True
+            if len(indices) == 1 and indices[0] not in self.images.selected_ordered: 
+                selected_ordered += [indices[0]]
+            elif len(indices) > 1:
+                selected_ordered = []
+            self.draw()
+
+        indices = np.arange(self.images.natoms)[self.images.selected]
+        if len(indices) != len(selected_ordered):
+            selected_ordered = []
+        self.images.selected_ordered = selected_ordered
+
+    def press(self, drawing_area, event):
+        self.button = event.button
+        self.xy = (event.x, event.y)
+        self.t0 = event.time
+        self.axes0 = self.axes
+        self.center0 = self.center
+        
+    def move(self, drawing_area, event):
+             
+        x, y, state = event.window.get_pointer()
+        x0, y0 = self.xy
+        if self.button == 1:
+            window = self.drawing_area.window
+            window.draw_drawable(self.white_gc, self.pixmap,
+                                 0, 0, 0, 0,
+                                 self.width, self.height)
+            x0 = int(round(x0))
+            y0 = int(round(y0))
+            window.draw_rectangle(self.selected_gc, False,
+                                  min(x, x0), min(y, y0),
+                                  abs(x - x0), abs(y - y0))
+            return
+        if self.button == 2:
+            return
+        if state & gtk.gdk.SHIFT_MASK:
+            self.center = (self.center0 -
+                           np.dot(self.axes, (x - x0, y0 - y, 0)) / self.scale)
+        else:
+            # Snap mode: the a-b angle and t should multipla of 15 degrees ???
+            a = x - x0
+            b = y0 - y
+            t = sqrt(a * a + b * b)
+            if t > 0:
+                a /= t
+                b /= t
+            else:
+                a = 1.0
+                b = 0.0
+            c = cos(0.01 * t)
+            s = -sin(0.01 * t)
+            rotation = np.array([(c * a * a + b * b, (c - 1) * b * a, s * a),
+                                 ((c - 1) * a * b, c * b * b + a * a, s * b),
+                                 (-s * a, -s * b, c)])
+            self.axes = np.dot(self.axes0, rotation)
+            if self.images.natoms > 0:
+                com = self.X[:self.images.natoms].mean(0) 
+            else:
+                com = self.images.A[self.frame].mean(0)
+            self.center = com - np.dot(com - self.center0,
+                                       np.dot(self.axes0, self.axes.T))
+        self.draw(status=False)
+        
+    # Create a new backing pixmap of the appropriate size
+    def configure_event(self, drawing_area, event):
+        if self.configured:
+            w = self.width
+            h = self.height
+        else:
+            self.colormap = self.drawing_area.get_colormap()
+            self.black_gc = self.drawing_area.get_style().black_gc
+            self.white_gc = self.drawing_area.get_style().white_gc
+            self.red = self.drawing_area.window.new_gc(
+                self.colormap.alloc_color(62345, 0, 0), line_width=2)
+            self.green = self.drawing_area.window.new_gc(
+                self.colormap.alloc_color(0, 54456, 0), line_width=2)
+            self.blue = self.drawing_area.window.new_gc(
+                self.colormap.alloc_color(0, 0, 54456), line_width=2)
+            self.selected_gc = self.drawing_area.window.new_gc(
+                self.colormap.alloc_color(0, 16456, 0),
+                line_width=3)
+            
+        x, y, self.width, self.height = drawing_area.get_allocation()
+        self.pixmap = gtk.gdk.Pixmap(drawing_area.window,
+                                     self.width, self.height)
+        if self.configured:
+            self.scale *= sqrt(1.0 * self.width * self.height / (w * h))
+            self.draw()
+        self.configured = True
+        
+    # Redraw the screen from the backing pixmap
+    def expose_event(self, drawing_area, event):
+        x , y, width, height = event.area
+        gc = self.white_gc
+        drawing_area.window.draw_drawable(gc, self.pixmap,
+                                          x, y, x, y, width, height)
+
+    def external_viewer(self, action):
+        name = action.get_name()
+        command = {'Avogadro' : 'avogadro',
+                   'XMakeMol': 'xmakemol -f',
+                   'RasMol':'rasmol -xyz',
+                   'VMD': 'vmd'}[name]
+        fd, filename = tempfile.mkstemp('.xyz', 'ase.gui-')
+        os.close(fd)
+        self.images.write(filename)
+        os.system('(%s %s &); (sleep 60; rm %s) &' %
+                  (command, filename, filename))
+
+    def render_window(self, action):
+        Render(self)
+        
diff --git a/ase/gui/widgets.py b/ase/gui/widgets.py
new file mode 100644
index 0000000..72e0f59
--- /dev/null
+++ b/ase/gui/widgets.py
@@ -0,0 +1,159 @@
+from gettext import gettext as _
+import gtk
+
+
+class Number(gtk.SpinButton):
+    def __init__(self, value=0,
+                 lower=0, upper=10000,
+                 step_incr=1, page_incr=10,
+                 climb_rate=0.5, digits=0):
+        self.adj = gtk.Adjustment(value, lower, upper, step_incr, page_incr, 0)
+        gtk.SpinButton.__init__(self, self.adj, climb_rate, digits)
+
+    def connect(self, *args):
+        return self.adj.connect(*args)
+
+
+class Menu:
+    def __init__(self, menubar, name, items):
+        self.items = {}
+        menu = gtk.Menu()
+        for data in items:
+            text = data[0]
+            callback = data[1]
+            args = data[2:]
+            menuitem = gtk.MenuItem(text)
+            menu.append(menuitem)
+            menuitem.connect('activate', callback, *args)
+            menuitem.show()
+            self.items[text] = menuitem
+        menuitem = gtk.MenuItem(name)
+        menubar.append(menuitem)
+        menuitem.set_submenu(menu)
+        menuitem.show()
+
+
+class Help(gtk.Window):
+    __instance = None
+    def __new__(cls, *args, **kwargs):
+        # Make this a singleton.
+        if Help.__instance is None:
+            Help.__instance = gtk.Window.__new__(cls, *args, **kwargs)
+        return Help.__instance
+    def __init__(self, text):
+        # Now, __init__ may be called multiple times!
+        if not hasattr(self, '_initialized'):
+            self.initialize(text)
+        else:
+            self.set_text(text)
+        self.present()  # Show the window.
+        
+    def initialize(self, text):
+        gtk.Window.__init__(self)
+        self.set_title(_("Help"))
+        self._initialized = True
+        vbox = gtk.VBox()
+        self.add(vbox)
+        self.label = pack(vbox, gtk.Label())
+        self.label.set_line_wrap(True)
+        self.set_text(text)
+        close = gtk.Button(_('Close'))
+        pack(vbox, [close])
+        close.connect('clicked', self.destroy)
+        self.connect("delete-event", self.destroy) 
+        self.show_all()
+
+    def set_text(self, text):
+        # Count line length
+        linelen = max([len(x) for x in text.split('\n')])
+        text = text.replace('<c>', '<span foreground="blue">')
+        text = text.replace('</c>', '</span>')
+        self.label.set_width_chars(linelen)
+        self.label.set_line_wrap(False)
+        self.label.set_markup(text)
+
+    def destroy(self, *args):
+        self.hide()
+        return True  # Prevents destruction of the window.
+        
+def help(text):
+    button = gtk.Button(_('Help'))
+    button.connect('clicked', lambda widget, text=text: Help(text))
+    return button
+
+
+class Window(gtk.Window):
+    def __init__(self, gui):
+        self.gui = gui
+        gtk.Window.__init__(self)
+        self.set_title(_('Constraints'))
+        vbox = gtk.VBox()
+        b = pack(vbox, [gtk.Button(_('Constrain')),
+                        gtk.Label(_(' selected atoms'))])[0]
+        b.connect('clicked', self.selected)
+        b = pack(vbox, [gtk.Button(_('Constrain')),
+                        gtk.Label(_(' immobile atoms:'))])[0]
+        b.connect('clicked', self.immobile)
+        b = pack(vbox, gtk.Button(_('Clear constraint')))
+        b.connect('clicked', self.clear)
+        close = pack(vbox, gtk.Button(_('Close')))
+        close.connect('clicked', lambda widget: self.destroy())
+        self.add(vbox)
+        vbox.show()
+        self.show()
+
+def pack(vbox, widgets, end=False, bottom=False, expand=False, padding=0):
+    if not isinstance(widgets, list):
+        widgets.show()
+        if bottom:
+            vbox.pack_end(widgets, expand, expand, padding)
+        else:
+            vbox.pack_start(widgets, expand, expand, padding)
+        return widgets
+    hbox = gtk.HBox(0, 0)
+    hbox.show()
+    if bottom:
+        vbox.pack_end(hbox, expand, expand, padding)
+    else:
+        vbox.pack_start(hbox, expand, expand, padding)
+    for widget in widgets:
+        if type(widget) is gtk.Entry:
+            widget.set_size_request(widget.get_max_length() * 9, 24)
+        widget.show()
+        if end and widget is widgets[-1]:
+            hbox.pack_end(widget, expand, expand, padding)
+        else:
+            hbox.pack_start(widget, expand, expand, padding)
+    return widgets
+
+class cancel_apply_ok(gtk.HButtonBox):
+    "Widget with Cancel, Apply and OK buttons.  The arguments are callbacks."
+    def __init__(self, cancel, apply, ok):
+        gtk.HButtonBox.__init__(self)
+        cancel_but = gtk.Button(stock=gtk.STOCK_CANCEL)
+        cancel_but.connect('clicked', cancel)
+        apply_but = gtk.Button(stock=gtk.STOCK_APPLY)
+        apply_but.connect('clicked', apply)
+        ok_but = gtk.Button(stock=gtk.STOCK_OK)
+        ok_but.connect('clicked', ok)
+        for w in (cancel_but, apply_but, ok_but):
+            self.pack_start(w, 0, 0)
+            w.show()
+        #self.show_all()
+        
+def oops(message, message2=None):
+    dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
+                               type=gtk.MESSAGE_WARNING,
+                               buttons=gtk.BUTTONS_CLOSE,
+                               message_format=message)
+    try:
+        dialog.format_secondary_text(message2)
+    except AttributeError:
+        print >>sys.stderr, message
+        print >>sys.stderr, message2
+    dialog.connect('response', lambda x, y, dialog=dialog: dialog.destroy())
+    dialog.show()
+
+class AseGuiCancelException(Exception):
+    pass
+        
diff --git a/ase/infrared.py b/ase/infrared.py
new file mode 100644
index 0000000..6460d54
--- /dev/null
+++ b/ase/infrared.py
@@ -0,0 +1,298 @@
+
+# -*- coding: utf-8 -*-
+
+"""Infrared intensities"""
+
+import pickle
+from math import sin, pi, sqrt, exp, log
+
+import numpy as np
+
+import ase.units as units
+from ase.io.trajectory import PickleTrajectory
+from ase.parallel import rank, barrier, parprint
+from ase.vibrations import Vibrations
+
+
+class InfraRed(Vibrations):
+    """Class for calculating vibrational modes and infrared intensities
+    using finite difference.
+
+    The vibrational modes are calculated from a finite difference
+    approximation of the Dynamical matrix and the IR intensities from
+    a finite difference approximation of the gradient of the dipole
+    moment. The method is described in:
+
+      D. Porezag, M. R. Pederson:
+      "Infrared intensities and Raman-scattering activities within
+      density-functional theory",
+      Phys. Rev. B 54, 7830 (1996)
+
+    The calculator object (calc) linked to the Atoms object (atoms) must 
+    have the attribute:
+    
+    >>> calc.get_dipole_moment(atoms)
+
+    In addition to the methods included in the ``Vibrations`` class
+    the ``InfraRed`` class introduces two new methods;
+    *get_spectrum()* and *write_spectra()*. The *summary()*, *get_energies()*, 
+    *get_frequencies()*, *get_spectrum()* and *write_spectra()*
+    methods all take an optional *method* keyword.  Use
+    method='Frederiksen' to use the method described in:
+
+      T. Frederiksen, M. Paulsson, M. Brandbyge, A. P. Jauho:
+      "Inelastic transport theory from first-principles: methodology
+      and applications for nanoscale devices", 
+      Phys. Rev. B 75, 205413 (2007) 
+
+    atoms: Atoms object
+        The atoms to work on.
+    indices: list of int
+        List of indices of atoms to vibrate.  Default behavior is
+        to vibrate all atoms.
+    name: str
+        Name to use for files.
+    delta: float
+        Magnitude of displacements.
+    nfree: int
+        Number of displacements per degree of freedom, 2 or 4 are
+        supported. Default is 2 which will displace each atom +delta
+        and -delta in each cartesian direction.
+    directions: list of int
+        Cartesian coordinates to calculate the gradient of the dipole moment in. 
+        For example directions = 2 only dipole moment in the z-direction will
+        be considered, whereas for directions = [0, 1] only the dipole
+        moment in the xy-plane will be considered. Default behavior is to
+        use the dipole moment in all directions.
+
+    Example:
+    
+    >>> from ase.io import read
+    >>> from ase.calculators.vasp import Vasp
+    >>> from ase.infrared import InfraRed
+    >>> water = read('water.traj')  # read pre-relaxed structure of water molecule
+    >>> calc = Vasp(prec='Accurate',
+    ...             ediff=1E-8,
+    ...             isym=0,
+    ...             idipol=4,       # calculate the total dipole moment
+    ...             dipol=water.get_center_of_mass(scaled=True),
+    ...             ldipol=True)
+    >>> water.set_calculator(calc)
+    >>> ir = InfraRed(water)
+    >>> ir.run()
+    >>> ir.summary()
+    -------------------------------------
+    Mode    Frequency        Intensity
+    #    meV     cm^-1   (D/Å)^2 amu^-1
+    -------------------------------------
+    0   16.9i    136.2i     1.6108
+    1   10.5i     84.9i     2.1682
+    2    5.1i     41.1i     1.7327
+    3    0.3i      2.2i     0.0080
+    4    2.4      19.0      0.1186
+    5   15.3     123.5      1.4956
+    6  195.5    1576.7      1.6437
+    7  458.9    3701.3      0.0284
+    8  473.0    3814.6      1.1812
+    -------------------------------------
+    Zero-point energy: 0.573 eV
+    Static dipole moment: 1.833 D
+    Maximum force on atom in `equilibrium`: 0.0026 eV/Å
+
+
+
+    This interface now also works for calculator 'siesta', 
+    (added get_dipole_moment for siesta).
+
+    Example:
+
+    >>> #!/usr/bin/env python
+
+    >>> from ase.io import read
+    >>> from ase.calculators.siesta import Siesta
+    >>> from ase.infrared import InfraRed
+
+    >>> bud = read('bud1.xyz')
+
+    >>> calc = Siesta(label='bud',
+    ...       meshcutoff=250 * Ry,
+    ...       basis='DZP',
+    ...       kpts=[1, 1, 1])
+
+    >>> calc.set_fdf('DM.MixingWeight', 0.08)
+    >>> calc.set_fdf('DM.NumberPulay', 3)
+    >>> calc.set_fdf('DM.NumberKick', 20)
+    >>> calc.set_fdf('DM.KickMixingWeight', 0.15)
+    >>> calc.set_fdf('SolutionMethod',      'Diagon')
+    >>> calc.set_fdf('MaxSCFIterations', 500)
+    >>> calc.set_fdf('PAO.BasisType',  'split')
+    >>> #50 meV = 0.003674931 * Ry
+    >>> calc.set_fdf('PAO.EnergyShift', 0.003674931 * Ry )
+    >>> calc.set_fdf('LatticeConstant', 1.000000 * Ang)
+    >>> calc.set_fdf('WriteCoorXmol',       'T')
+
+    >>> bud.set_calculator(calc)
+
+    >>> ir = InfraRed(bud)
+    >>> ir.run()
+    >>> ir.summary()
+
+
+
+
+    """
+    def __init__(self, atoms, indices=None, name='ir', delta=0.01, nfree=2, directions=None):
+        assert nfree in [2, 4]
+        self.atoms = atoms
+        if atoms.constraints:
+            print "WARNING! \n Your Atoms object is constrained. Some forces may be unintended set to zero. \n"
+        self.calc = atoms.get_calculator()
+        if indices is None:
+            indices = range(len(atoms))
+        self.indices = np.asarray(indices)
+        self.nfree = nfree
+        self.name = name+'-d%.3f' % delta
+        self.delta = delta
+        self.H = None
+        if directions is None:
+            self.directions = np.asarray([0, 1, 2])
+        else:
+            self.directions = np.asarray(directions)
+        self.ir = True
+
+    def read(self, method='standard', direction='central'):
+        self.method = method.lower()
+        self.direction = direction.lower()
+        assert self.method in ['standard', 'frederiksen']
+        if direction != 'central':
+            raise NotImplementedError('Only central difference is implemented at the moment.')
+
+        # Get "static" dipole moment and forces
+        name = '%s.eq.pckl' % self.name
+        [forces_zero, dipole_zero] = pickle.load(open(name))
+        self.dipole_zero = (sum(dipole_zero**2)**0.5)*units.Debye
+        self.force_zero = max([sum((forces_zero[j])**2)**0.5 for j in self.indices])
+
+        ndof = 3 * len(self.indices)
+        H = np.empty((ndof, ndof))
+        dpdx = np.empty((ndof, 3))
+        r = 0
+        for a in self.indices:
+            for i in 'xyz':
+                name = '%s.%d%s' % (self.name, a, i)
+                [fminus, dminus] = pickle.load(open(name + '-.pckl'))
+                [fplus, dplus] = pickle.load(open(name + '+.pckl'))
+                if self.nfree == 4:
+                    [fminusminus, dminusminus] = pickle.load(open(name + '--.pckl'))
+                    [fplusplus, dplusplus] = pickle.load(open(name + '++.pckl'))
+                if self.method == 'frederiksen':
+                    fminus[a] += -fminus.sum(0)
+                    fplus[a] += -fplus.sum(0)
+                    if self.nfree == 4:
+                        fminusminus[a] += -fminus.sum(0)
+                        fplusplus[a] += -fplus.sum(0)
+                if self.nfree == 2:
+                    H[r] = (fminus - fplus)[self.indices].ravel() / 2.0
+                    dpdx[r] = (dminus - dplus)
+                if self.nfree == 4:
+                    H[r] = (-fminusminus+8*fminus-8*fplus+fplusplus)[self.indices].ravel() / 12.0
+                    dpdx[r] = (-dplusplus + 8*dplus - 8*dminus +dminusminus) / 6.0
+                H[r] /= 2 * self.delta
+                dpdx[r] /= 2 * self.delta
+                for n in range(3):
+                    if n not in self.directions:
+                        dpdx[r][n] = 0
+                        dpdx[r][n] = 0
+                r += 1
+        # Calculate eigenfrequencies and eigenvectors
+        m = self.atoms.get_masses()
+        H += H.copy().T
+        self.H = H
+        m = self.atoms.get_masses()
+        self.im = np.repeat(m[self.indices]**-0.5, 3)
+        omega2, modes = np.linalg.eigh(self.im[:, None] * H * self.im)
+        self.modes = modes.T.copy()
+
+        # Calculate intensities
+        dpdq = np.array([dpdx[j]/sqrt(m[self.indices[j/3]]*units._amu/units._me) for j in range(ndof)])
+        dpdQ = np.dot(dpdq.T, modes)
+        dpdQ = dpdQ.T
+        intensities = np.array([sum(dpdQ[j]**2) for j in range(ndof)])
+        # Conversion factor:
+        s = units._hbar * 1e10 / sqrt(units._e * units._amu)
+        self.hnu = s * omega2.astype(complex)**0.5
+        # Conversion factor from atomic units to (D/Angstrom)^2/amu.
+        conv = units.Debye**2*units._amu/units._me
+        self.intensities = intensities*conv
+
+    def summary(self, method='standard', direction='central'):
+        hnu = self.get_energies(method, direction)
+        s = 0.01 * units._e / units._c / units._hplanck
+        parprint('-------------------------------------')
+        parprint(' Mode    Frequency        Intensity')
+        parprint('  #    meV     cm^-1   (D/Å)^2 amu^-1')
+        parprint('-------------------------------------')
+        for n, e in enumerate(hnu):
+            if e.imag != 0:
+                c = 'i'
+                e = e.imag
+            else:
+                c = ' '
+            parprint('%3d %6.1f%s  %7.1f%s  %9.4f' % 
+                     (n, 1000 * e, c, s * e, c, self.intensities[n]))
+        parprint('-------------------------------------')
+        parprint('Zero-point energy: %.3f eV' % self.get_zero_point_energy())
+        parprint('Static dipole moment: %.3f D' % self.dipole_zero)
+        parprint('Maximum force on atom in `equilibrium`: %.4f eV/Å' % 
+                  self.force_zero)
+        parprint()
+
+    def get_spectrum(self, start=800, end=4000, npts=None, width=4, type='Gaussian', method='standard', direction='central'):
+        """Get infrared spectrum.
+
+        The method returns wavenumbers in cm^-1 with corresonding absolute infrared intensity.
+        Start and end point, and width of the Gaussian/Lorentzian should be given in cm^-1."""
+
+        self.type = type.lower()
+        assert self.type in ['gaussian', 'lorentzian']
+        if not npts: 
+            npts = (end-start)/width*10+1
+        frequencies = self.get_frequencies(method, direction).real
+        intensities=self.intensities
+        if type == 'lorentzian':
+            intensities = intensities*width*pi/2.
+        else:
+            sigma = width/2./sqrt(2.*log(2.))
+        #Make array with spectrum data
+        spectrum = np.empty(npts,np.float)
+        energies = np.empty(npts,np.float)
+        ediff = (end-start)/float(npts-1)
+        energies = np.arange(start, end+ediff/2, ediff)
+        for i, energy in enumerate(energies):
+            energies[i] = energy
+            if type == 'lorentzian':
+                spectrum[i] = (intensities*0.5*width/pi/((frequencies-energy)**2+0.25*width**2)).sum()
+            else:
+                spectrum[i] = (intensities*np.exp(-(frequencies - energy)**2/2./sigma**2)).sum()
+        return [energies, spectrum]
+
+    def write_spectra(self, out='ir-spectra.dat', start=800, end=4000, npts=None, width=10, type='Gaussian', method='standard', direction='central'):
+        """Write out infrared spectrum to file.
+
+        First column is the wavenumber in cm^-1, the second column the absolute infrared intensities, and
+        the third column the absorbance scaled so that data runs from 1 to 0. Start and end 
+        point, and width of the Gaussian/Lorentzian should be given in cm^-1."""
+        energies, spectrum = self.get_spectrum(start, end, npts, width, type, method, direction)
+
+        #Write out spectrum in file. First column is absolute intensities. 
+        #Second column is absorbance scaled so that data runs from 1 to 0
+        spectrum2 = 1. - spectrum/spectrum.max()
+        outdata = np.empty([len(energies), 3])
+        outdata.T[0] = energies
+        outdata.T[1] = spectrum
+        outdata.T[2] = spectrum2
+        fd = open(out, 'w')
+        for row in outdata:
+            fd.write('%.3f  %15.5e  %15.5e \n' % (row[0], row[1], row[2]) )
+        fd.close()
+        #np.savetxt(out, outdata, fmt='%.3f  %15.5e  %15.5e')
diff --git a/ase/io/__init__.py b/ase/io/__init__.py
new file mode 100644
index 0000000..541dd43
--- /dev/null
+++ b/ase/io/__init__.py
@@ -0,0 +1,626 @@
+import os
+import sys
+from tarfile import is_tarfile
+from zipfile import is_zipfile
+
+from ase.atoms import Atoms
+from ase.units import Bohr, Hartree
+from ase.io.trajectory import PickleTrajectory
+from ase.io.bundletrajectory import BundleTrajectory
+from ase.calculators.singlepoint import SinglePointCalculator
+
+__all__ = ['read', 'write', 'PickleTrajectory', 'BundleTrajectory']
+
+
+def read(filename, index=-1, format=None):
+    """Read Atoms object(s) from file.
+
+    filename: str
+        Name of the file to read from.
+    index: int or slice
+        If the file contains several configurations, the last configuration
+        will be returned by default.  Use index=n to get configuration
+        number n (counting from zero).
+    format: str
+        Used to specify the file-format.  If not given, the
+        file-format will be guessed by the *filetype* function.
+
+    Known formats:
+
+    =========================  ===========
+    format                     short name
+    =========================  ===========
+    GPAW restart-file          gpw
+    Dacapo netCDF output file  dacapo
+    Old ASE netCDF trajectory  nc
+    Virtual Nano Lab file      vnl
+    ASE pickle trajectory      traj
+    ASE bundle trajectory      bundle
+    GPAW text output           gpaw-text
+    CUBE file                  cube
+    XCrySDen Structure File    xsf
+    Dacapo text output         dacapo-text
+    XYZ-file                   xyz
+    VASP POSCAR/CONTCAR file   vasp
+    VASP OUTCAR file           vasp_out
+    SIESTA STRUCT file         struct_out
+    ABINIT input file          abinit
+    V_Sim ascii file           v_sim
+    Protein Data Bank          pdb
+    CIF-file                   cif
+    FHI-aims geometry file     aims
+    FHI-aims output file       aims_out
+    VTK XML Image Data         vti
+    VTK XML Structured Grid    vts
+    VTK XML Unstructured Grid  vtu
+    TURBOMOLE coord file       tmol
+    TURBOMOLE gradient file    tmol-gradient
+    exciting input             exi
+    AtomEye configuration      cfg
+    WIEN2k structure file      struct
+    DftbPlus input file        dftb
+    CASTEP geom file           cell
+    CASTEP output file         castep
+    CASTEP trajectory file     geom
+    ETSF format                etsf.nc
+    DFTBPlus GEN format        gen
+    CMR db/cmr-file            db
+    CMR db/cmr-file            cmr
+    LAMMPS dump file           lammps
+    =========================  ===========
+
+    """
+    if isinstance(filename, str):
+        p = filename.rfind('@')
+        if p != -1:
+            try:
+                index = string2index(filename[p + 1:])
+            except ValueError:
+                pass
+            else:
+                filename = filename[:p]
+
+    if isinstance(index, str):
+        index = string2index(index)
+
+    if format is None:
+        format = filetype(filename)
+
+    if format.startswith('gpw'):
+        import gpaw
+        r = gpaw.io.open(filename, 'r')
+        positions = r.get('CartesianPositions') * Bohr
+        numbers = r.get('AtomicNumbers')
+        cell = r.get('UnitCell') * Bohr
+        pbc = r.get('BoundaryConditions')
+        tags = r.get('Tags')
+        magmoms = r.get('MagneticMoments')
+        energy = r.get('PotentialEnergy') * Hartree
+
+        if r.has_array('CartesianForces'):
+            forces = r.get('CartesianForces') * Hartree / Bohr
+        else:
+            forces = None
+
+        atoms = Atoms(positions=positions,
+                      numbers=numbers,
+                      cell=cell,
+                      pbc=pbc)
+        if tags.any():
+            atoms.set_tags(tags)
+
+        if magmoms.any():
+            atoms.set_initial_magnetic_moments(magmoms)
+        else:
+            magmoms = None
+
+        atoms.calc = SinglePointCalculator(energy, forces, None, magmoms,
+                                           atoms)
+
+        return atoms
+
+    if format == 'castep':
+        from ase.io.castep import read_castep
+        return read_castep(filename, index)
+
+    if format == 'castep_cell':
+        import ase.io.castep
+        return ase.io.castep.read_cell(filename, index)
+
+    if format == 'castep_geom':
+        import ase.io.castep
+        return ase.io.castep.read_geom(filename, index)
+
+    if format == 'exi':
+        from ase.io.exciting import read_exciting
+        return read_exciting(filename, index)
+
+    if format == 'xyz':
+        from ase.io.xyz import read_xyz
+        return read_xyz(filename, index)
+
+    if format == 'traj':
+        from ase.io.trajectory import read_trajectory
+        return read_trajectory(filename, index)
+
+    if format == 'bundle':
+        from ase.io.bundletrajectory import read_bundletrajectory
+        return read_bundletrajectory(filename, index)
+
+    if format == 'cube':
+        from ase.io.cube import read_cube
+        return read_cube(filename, index)
+
+    if format == 'nc':
+        from ase.io.netcdf import read_netcdf
+        return read_netcdf(filename, index)
+
+    if format == 'gpaw-text':
+        from ase.io.gpawtext import read_gpaw_text
+        return read_gpaw_text(filename, index)
+
+    if format == 'dacapo-text':
+        from ase.io.dacapo import read_dacapo_text
+        return read_dacapo_text(filename)
+
+    if format == 'dacapo':
+        from ase.io.dacapo import read_dacapo
+        return read_dacapo(filename)
+
+    if format == 'xsf':
+        from ase.io.xsf import read_xsf
+        return read_xsf(filename, index)
+
+    if format == 'vasp':
+        from ase.io.vasp import read_vasp
+        return read_vasp(filename)
+
+    if format == 'vasp_out':
+        from ase.io.vasp import read_vasp_out
+        return read_vasp_out(filename, index)
+
+    if format == 'abinit':
+        from ase.io.abinit import read_abinit
+        return read_abinit(filename)
+
+    if format == 'v_sim':
+        from ase.io.v_sim import read_v_sim
+        return read_v_sim(filename)
+
+    if format == 'mol':
+        from ase.io.mol import read_mol
+        return read_mol(filename)
+
+    if format == 'pdb':
+        from ase.io.pdb import read_pdb
+        return read_pdb(filename, index)
+
+    if format == 'cif':
+        from ase.io.cif import read_cif
+        return read_cif(filename, index)
+
+    if format == 'struct':
+        from ase.io.wien2k import read_struct
+        return read_struct(filename)
+
+    if format == 'struct_out':
+        from ase.io.siesta import read_struct
+        return read_struct(filename)
+
+    if format == 'vti':
+        from ase.io.vtkxml import read_vti
+        return read_vti(filename)
+
+    if format == 'vts':
+        from ase.io.vtkxml import read_vts
+        return read_vts(filename)
+
+    if format == 'vtu':
+        from ase.io.vtkxml import read_vtu
+        return read_vtu(filename)
+
+    if format == 'aims':
+        from ase.io.aims import read_aims
+        return read_aims(filename)
+
+    if format == 'aims_out':
+        from ase.io.aims import read_aims_output
+        return read_aims_output(filename, index)
+
+    if format == 'iwm':
+        from ase.io.iwm import read_iwm
+        return read_iwm(filename)
+
+    if format == 'Cmdft':
+        from ase.io.cmdft import read_I_info
+        return read_I_info(filename)
+
+    if format == 'tmol':
+        from ase.io.turbomole import read_turbomole
+        return read_turbomole(filename)
+
+    if format == 'tmol-gradient':
+        from ase.io.turbomole import read_turbomole_gradient
+        return read_turbomole_gradient(filename)
+
+    if format == 'cfg':
+        from ase.io.cfg import read_cfg
+        return read_cfg(filename)
+
+    if format == 'dftb':
+        from ase.io.dftb import read_dftb
+        return read_dftb(filename)
+
+    if format == 'sdf':
+        from ase.io.sdf import read_sdf
+        return read_sdf(filename)
+
+    if format == 'etsf':
+        from ase.io.etsf import ETSFReader
+        return ETSFReader(filename).read_atoms()
+
+    if format == 'gen':
+        from ase.io.gen import read_gen
+        return read_gen(filename)
+
+    if format == 'db':
+        from ase.io.cmr_io import read_db
+        return read_db(filename, index)
+
+    if format == 'lammps':
+        from ase.io.lammps import read_lammps_dump
+        return read_lammps_dump(filename, index)
+
+    raise RuntimeError('File format descriptor '+format+' not recognized!')
+
+
+def write(filename, images, format=None, **kwargs):
+    """Write Atoms object(s) to file.
+
+    filename: str
+        Name of the file to write to.
+    images: Atoms object or list of Atoms objects
+        A single Atoms object or a list of Atoms objects.
+    format: str
+        Used to specify the file-format.  If not given, the
+        file-format will be taken from suffix of the filename.
+
+    The accepted output formats:
+
+    =========================  ===========
+    format                     short name
+    =========================  ===========
+    ASE pickle trajectory      traj
+    ASE bundle trajectory      bundle
+    CUBE file                  cube
+    XYZ-file                   xyz
+    VASP POSCAR/CONTCAR file   vasp
+    ABINIT input file          abinit
+    Protein Data Bank          pdb
+    CIF-file                   cif
+    XCrySDen Structure File    xsf
+    FHI-aims geometry file     aims
+    gOpenMol .plt file         plt
+    Python script              py
+    Encapsulated Postscript    eps
+    Portable Network Graphics  png
+    Persistance of Vision      pov
+    VTK XML Image Data         vti
+    VTK XML Structured Grid    vts
+    VTK XML Unstructured Grid  vtu
+    TURBOMOLE coord file       tmol
+    exciting                   exi
+    AtomEye configuration      cfg
+    WIEN2k structure file      struct
+    CASTEP cell file           cell
+    DftbPlus input file        dftb
+    ETSF                       etsf.nc
+    DFTBPlus GEN format        gen
+    CMR db/cmr-file            db
+    CMR db/cmr-file            cmr
+    =========================  ===========
+
+    The use of additional keywords is format specific.
+
+    The ``cube`` and ``plt`` formats accept (plt requires it) a ``data``
+    keyword, which can be used to write a 3D array to the file along
+    with the nuclei coordinates.
+
+    The ``vti``, ``vts`` and ``vtu`` formats are all specifically directed
+    for use with MayaVi, and the latter is designated for visualization of
+    the atoms whereas the two others are intended for volume data. Further,
+    it should be noted that the ``vti`` format is intended for orthogonal
+    unit cells as only the grid-spacing is stored, whereas the ``vts`` format
+    additionally stores the coordinates of each grid point, thus making it
+    useful for volume date in more general unit cells.
+
+    The ``eps``, ``png``, and ``pov`` formats are all graphics formats,
+    and accept the additional keywords:
+
+    rotation: str (default '')
+      The rotation angles, e.g. '45x,70y,90z'.
+
+    show_unit_cell: int (default 0)
+      Can be 0, 1, 2 to either not show, show, or show all of the unit cell.
+
+    radii: array or float (default 1.0)
+      An array of same length as the list of atoms indicating the sphere radii.
+      A single float specifies a uniform scaling of the default covalent radii.
+
+    bbox: 4 floats (default None)
+      Set the bounding box to (xll, yll, xur, yur) (lower left, upper right).
+
+    colors: array (default None)
+      An array of same length as the list of atoms, indicating the rgb color
+      code for each atom. Default is the jmol_colors of ase/data/colors.
+
+    scale: int (default 20)
+      Number of pixels per Angstrom.
+
+    For the ``pov`` graphics format, ``scale`` should not be specified.
+    The elements of the color array can additionally be strings, or 4
+    and 5 vectors for named colors, rgb + filter, and rgb + filter + transmit
+    specification. This format accepts the additional keywords:
+
+    ``run_povray``, ``display``, ``pause``, ``transparent``,
+    ``canvas_width``, ``canvas_height``, ``camera_dist``,
+    ``image_plane``, ``camera_type``, ``point_lights``,
+    ``area_light``, ``background``, ``textures``, ``celllinewidth``,
+    ``bondlinewidth``, ``bondatoms``
+    """
+
+    if format is None:
+        if filename == '-':
+            format = 'xyz'
+            filename = sys.stdout
+        elif 'POSCAR' in filename or 'CONTCAR' in filename:
+            format = 'vasp'
+        elif 'OUTCAR' in filename:
+            format = 'vasp_out'
+        elif filename.endswith('etsf.nc'):
+            format = 'etsf'
+        else:
+            suffix = filename.split('.')[-1]
+            format = {'cell':'castep_cell',
+                    }.get(suffix, suffix) # XXX this does not make sense
+            # Maybe like this:
+##             format = {'traj': 'trajectory',
+##                       'nc': 'netcdf',
+##                       'exi': 'exciting',
+##                       'in': 'aims',
+##                       'tmol': 'turbomole',
+##                       }.get(suffix, suffix)
+
+    if format == 'castep_cell':
+        from ase.io.castep import write_cell
+        write_cell(filename, images, **kwargs)
+        return
+    if format == 'exi':
+        from ase.io.exciting import write_exciting
+        write_exciting(filename, images)
+        return
+    if format == 'cif':
+        from ase.io.cif import write_cif
+        write_cif(filename, images)
+    if format == 'xyz':
+        from ase.io.xyz import write_xyz
+        write_xyz(filename, images)
+        return
+    if format == 'gen':
+        from ase.io.gen import write_gen
+        write_gen(filename, images)
+        return
+    elif format == 'in':
+        format = 'aims'
+    elif format == 'tmol':
+        from ase.io.turbomole import write_turbomole
+        write_turbomole(filename, images)
+        return
+    elif format == 'dftb':
+        from ase.io.dftb import write_dftb
+        write_dftb(filename, images)
+        return
+    elif format == 'struct':
+        from ase.io.wien2k import write_struct
+        write_struct(filename, images, **kwargs)
+        return
+    elif format == 'findsym':
+        from ase.io.findsym import write_findsym
+        write_findsym(filename, images)
+        return
+    elif format == 'etsf':
+        from ase.io.etsf import ETSFWriter
+        writer = ETSFWriter(filename)
+        if not isinstance(images, (list, tuple)):
+            images = [images]
+        writer.write_atoms(images[0])
+        writer.close()
+        return
+    elif format == 'db' or format == 'cmr':
+        from ase.io.cmr_io import write_db
+        return write_db(filename, images, **kwargs)
+
+    format = {'traj': 'trajectory',
+              'nc': 'netcdf',
+              'bundle': 'bundletrajectory'
+              }.get(format, format)
+    name = 'write_' + format
+
+    if format in ['vti', 'vts', 'vtu']:
+        format = 'vtkxml'
+
+    if format is None:
+        format = filetype(filename)
+
+    try:
+        write = getattr(__import__('ase.io.%s' % format, {}, {}, [name]), name)
+    except ImportError:
+        raise TypeError('Unknown format: "%s".' % format)
+
+    write(filename, images, **kwargs)
+
+
+def string2index(string):
+    if ':' not in string:
+        return int(string)
+    i = []
+    for s in string.split(':'):
+        if s == '':
+            i.append(None)
+        else:
+            i.append(int(s))
+    i += (3 - len(i)) * [None]
+    return slice(*i)
+
+
+def filetype(filename):
+    """Try to guess the type of the file."""
+    if os.path.isdir(filename):
+        # Potentially a BundleTrajectory
+        if BundleTrajectory.is_bundle(filename):
+            return 'bundle'
+        else:
+            raise IOError('Directory: ' + filename)
+
+    fileobj = open(filename)
+    s3 = fileobj.read(3)
+    if len(s3) == 0:
+        raise IOError('Empty file: ' + filename)
+
+    if filename.lower().endswith('.db') or filename.lower().endswith('.cmr'):
+        return 'db'
+
+    if is_tarfile(filename):
+        return 'gpw'
+
+    if s3 == 'CDF':
+        from ase.io.pupynere import NetCDFFile
+        nc = NetCDFFile(filename)
+        if 'number_of_dynamic_atoms' in nc.dimensions:
+            return 'dacapo'
+
+        history = nc.history
+        if history == 'GPAW restart file':
+            return 'gpw-nc'
+        if history == 'ASE trajectory':
+            return 'nc'
+        if history == 'Dacapo':
+            return 'dacapo'
+        if hasattr(nc, 'file_format') and nc.file_format.startswith('ETSF'):
+            return 'etsf'
+        raise IOError('Unknown netCDF file!')
+
+
+    if is_zipfile(filename):
+        return 'vnl'
+
+    fileobj.seek(0)
+    lines = fileobj.readlines(1000)
+
+    if lines[0].startswith('PickleTrajectory'):
+        return 'traj'
+
+    if lines[1].startswith('OUTER LOOP:') or filename.lower().endswith('.cube'):
+        return 'cube'
+
+    if '  ___ ___ ___ _ _ _  \n' in lines:
+        return 'gpaw-text'
+
+    if (' &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n'
+        in lines[:90]):
+        return 'dacapo-text'
+
+
+    for line in lines:
+        if line[0] != '#':
+            word = line.strip()
+            if word in ['ANIMSTEPS', 'CRYSTAL', 'SLAB', 'POLYMER', 'MOLECULE']:
+                return 'xsf'
+
+    filename_v = os.path.basename(filename)
+    if 'POSCAR' in filename_v or 'CONTCAR' in filename_v:
+        return 'vasp'
+
+    if 'OUTCAR' in filename_v:
+        return 'vasp_out'
+
+    if filename.lower().endswith('.exi'):
+        return 'exi'
+
+    if filename.lower().endswith('.mol'):
+        return 'mol'
+
+    if filename.lower().endswith('.pdb'):
+        return 'pdb'
+
+    if filename.lower().endswith('.cif'):
+        return 'cif'
+
+    if filename.lower().endswith('.struct'):
+        return 'struct'
+
+    if filename.lower().endswith('.struct_out'):
+        return 'struct_out'
+
+    for line in lines:
+        if 'Invoking FHI-aims ...' in line:
+            return 'aims_out'
+        if 'atom' in line:
+            data = line.split()
+            try:
+                a = Atoms(symbols=[data[4]],positions = [[float(data[1]),float(data[2]),float(data[3])]])
+                return 'aims'
+            except:
+                pass
+
+    if filename.lower().endswith('.in'):
+        return 'aims'
+
+    if filename.lower().endswith('.cfg'):
+        return 'cfg'
+
+    if os.path.split(filename)[1] == 'atoms.dat':
+        return 'iwm'
+
+    if filename.endswith('I_info'):
+        return 'Cmdft'
+
+    if lines[0].startswith('$coord') or os.path.basename(filename) == 'coord':
+        return 'tmol'
+
+    if lines[0].startswith('$grad') or os.path.basename(filename) == 'gradient':
+        return 'tmol-gradient'
+
+    if lines[0].startswith('Geometry'):
+        return 'dftb'
+
+    if filename.lower().endswith('.geom'):
+        return 'castep_geom'
+
+    if filename.lower().endswith('.castep'):
+        return 'castep'
+
+    if filename.lower().endswith('.cell'):
+        return 'castep_cell'
+    if s3 == '<?x':
+        from ase.io.vtkxml import probe_vtkxml
+        xmltype = probe_vtkxml(filename)
+        if xmltype == 'ImageData':
+            return 'vti'
+        elif xmltype == 'StructuredGrid':
+            return 'vts'
+        elif xmltype == 'UnstructuredGrid':
+            return 'vtu'
+        elif xmltype is not None:
+            raise IOError('Unknown VTK XML file!')
+
+    if filename.lower().endswith('.sdf'):
+        return 'sdf'
+
+    if filename.lower().endswith('.gen'):
+        return 'gen'
+
+    if 'ITEM: TIMESTEP\n' in lines:
+        return 'lammps'
+
+    return 'xyz'
diff --git a/ase/io/abinit.py b/ase/io/abinit.py
new file mode 100644
index 0000000..b27b448
--- /dev/null
+++ b/ase/io/abinit.py
@@ -0,0 +1,201 @@
+"""
+This module contains functionality for reading an ASE
+Atoms object in ABINIT input format.
+
+"""
+
+import os
+
+def read_abinit(filename='abinit.in'):
+    """Import ABINIT input file.
+
+    Reads cell, atom positions, etc. from abinit input file
+    """
+
+    from ase import Atoms, units
+
+    if isinstance(filename, str):
+        f = open(filename)
+    else: # Assume it's a file-like object
+        f = filename
+
+    lines = f.readlines()
+    if type(filename) == str:
+        f.close()
+
+    full_file = ''
+    for line in lines:
+        if '#' in line:
+            meat, comment = line.split('#')
+        else:
+            meat = line
+        full_file = full_file + meat + ' '
+
+    full_file.strip()
+    tokens = full_file.lower().split()
+
+    # note that the file can not be scanned sequentially
+
+    index = tokens.index("acell")
+    unit = 1.0
+    if(tokens[index+4].lower()[:3] != 'ang'):
+        unit = units.Bohr
+    acell = [unit*float(tokens[index+1]),
+             unit*float(tokens[index+2]),
+             unit*float(tokens[index+3])]
+
+    index = tokens.index("natom")
+    natom = int(tokens[index+1])
+
+    index = tokens.index("ntypat")
+    ntypat = int(tokens[index+1])
+
+    index = tokens.index("typat")
+    typat = []
+    for i in range(natom):
+        typat.append(int(tokens[index+1+i]))
+
+    index = tokens.index("znucl")
+    znucl = []
+    for i in range(ntypat):
+        znucl.append(int(tokens[index+1+i]))
+
+    index = tokens.index("rprim")
+    rprim = []
+    for i in range(3):
+        rprim.append([acell[i]*float(tokens[index+3*i+1]),
+                      acell[i]*float(tokens[index+3*i+2]),
+                      acell[i]*float(tokens[index+3*i+3])])
+
+    # create a list with the atomic numbers
+    numbers = []
+    for i in range(natom):
+        ii = typat[i] - 1
+        numbers.append(znucl[ii])
+
+    # now the positions of the atoms
+    if "xred" in tokens:
+        index = tokens.index("xred")
+        xred = []
+        for i in range(natom):
+            xred.append([float(tokens[index+3*i+1]),
+                         float(tokens[index+3*i+2]),
+                         float(tokens[index+3*i+3])])
+        atoms = Atoms(cell=rprim, scaled_positions=xred, numbers=numbers)
+        return atoms
+
+    index = None
+    if "xcart" in tokens:
+        index = tokens.index("xcart")
+        unit = units.Bohr
+    elif "xangs" in tokens:
+        unit = 1.0
+        index = tokens.index("xangs")
+
+    if(index != None):
+        xangs = []
+        for i in range(natom):
+            xangs.append([unit*float(tokens[index+3*i+1]),
+                          unit*float(tokens[index+3*i+2]),
+                          unit*float(tokens[index+3*i+3])])
+        atoms = Atoms(cell=rprim, positions=xangs, numbers=numbers)
+        return atoms
+
+    raise IOError("No xred, xcart, or xangs keyword in abinit input file")
+
+
+def write_abinit(filename, atoms, cartesian=False, long_format=True):
+    """Method to write abinit input files."""
+
+    import numpy as np
+    from ase import Atoms, data
+
+    if isinstance(filename, str):
+        f = open(filename, 'w')
+    else: # Assume it's a 'file-like object'
+        f = filename
+
+    if isinstance(atoms, (list, tuple)):
+        if len(atoms) > 1:
+            raise RuntimeError("Don't know how to save more than "+
+                            "one image to input")
+        else:
+            atoms = atoms[0]
+
+    # Write atom positions in scaled or cartesian coordinates
+    if cartesian:
+        coord = atoms.get_positions()
+    else:
+        coord = atoms.get_scaled_positions()
+
+    # let us order the atoms according to chemical symbol
+    ind = np.argsort(atoms.get_chemical_symbols())
+    symbols = np.array(atoms.get_chemical_symbols())[ind]
+    coord = coord[ind]
+
+    # and now we count how many atoms of which type we have
+    sc = []
+    psym = symbols[0]
+    count = 0
+    for sym in symbols:
+        if sym != psym:
+            sc.append((psym, count))
+            psym = sym
+            count = 1
+        else:
+            count += 1
+    sc.append((psym, count))
+
+    f.write('\n# Definition of the atom types\n')
+    f.write("ntypat  " + str(len(sc)) + "\n")
+    f.write("znucl  ")
+    for specie in sc:
+        f.write(str(data.atomic_numbers[specie[0]]) + " ")
+    f.write('\n')
+
+    f.write('\n# Definition of the atoms\n')
+    f.write('natom  ' + str(len(symbols)) + '\n')
+    f.write('typat  ')
+    typat = 1
+    for specie in sc:
+        for natom in range(specie[1]):
+            f.write(str(typat) + ' ')
+        typat = typat + 1
+    f.write('\n')
+
+    f.write('\n# Definition of the unit cell\n')
+    f.write('acell\n')
+    f.write('%.14f %.14f %.14f Angstrom\n' %  (1.0, 1.0, 1.0))
+    f.write('\n')
+    f.write('rprim\n')
+    if long_format:
+        latt_form = ' %21.16f'
+    else:
+        latt_form = ' %11.6f'
+
+    for vec in atoms.get_cell():
+        f.write(' ')
+        for el in vec:
+            f.write(latt_form % el)
+        f.write('\n')
+    f.write('\n')
+
+    # Write atom positions in scaled or cartesian coordinates
+    if cartesian:
+        f.write('xangst\n')
+    else:
+        f.write('xred\n')
+
+    if long_format:
+        cform = ' %19.16f'
+    else:
+        cform = ' %9.6f'
+
+    for iatom, atom in enumerate(coord):
+        f.write(' ')
+        for dcoord in atom:
+            f.write(cform % dcoord)
+        f.write('\n')
+
+    if type(filename) == str:
+        f.close()
diff --git a/ase/io/aims.py b/ase/io/aims.py
new file mode 100644
index 0000000..373a874
--- /dev/null
+++ b/ase/io/aims.py
@@ -0,0 +1,345 @@
+
+def read_aims(filename):
+    """Import FHI-aims geometry type files.
+
+    Reads unitcell, atom positions and constraints from
+    a geometry.in file.
+    """
+
+    from ase import Atoms
+    from ase.constraints import FixAtoms, FixCartesian
+    import numpy as np
+
+    atoms = Atoms()
+    fd = open(filename, 'r')
+    lines = fd.readlines()
+    fd.close()
+    positions = []
+    cell = []
+    symbols = []
+    fix = []
+    fix_cart = []
+    xyz = np.array([0, 0, 0])
+    i = -1
+    n_periodic = -1
+    periodic = np.array([False, False, False])
+    for n, line in enumerate(lines):
+        inp = line.split()
+        if inp == []:
+            continue
+        if inp[0] == 'atom':
+            if xyz.all():
+                fix.append(i)
+            elif xyz.any():
+                fix_cart.append(FixCartesian(i, xyz))
+            floatvect = float(inp[1]), float(inp[2]), float(inp[3])
+            positions.append(floatvect)
+            symbols.append(inp[-1])
+            i += 1
+            xyz = np.array([0, 0, 0])
+        elif inp[0] == 'lattice_vector':
+            floatvect = float(inp[1]), float(inp[2]), float(inp[3])
+            cell.append(floatvect)
+            n_periodic = n_periodic + 1
+            periodic[n_periodic] = True
+        if inp[0] == 'constrain_relaxation':
+            if inp[1] == '.true.':
+                fix.append(i)
+            elif inp[1] == 'x':
+                xyz[0] = 1
+            elif inp[1] == 'y':
+                xyz[1] = 1
+            elif inp[1] == 'z':
+                xyz[2] = 1
+    if xyz.all():
+        fix.append(i)
+    elif xyz.any():
+        fix_cart.append(FixCartesian(i, xyz))
+    atoms = Atoms(symbols, positions)
+    if periodic.all():
+        atoms.set_cell(cell)
+        atoms.set_pbc(periodic)
+    if len(fix):
+        atoms.set_constraint([FixAtoms(indices=fix)]+fix_cart)
+    else:
+        atoms.set_constraint(fix_cart)
+    return atoms
+
+def write_aims(filename, atoms):
+    """Method to write FHI-aims geometry files.
+
+    Writes the atoms positions and constraints (only FixAtoms is
+    supported at the moment). 
+    """
+
+    from ase.constraints import FixAtoms, FixCartesian
+    import numpy as np
+
+    if isinstance(atoms, (list, tuple)):
+        if len(atoms) > 1:
+            raise RuntimeError("Don't know how to save more than "+
+                               "one image to FHI-aims input")
+        else:
+            atoms = atoms[0]
+
+    fd = open(filename, 'w')
+    fd.write('#=======================================================\n')
+    fd.write('#FHI-aims file: '+filename+'\n')
+    fd.write('#Created using the Atomic Simulation Environment (ASE)\n')
+    fd.write('#=======================================================\n')
+    i = 0
+    if atoms.get_pbc().any():
+        for n, vector in enumerate(atoms.get_cell()):
+            fd.write('lattice_vector ')
+            for i in range(3):
+                fd.write('%16.16f ' % vector[i])
+            fd.write('\n')
+    fix_cart = np.zeros([len(atoms),3]) 
+
+    if atoms.constraints:
+        for constr in atoms.constraints:
+            if isinstance(constr, FixAtoms):
+                fix_cart[constr.index] = [1,1,1]
+            elif isinstance(constr, FixCartesian):
+                fix_cart[constr.a] = -constr.mask+1
+
+    for i, atom in enumerate(atoms):
+        fd.write('atom ')
+        for pos in atom.position:
+            fd.write('%16.16f ' % pos)
+        fd.write(atom.symbol)
+        fd.write('\n')
+# (1) all coords are constrained:
+        if fix_cart[i].all():
+            fd.write('constrain_relaxation .true.\n')
+# (2) some coords are constrained:
+        elif fix_cart[i].any():
+            xyz = fix_cart[i]
+            for n in range(3):
+                if xyz[n]:
+                    fd.write('constrain_relaxation %s\n' % 'xyz'[n])
+        if atom.charge:
+            fd.write('initial_charge %16.6f\n' % atom.charge)
+        if atom.magmom:
+            fd.write('initial_moment %16.6f\n' % atom.magmom)
+# except KeyError:
+#     continue
+
+def read_energy(filename):
+    for line in open(filename, 'r'):
+        if line.startswith('  | Total energy corrected'):
+            E = float(line.split()[-2])
+    return E
+
+def read_aims_output(filename, index = -1):
+    """  Import FHI-aims output files with all data available, i.e. relaxations, 
+    MD information, force information etc etc etc. """
+    from ase import Atoms, Atom 
+    from ase.calculators.singlepoint import SinglePointCalculator
+    from ase.units import Ang, fs
+    from ase.constraints import FixAtoms, FixCartesian
+    molecular_dynamics = False
+    fd = open(filename, 'r')
+    cell = []
+    images = []
+    fix = []
+    fix_cart = []    
+    n_periodic = -1
+    f = None
+    pbc = False
+    found_aims_calculator = False
+    v_unit = Ang/(1000.0*fs)
+    while True:
+        line = fd.readline()
+        if not line:
+            break
+        if "List of parameters used to initialize the calculator:" in line:
+            fd.readline()
+            calc = read_aims_calculator(fd)
+            calc.out = filename
+            found_aims_calculator = True
+        if "Number of atoms" in line:
+            inp = line.split()
+            n_atoms = int(inp[5])
+        if "| Unit cell:" in line:
+            if not pbc:
+                pbc = True
+                for i in range(3):
+                    inp = fd.readline().split()
+                    cell.append([inp[1],inp[2],inp[3]])
+        if "Found relaxation constraint for atom" in line:
+            xyz = [0, 0, 0]
+            ind = int(line.split()[5][:-1])-1
+            if "All coordinates fixed" in line:
+                if ind not in fix:
+                    fix.append(ind)
+            if "coordinate fixed" in line:
+                coord = line.split()[6]
+                constr_ind = 0
+                if coord == 'x':
+                    xyz[0] = 1
+                elif coord == 'y':
+                    xyz[1] = 1
+                elif coord == 'z':
+                    xyz[2] = 1
+                keep = True
+                for n,c in enumerate(fix_cart):
+                    if ind == c.a:
+                        keep = False
+                        constr_ind = n
+                if keep:
+                    fix_cart.append(FixCartesian(ind, xyz))
+                else:
+                    fix_cart[n].mask[xyz.index(1)] = 0
+        if "Atomic structure:" in line and not molecular_dynamics:
+            fd.readline()
+            atoms = Atoms()
+            for i in range(n_atoms):
+                inp = fd.readline().split()
+                atoms.append(Atom(inp[3],(inp[4],inp[5],inp[6])))
+        if "Complete information for previous time-step:" in line:
+            molecular_dynamics = True
+        if "Updated atomic structure:" in line and not molecular_dynamics:
+            fd.readline()
+            atoms = Atoms()
+            velocities = []
+            for i in range(n_atoms):
+                inp = fd.readline().split()
+                if 'lattice_vector' in inp[0]:
+                    cell = []
+                    for i in range(3):
+                        cell += [[float(inp[1]),float(inp[2]),float(inp[3])]]
+                        inp = fd.readline().split()
+                    atoms.set_cell(cell)
+                    inp = fd.readline().split()
+                atoms.append(Atom(inp[4],(inp[1],inp[2],inp[3])))  
+                if molecular_dynamics:
+                    inp = fd.readline().split()
+        if "Atomic structure (and velocities)" in line:
+            fd.readline()
+            atoms = Atoms()
+            velocities = []
+            for i in range(n_atoms):
+                inp = fd.readline().split()
+                atoms.append(Atom(inp[4],(inp[1],inp[2],inp[3])))  
+                inp = fd.readline().split()
+                velocities += [[float(inp[1])*v_unit,float(inp[2])*v_unit,float(inp[3])*v_unit]]
+            atoms.set_velocities(velocities)
+            if len(fix):
+                atoms.set_constraint([FixAtoms(indices=fix)]+fix_cart)
+            else:
+                atoms.set_constraint(fix_cart)
+            images.append(atoms)
+        if "Total atomic forces" in line:
+            f = []
+            for i in range(n_atoms):
+                inp = fd.readline().split()
+                f.append([float(inp[2]),float(inp[3]),float(inp[4])])
+            if not found_aims_calculator:
+                e = images[-1].get_potential_energy()
+                images[-1].set_calculator(SinglePointCalculator(e,f,None,None,atoms))
+            e = None
+            f = None
+        if "Total energy corrected" in line:
+            e = float(line.split()[5])
+            if pbc:
+                atoms.set_cell(cell)
+                atoms.pbc = True
+            if not found_aims_calculator:
+                atoms.set_calculator(SinglePointCalculator(e,None,None,None,atoms))
+            if not molecular_dynamics: 
+                if len(fix):
+                    atoms.set_constraint([FixAtoms(indices=fix)]+fix_cart)
+                else:
+                    atoms.set_constraint(fix_cart)
+                images.append(atoms)
+            e = None
+            if found_aims_calculator:
+                calc.set_results(images[-1])
+                images[-1].set_calculator(calc)
+    fd.close()
+    if molecular_dynamics:
+        images = images[1:]
+
+    # return requested images, code borrowed from ase/io/trajectory.py
+    if isinstance(index, int):
+        return images[index]
+    else:
+        step = index.step or 1
+        if step > 0:
+            start = index.start or 0
+            if start < 0:
+                start += len(images)
+            stop = index.stop or len(images)
+            if stop < 0:
+                stop += len(images)
+        else:
+            if index.start is None:
+                start = len(images) - 1
+            else:
+                start = index.start
+                if start < 0:
+                    start += len(images)
+            if index.stop is None:
+                stop = -1
+            else:
+                stop = index.stop
+                if stop < 0:
+                    stop += len(images)
+        return [images[i] for i in range(start, stop, step)]
+
+def read_aims_calculator(file):
+    """  found instructions for building an FHI-aims calculator in the output file, 
+    read its specifications and return it. """
+    from ase.calculators.aims import Aims
+    calc = Aims()
+    while True:
+        line = file.readline()
+        if "=======================================================" in line:
+            break
+        else:
+            args = line.split()
+            key = '#'
+            if len(args) > 0:
+                key = args[0]
+            if key == '#':
+                comment = True   
+            elif calc.float_params.has_key(key):
+                calc.float_params[key] = float(args[1])
+            elif calc.exp_params.has_key(key):
+                calc.exp_params[key] = float(args[1])
+            elif calc.string_params.has_key(key):
+                calc.string_params[key] = args[1]
+                if len(args) > 2:
+                    for s in args[2:]:
+                        calc.string_params[key] += " "+s
+            elif calc.int_params.has_key(key):
+                calc.int_params[key] = int(args[1])
+            elif calc.bool_params.has_key(key):
+                try:
+                    calc.bool_params[key] = bool(args[1])
+                except:
+                    if key == 'vdw_correction_hirshfeld':
+                        calc.bool_params[key] = True
+            elif calc.list_params.has_key(key):
+                if key == 'output':
+                    # build output string from args:
+                    out_option = ''
+                    for arg in args[1:]:
+                        out_option +=str(arg)+' '
+                    if calc.list_params['output'] is not None:
+                        calc.list_params['output'] += [out_option]
+                    else:
+                        calc.list_params['output'] = [out_option]
+                else:
+                    calc.list_params[key] = list(args[1:])
+            elif '#' in key:
+                key = key[1:]
+                if calc.input_parameters.has_key(key):
+                    calc.input_parameters[key] = args[1]
+                    if len(args) > 2: 
+                        for s in args[2:]:
+                            calc.input_parameters[key] += " "+s                
+            else:
+                raise TypeError('FHI-aims keyword not defined in ASE: ' + key + '. Please check.')
+    return calc
diff --git a/ase/io/ascii.py b/ase/io/ascii.py
new file mode 100644
index 0000000..909fa5a
--- /dev/null
+++ b/ase/io/ascii.py
@@ -0,0 +1,19 @@
+from ase.atoms import Atoms
+
+
+def write_ascii(fileobj, images):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+        fileobj.write('atoms = ')
+    else:
+        fileobj.write('images = [')
+
+    symbols = images[0].get_chemical_symbols()
+    natoms = len(symbols)
+    for atoms in images:
+        fileobj.write('%d\n\n' % natoms)
+        for s, (x, y, z) in zip(symbols, atoms.get_positions()):
+            fileobj.write('%-2s %22.15f %22.15f %22.15f\n' % (s, x, y, z))
diff --git a/ase/io/bader.py b/ase/io/bader.py
new file mode 100644
index 0000000..bfacf07
--- /dev/null
+++ b/ase/io/bader.py
@@ -0,0 +1,46 @@
+import numpy as np
+from ase.units import Bohr
+
+def attach_charges(atoms, fileobj='ACF.dat', displacement=1e-4):
+    """Attach the charges from the fileobj to the Atoms."""
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    sep = '---------------'
+    i = 0 # Counter for the lines
+    k = 0 # Counter of sep
+    assume6columns = False
+    for line in fileobj:
+        if line[0] == '\n': # check if there is an empty line in the 
+            i -= 1          # head of ACF.dat file
+        if i == 0:
+            headings = line
+            if 'BADER' in headings.split():
+                j = headings.split().index('BADER')
+            elif 'CHARGE' in headings.split():
+                j = headings.split().index('CHARGE')
+            else:
+                print 'Can\'t find keyword "BADER" or "CHARGE".' \
+                +' Assuming the ACF.dat file has 6 columns.'
+                j = 4
+                assume6columns = True
+        if sep in line: # Stop at last seperator line
+            if k == 1:
+                break
+            k += 1
+        if not i > 1:
+            pass
+        else:
+            words = line.split()
+            if assume6columns is True:
+                if len(words) != 6:
+                    raise IOError('Number of columns in ACF file incorrect!\n'
+                                  'Check that Bader program version >= 0.25')
+                
+            atom = atoms[int(words[0]) - 1]
+            atom.charge = atom.number - float(words[j])
+            if displacement is not None: # check if the atom positions match
+                xyz = np.array([float(w) for w in words[1:4]]) * Bohr
+                assert np.linalg.norm(atom.position - xyz) < displacement
+        i += 1
+
diff --git a/ase/io/bundletrajectory.py b/ase/io/bundletrajectory.py
new file mode 100644
index 0000000..7cba3f6
--- /dev/null
+++ b/ase/io/bundletrajectory.py
@@ -0,0 +1,846 @@
+"""bundletrajectory - a module for I/O from large MD simulations.
+
+The BundleTrajectory class writes trajectory into a directory with the
+following structure::
+
+    filename.bundle (dir)
+        metadata.pickle        Data about the file format, and about which
+                               data is present.
+        state.pickle           The number of frames
+        F0 (dir)               Frame number 0
+            small.pickle       Small data structures in a dictionary
+                               (pbc, cell, ...)
+            numbers.pickle     Atomic numbers
+            positions.pickle   Positions
+            momenta.pickle     Momenta
+            ...
+        F1 (dir)
+"""
+
+import ase.parallel 
+from ase.parallel import paropen
+from ase.calculators.singlepoint import SinglePointCalculator
+import numpy as np
+import os
+import shutil
+import time
+import cPickle as pickle
+
+class BundleTrajectory:
+    """Reads and writes atoms into a .bundle directory.
+
+    The BundleTrajectory is an alternative way of storing
+    trajectories, intended for large-scale molecular dynamics
+    simulations, where a single flat file becomes unwieldy.  Instead,
+    the data is stored in directory, a 'bundle' (the name bundle is
+    inspired from bundles in Mac OS, which are really just directories
+    the user is supposed to think of as a single file-like unit).
+
+    Parameters:
+
+    filename:
+        The name of the directory.  Preferably ending in .bundle.
+
+    mode (optional):
+        The file opening mode.  'r' means open for reading, 'w' for
+        writing and 'a' for appending.  Default: 'r'.  If opening in
+        write mode, and the filename already exists, the old file is
+        renamed to .bak (any old .bak file is deleted), except if the
+        existing file is empty.
+
+    atoms (optional):
+        The atoms that will be written.  Can only be specified in
+        write or append mode.  If not specified, the atoms must be
+        given as an argument to the .write() method instead.
+
+    backup=True:
+        Use backup=False to disable renaming of an existing file.
+    """
+    slavelog = True  # Log from all nodes
+    def __init__(self, filename, mode='r', atoms=None, backup=True):
+        self.state = 'constructing'
+        self.filename = filename
+        self.pre_observers = []   # Callback functions before write is performed
+        self.post_observers = []  # Callback functions after write is performed
+        self.master = ase.parallel.rank == 0
+        self.extra_data = []
+        self._set_defaults()
+        self._set_backend()
+        if mode == 'r':
+            if atoms is not None:
+                raise ValueError("You cannot specify atoms in read mode.")
+            self._open_read()
+        elif mode == 'w':
+            self._open_write(atoms, backup)
+        elif mode == 'a':
+            self._open_append(atoms)
+
+    def _set_defaults(self):
+        "Set default values for internal parameters."
+        self.version = 1
+        self.subtype = 'normal'
+        self.backend_name = 'pickle'
+        self.datatypes = {'positions': True,
+                          'numbers': 'once',
+                          'tags': 'once',
+                          'masses': 'once',
+                          'momenta': True,
+                          'forces': True,
+                          'energy': True,
+                          'energies': False,
+                          'stress': False,
+                          'magmoms': True
+                          }
+
+    def _set_backend(self, backend=None):
+        """Set the backed doing the actual I/O."""
+        if backend is not None:
+            self.backend_name = backend
+        if self.backend_name == 'pickle':
+            self.backend = PickleBundleBackend(self.master)
+        else:
+            raise NotImplementedError(
+                "This version of ASE cannot use BundleTrajectory with backend '%s'"
+                % self.backend_name)
+
+    def write(self, atoms=None):
+        """Write the atoms to the file.
+
+        If the atoms argument is not given, the atoms object specified
+        when creating the trajectory object is used.
+        """
+        # Check that we are in write mode
+        if self.state == 'prewrite':
+            self.state = 'write'
+            assert self.nframes == 0
+        elif self.state != 'write':
+            raise RuntimeError('Cannot write in ' + self.state + ' mode.')
+
+        if atoms is None:
+            atoms = self.atoms
+
+        if hasattr(atoms, 'interpolate'):
+            # seems to be a NEB
+            self.log('Beginning to write NEB data')
+            neb = atoms
+            assert not neb.parallel
+            try:
+                neb.get_energies_and_forces(all=True)
+            except AttributeError:
+                pass
+            for image in neb.images:
+                self.write(image)
+            self.log('Done writing NEB data')
+            return
+
+        # OK, it is a real atoms object.  Write it.
+        self._call_observers(self.pre_observers)
+        self.log("Beginning to write frame " + str(self.nframes))
+        framedir = self._make_framedir(self.nframes)
+
+        # Check which data should be written the first time:
+        # Modify datatypes so any element of type 'once' becomes true
+        # for the first frame but false for subsequent frames.
+        datatypes = {}
+        for k, v in self.datatypes.items():
+            if v == 'once':
+                v = (self.nframes == 0)
+            datatypes[k] = v
+
+        # Write 'small' data structures.  They are written jointly.
+        smalldata = {'pbc': atoms.get_pbc(),
+                     'cell': atoms.get_cell(),
+                     'natoms': atoms.get_number_of_atoms(),
+                     'constraints': atoms.constraints,
+                     }
+        if datatypes.get('energy'):
+            try:
+                smalldata['energy'] = atoms.get_potential_energy()
+            except (RuntimeError, NotImplementedError):
+                self.datatypes['energy'] = False
+        if datatypes.get('stress'):
+            try:
+                smalldata['stress'] = atoms.get_stress()
+            except NotImplementedError:
+                self.datatypes['stress'] = False
+        self.backend.write_small(framedir, smalldata)
+        
+        # Write the large arrays.
+        if datatypes.get('positions'):
+            self.backend.write(framedir, 'positions', atoms.get_positions())
+        if datatypes.get('numbers'):
+            self.backend.write(framedir, 'numbers', atoms.get_atomic_numbers())
+        if datatypes.get('tags'):
+            if atoms.has('tags'):
+                self.backend.write(framedir, 'tags', atoms.get_tags())
+            else:
+                self.datatypes['tags'] = False
+        if datatypes.get('masses'):
+            if atoms.has('masses'):
+                self.backend.write(framedir, 'masses', atoms.get_masses())
+            else:
+                self.datatypes['masses'] = False
+        if datatypes.get('momenta'):
+            if atoms.has('momenta'):
+                self.backend.write(framedir, 'momenta', atoms.get_momenta())
+            else:
+                self.datatypes['momenta'] = False
+        if datatypes.get('magmoms'):
+            if atoms.has('magmoms'):
+                self.backend.write(framedir, 'magmoms', atoms.get_magmoms())
+            else:
+                self.datatypes['magmoms'] = False
+        if datatypes.get('forces'):
+            try:
+                x = atoms.get_forces()
+            except (RuntimeError, NotImplementedError):
+                self.datatypes['forces'] = False
+            else:
+                self.backend.write(framedir, 'forces', x) 
+                del x
+        if datatypes.get('energies'):
+            try:
+                x = atoms.get_potential_energies()
+            except (RuntimeError, NotImplementedError):
+                self.datatypes['energies'] = False
+            else:
+                self.backend.write(framedir, 'energies', x) 
+                del x
+        # Write any extra data
+        for (label, source, once) in self.extra_data:
+            if self.nframes == 0 or not once:
+                if source is not None:
+                    x = source()
+                else:
+                    x = atoms.arrays[label]
+                self.backend.write(framedir, label, x)
+                del x
+                if once:
+                    self.datatypes[label] = 'once'
+                else:
+                    self.datatypes[label] = True
+        # Finally, write metadata if it is the first frame
+        if self.nframes == 0:
+            metadata = {'datatypes': self.datatypes}
+            self._write_metadata(metadata)
+        self._write_nframes(self.nframes + 1)
+        self._call_observers(self.post_observers)
+        self.log("Done writing frame " + str(self.nframes))
+        self.nframes += 1
+
+    def select_data(self, data, value):
+        """Selects if a given data type should be written.
+
+        Data can be written in every frame (specify True), in the
+        first frame only (specify 'only') or not at all (specify
+        False).  Not all data types support the 'only' keyword, if not
+        supported it is interpreted as True.
+
+        The following data types are supported, the letter in parenthesis
+        indicates the default:
+
+        positions (T), numbers (O), tags (O), masses (O), momenta (T),
+        forces (T), energy (T), energies (F), stress (F), magmoms (T)
+
+        If a given property is not present during the first write, it
+        will be not be saved at all.
+        """
+        if value not in (True, False, 'once'):
+            raise ValueError("Unknown write mode")
+        if data not in self.datatypes:
+            raise ValueError("Unsupported data type: " + data)
+        self.datatypes[data] = value
+
+    def set_extra_data(self, name, source=None, once=False):
+        """Adds extra data to be written.
+
+        Parameters:
+        name:  The name of the data.
+
+        source (optional): If specified, a callable object returning
+        the data to be written.  If not specified it is instead
+        assumed that the atoms contains the data as an array of the
+        same name.
+
+        once (optional): If specified and True, the data will only be
+        written to the first frame.
+        """
+        self.extra_data.append((name, source, once))
+        
+    def close(self):
+        "Closes the trajectory."
+        self.state = 'closed'
+        lf = getattr(self, 'logfile', None)
+        if lf is not None:
+            lf.close()
+            del self.logfile
+            
+    def log(self, text):
+        """Write to the log file in the bundle.
+
+        Logging is only possible in write/append mode.
+
+        This function is mainly for internal use, but can also be called by
+        the user.
+        """
+        if not (self.master or self.slavelog):
+            return
+        text = time.asctime() + ': ' + text
+        if hasattr(self, "logfile"):
+            # Logging enabled
+            if self.logfile is None:
+                # Logfile not yet open
+                try:
+                    self.logdata.append(text)
+                except AttributeError:
+                    self.logdata = [text]
+            else:
+                self.logfile.write(text + '\n')
+                self.logfile.flush()
+        else:
+            raise RuntimeError("Cannot write to log file in mode " + self.state)
+
+    # __getitem__ is the main reading method.
+    def __getitem__(self, n):
+        return self._read(n)
+
+    def _read(self, n):
+        "Read an atoms object from the BundleTrajectory."
+        if self.state != 'read':
+            raise IOError('Cannot read in %s mode' % (self.state,))
+        if n < 0:
+            n += self.nframes
+        if n < 0 or n >= self.nframes:
+            raise IndexError('Trajectory index %d out of range [0, %d['
+                             % (n, self.nframes))
+
+        framedir = os.path.join(self.filename, 'F' + str(n))
+        framezero = os.path.join(self.filename, 'F0')
+        smalldata = self.backend.read_small(framedir)
+        data = {}
+        data['pbc'] = smalldata['pbc']
+        data['cell'] = smalldata['cell']
+        data['constraint'] = smalldata['constraints']
+        if self.subtype == 'split':
+            self.backend.set_fragments(smalldata['fragments'])
+            atom_id = self.backend.read_split(framedir, 'ID')
+        else:
+            atom_id = None
+        atoms = ase.Atoms(**data)
+        natoms = smalldata['natoms']
+        for name in ('positions', 'numbers', 'tags', 'masses',
+                     'momenta'):
+            if self.datatypes.get(name):
+                atoms.arrays[name] = self._read_data(framezero, framedir,
+                                                     name, atom_id)
+                assert len(atoms.arrays[name]) == natoms
+                
+        # Create the atoms object
+        if self.datatypes.get('energy'):
+            if self.datatypes.get('forces'):
+                forces = self.backend.read(framedir, 'forces')
+            else:
+                forces = None
+            if self.datatypes.get('magmoms'):
+                magmoms = self.backend.read(framedir, 'magmoms')
+            else:
+                magmoms = None
+            calc = SinglePointCalculator(smalldata.get('energy'),
+                                         forces,
+                                         smalldata.get('stress'),
+                                         magmoms, atoms)
+            atoms.set_calculator(calc)
+        return atoms
+
+    def read_extra_data(self, name, n=0):
+        """Read extra data stored alongside the atoms.
+        
+        Currently only used to read data stored by an NPT dynamics object.
+        The data is not associated with individual atoms.
+        """
+        if self.state != 'read':
+            raise IOError('Cannot read extra data in %s mode' % (self.state,))
+        # Handle negative n.
+        if n < 0:
+            n += self.nframes
+        if n < 0 or n >= self.nframes:
+            raise IndexError('Trajectory index %d out of range [0, %d['
+                             % (n, self.nframes))
+        framedir = os.path.join(self.filename, 'F' + str(n))
+        return self.backend.read(framedir, name) 
+
+    def _read_data(self, f0, f, name, atom_id):
+        "Read single data item."
+        
+        if self.subtype == 'normal':
+            if self.datatypes[name] == 'once':
+                d = self.backend.read(f0, name)
+            else:
+                d = self.backend.read(f, name)
+        elif self.subtype == 'split':
+            if self.datatypes[name] == 'once':
+                d = self.backend.read_split(f0, name)
+            else:
+                d = self.backend.read_split(f, name)
+            if atom_id is not None:
+                assert len(d) == len(atom_id)
+                d = d[atom_id]
+        return d
+
+    def __len__(self):
+        return self.nframes
+    
+    def _open_log(self):
+        if not (self.master or self.slavelog):
+            return
+        if self.master:
+            lfn = os.path.join(self.filename, "log.txt")
+        else:
+            lfn = os.path.join(self.filename, ("log-node%d.txt" % 
+                                               (ase.parallel.rank,)))
+        self.logfile = open(lfn, "a")   # Append to log if it exists.
+        if hasattr(self, 'logdata'):
+            for text in self.logdata:
+                self.logfile.write(text + '\n')
+            self.logfile.flush()
+            del self.logdata
+
+    def _open_write(self, atoms, backup):
+        "Open a bundle trajectory for writing."
+        self.logfile = None # Enable delayed logging
+        self.atoms = atoms
+        if os.path.exists(self.filename):
+            # The output directory already exists.
+            if not self.is_bundle(self.filename):
+                raise IOError("Filename '" + self.filename + 
+                              "' already exists, but is not a BundleTrajectory." + 
+                              "Cowardly refusing to remove it.")
+            ase.parallel.barrier() # All must have time to see it exists.
+            if self.is_empty_bundle(self.filename):
+                self.log('Deleting old "%s" as it is empty' % (self.filename,)) 
+                self.delete_bundle(self.filename)
+            elif not backup:
+                self.log('Deleting old "%s" as backup is turned off.' % (self.filename,))
+                self.delete_bundle(self.filename)
+            else:
+                # Make a backup file
+                bakname = self.filename + '.bak'
+                if os.path.exists(bakname):
+                    ase.parallel.barrier()  # All must see it exists
+                    self.log('Deleting old backup file "%s"' % (bakname,))
+                    self.delete_bundle(bakname)
+                self.log('Renaming "%s" to "%s"' % (self.filename, bakname))
+                self._rename_bundle(self.filename, bakname)
+        # Ready to create a new bundle.
+        self.log('Creating new "%s"' % (self.filename,))
+        self._make_bundledir(self.filename)
+        self.state = 'prewrite'
+        self._write_metadata({})
+        self._write_nframes(0)    # Mark new bundle as empty
+        self._open_log()
+        self.nframes = 0
+
+    def _open_read(self):
+        "Open a bundle trajectory for reading."
+        if not os.path.exists(self.filename):
+            raise IOError('File not found: ' + self.filename)
+        if not self.is_bundle(self.filename):
+            raise IOError('Not a BundleTrajectory: ' + self.filename)
+        self.state = 'read'
+        # Read the metadata
+        metadata = self._read_metadata()
+        self.metadata = metadata
+        if metadata['version'] > self.version:
+            raise NotImplementedError(
+                "This version of ASE cannot read a BundleTrajectory version "
+                + str(metadata['version']))
+        if metadata['subtype'] not in ('normal', 'split'):
+            raise NotImplementedError(
+                "This version of ASE cannot read BundleTrajectory subtype "
+                + metadata['subtype'])
+        self.subtype = metadata['subtype']
+        self._set_backend(metadata['backend'])
+        self.nframes = self._read_nframes()
+        if self.nframes == 0:
+            raise IOError("Empty BundleTrajectory")
+        self.datatypes = metadata['datatypes']
+        self.state = 'read'
+        
+    def _write_nframes(self, n):
+        "Write the number of frames in the bundle."
+        assert self.state == 'write' or self.state == 'prewrite'
+        f = paropen(os.path.join(self.filename, "frames"), "w")
+        f.write(str(n) + '\n')
+        f.close()
+
+    def _read_nframes(self):
+        "Read the number of frames."
+        f = open(os.path.join(self.filename, 'frames'))
+        n = int(f.read())
+        return n
+
+    def _write_metadata(self, metadata):
+        """Write the metadata file of the bundle.
+
+        Modifies the medadata dictionary!
+        """
+        # Add standard fields that must always be present.
+        assert self.state == 'write' or self.state == 'prewrite'
+        metadata['format'] = 'BundleTrajectory'
+        metadata['version'] = self.version
+        metadata['subtype'] = self.subtype
+        metadata['backend'] = self.backend_name
+        f = paropen(os.path.join(self.filename, "metadata"), "w")
+        pickle.dump(metadata, f, -1)
+        f.close()
+
+    def _read_metadata(self):
+        """Read the metadata."""
+        assert self.state == 'read'
+        f = open(os.path.join(self.filename, 'metadata'))
+        metadata = pickle.load(f)
+        f.close()
+        return metadata
+
+    @staticmethod
+    def is_bundle(filename):
+        """Check if a filename exists and is a BundleTrajectory."""
+        if not os.path.isdir(filename):
+            return False
+        metaname = os.path.join(filename, 'metadata')
+        if not os.path.isfile(metaname):
+            return False
+        f = open(metaname)
+        mdata = pickle.load(f)
+        f.close()
+        try:
+            return mdata['format'] == 'BundleTrajectory'
+        except KeyError:
+            return False
+
+    @staticmethod
+    def is_empty_bundle(filename):
+        """Check if a filename is an empty bundle.  Assumes that it is a bundle."""
+        f = open(os.path.join(filename, "frames"))
+        nframes = int(f.read())
+        ase.parallel.barrier()  # File may be removed by the master immediately after this.
+        return nframes == 0
+
+    @classmethod
+    def delete_bundle(cls, filename):
+        "Deletes a bundle."
+        if ase.parallel.rank == 0:
+            # Only the master deletes
+            if not cls.is_bundle(filename):
+                raise IOError("Cannot remove '%s' as it is not a bundle trajectory."
+                              % (filename,))
+            if os.path.islink(filename):
+                # A symbolic link to a bundle.  Only remove the link.
+                os.remove(filename)
+            else:
+                # A real bundle
+                shutil.rmtree(filename)
+        else:
+            # All other tasks wait for the directory to go away.
+            while os.path.exists(filename):
+                time.sleep(1)
+        # The master may not proceed before all tasks have seen the
+        # directory go away, as it might otherwise create a new bundle
+        # with the same name, fooling the wait loop in _make_bundledir.
+        ase.parallel.barrier()
+
+    def _rename_bundle(self, oldname, newname):
+        "Rename a bundle.  Used to create the .bak"
+        if self.master:
+            os.rename(oldname, newname)
+        else:
+            while os.path.exists(oldname):
+                time.sleep(1)
+        # The master may not proceed before all tasks have seen the
+        # directory go away.
+        ase.parallel.barrier()
+            
+    def _make_bundledir(self, filename):
+        """Make the main bundle directory.
+
+        Since all MPI tasks might write to it, all tasks must wait for
+        the directory to appear.
+        """
+        self.log("Making directory " + filename)
+        assert not os.path.isdir(filename)
+        ase.parallel.barrier()
+        if self.master:
+            os.mkdir(filename)
+        else:
+            i = 0
+            while not os.path.isdir(filename):
+                time.sleep(1)
+                i += 1
+            if i > 10:
+                self.log("Waiting %d seconds for %s to appear!"
+                         % (i, filename))
+
+    def _make_framedir(self, frame):
+        """Make subdirectory for the frame.
+
+        As only the master writes to it, no synchronization between
+        MPI tasks is necessary.
+        """
+        framedir = os.path.join(self.filename, "F" + str(frame))
+        if self.master:
+            os.mkdir(framedir)
+        return framedir
+
+    def pre_write_attach(self, function, interval=1, *args, **kwargs):
+        """Attach a function to be called before writing begins.
+
+        function: The function or callable object to be called.
+
+        interval: How often the function is called.  Default: every time (1).
+
+        All other arguments are stored, and passed to the function.
+        """
+        if not callable(function):
+            raise ValueError("Callback object must be callable.")
+        self.pre_observers.append((function, interval, args, kwargs))
+
+    def post_write_attach(self, function, interval=1, *args, **kwargs):
+        """Attach a function to be called after writing ends.
+
+        function: The function or callable object to be called.
+
+        interval: How often the function is called.  Default: every time (1).
+
+        All other arguments are stored, and passed to the function.
+        """
+        if not callable(function):
+            raise ValueError("Callback object must be callable.")
+        self.post_observers.append((function, interval, args, kwargs))
+
+    def _call_observers(self, obs):
+        "Call pre/post write observers."
+        for function, interval, args, kwargs in obs:
+            if (self.nframes + 1) % interval == 0:
+                function(*args, **kwargs)
+    
+
+class PickleBundleBackend:
+    """Backend for writing BundleTrajectories stored as pickle files."""
+    def __init__(self, master):
+        # Store if this backend will actually write anything
+        self.writesmall = master
+        self.writelarge = master
+        
+    def write_small(self, framedir, smalldata):
+        "Write small data to be written jointly."
+        if self.writesmall:
+            f = open(os.path.join(framedir, "smalldata.pickle"), "w")
+            pickle.dump(smalldata, f, -1)
+            f.close()
+
+    def write(self, framedir, name, data):
+        "Write data to separate file."
+        if self.writelarge:
+            fn = os.path.join(framedir, name + '.pickle')
+            f = open(fn, "w")
+            try:
+                info = (data.shape, str(data.dtype))
+            except AttributeError:
+                info = None
+            pickle.dump(info, f, -1)
+            pickle.dump(data, f, -1)
+            f.close()
+
+    def read_small(self, framedir):
+        "Read small data."
+        f = open(os.path.join(framedir, "smalldata.pickle"))
+        data = pickle.load(f)
+        f.close()
+        return data
+
+    def read(self, framedir, name):
+        "Read data from separate file."
+        fn = os.path.join(framedir, name + '.pickle')
+        f = open(fn)
+        pickle.load(f)  # Discarded.
+        data = pickle.load(f)
+        f.close()
+        return data
+
+    def read_info(self, framedir, name, split=None):
+        "Read information about file contents without reading the data."
+        fn = os.path.join(framedir, name + '.pickle')
+        if split is None or os.path.exists(fn):
+            f = open(fn)
+            info = pickle.load(f)
+            f.close()
+            return info
+        else:
+            for i in range(split):
+                fn = os.path.join(framedir, name + '_' + str(i) + '.pickle')
+                f = open(fn)
+                info = pickle.load(f)
+                f.close()
+                if i == 0:
+                    shape = list(info[0])
+                    dtype = info[1]
+                else:
+                    shape[0] += info[0][0]
+                    assert dtype == info[1]
+            return (tuple(shape), dtype)
+
+    def set_fragments(self, nfrag):
+        self.nfrag = nfrag
+        
+    def read_split(self, framedir, name):
+        "Read data from multiple files."
+        data = []
+        for i in range(self.nfrag):
+            suf = "_%d" % (i,)
+            fn = os.path.join(framedir, name + suf + '.pickle')
+            f = open(fn)
+            shape = pickle.load(f)  # Discarded.
+            data.append(pickle.load(f))
+            f.close()
+        return np.concatenate(data)
+    
+def read_bundletrajectory(filename, index=-1):
+    """Reads one or more atoms objects from a BundleTrajectory.
+
+    Arguments:
+
+    filename: str
+        The name of the bundle (really a directory!)
+    index: int
+        An integer specifying which frame to read, or an index object
+        for reading multiple frames.  Default: -1 (reads the last
+        frame).
+    """
+    traj = BundleTrajectory(filename, mode='r')
+    if isinstance(index, int):
+        return traj[index]
+    else:
+        # Here, we try to read only the configurations we need to read
+        # and len(traj) should only be called if we need to as it will
+        # read all configurations!
+
+        # XXX there must be a simpler way?
+        step = index.step or 1
+        if step > 0:
+            start = index.start or 0
+            if start < 0:
+                start += len(traj)
+            stop = index.stop or len(traj)
+            if stop < 0:
+                stop += len(traj)
+        else:
+            if index.start is None:
+                start = len(traj) - 1
+            else:
+                start = index.start
+                if start < 0:
+                    start += len(traj)
+            if index.stop is None:
+                stop = -1
+            else:
+                stop = index.stop
+                if stop < 0:
+                    stop += len(traj)
+                    
+        return [traj[i] for i in range(start, stop, step)]
+
+def write_bundletrajectory(filename, images):
+    """Write image(s) to a BundleTrajectory.
+
+    Write also energy, forces, and stress if they are already
+    calculated.
+    """
+
+    traj = BundleTrajectory(filename, mode='w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+        
+    for atoms in images:
+        # Avoid potentially expensive calculations:
+        calc = atoms.get_calculator()
+        if hasattr(calc, 'calculation_required'):
+            for quantity in ('energy', 'forces', 'stress', 'magmoms'):
+                traj.select_data(quantity,
+                                 not calc.calculation_required(atoms, [quantity]))
+        traj.write(atoms)
+    traj.close()
+
+def print_bundletrajectory_info(filename):
+    """Prints information about a BundleTrajectory.
+
+    Mainly intended to be called from a command line tool.
+    """
+    if not BundleTrajectory.is_bundle(filename):
+        raise ValueError, "Not a BundleTrajectory!"
+    if BundleTrajectory.is_empty_bundle(filename):
+        print filename, "is an empty BundleTrajectory."
+        return
+    # Read the metadata
+    f = open(os.path.join(filename, 'metadata'))
+    metadata = pickle.load(f)
+    f.close()
+    print "Metadata information of BundleTrajectory '%s':" % (filename,)
+    for k, v in metadata.items():
+        if k != 'datatypes':
+            print "  %s: %s" % (k, v)
+    f = open(os.path.join(filename, 'frames'))
+    nframes = int(f.read())
+    print "Number of frames: %i" % (nframes,)
+    print "Data types:"
+    for k, v in metadata['datatypes'].items():
+        if v == 'once':
+            print "  %s: First frame only." % (k,)
+        elif v:
+            print "  %s: All frames." % (k,)
+    # Look at first frame
+    if metadata['backend'] == 'pickle':
+        backend = PickleBundleBackend(True)
+    else:
+        raise NotImplementedError("Backend %s not supported."
+                                  % (metadata['backend'],))
+    frame = os.path.join(filename, "F0")
+    small = backend.read_small(frame)
+    print "Contents of first frame:"
+    for k, v in small.items():
+        if k == 'constraints':
+            if v:
+                print "  %i constraints are present"
+            else:
+                print "  Constraints are absent."
+        elif k == 'pbc':
+            print "  Periodic boundary conditions: %s" % (str(v),)
+        elif k == 'natoms':
+            print "  Number of atoms: %i" % (v,)
+        elif hasattr(v, 'shape'):
+            print "  %s: shape = %s, type = %s" % (k, str(v.shape), str(v.dtype))
+        else:
+            print "  %s: %s" % (k, str(v))
+    # Read info from separate files.
+    for k, v in metadata['datatypes'].items():
+        if v and not k in small:
+            info = backend.read_info(frame, k)
+            if info and isinstance(info[0], tuple):
+                shape, dtype = info
+            else:
+                shape = info
+                dtype = 'unknown'
+            print "  %s: shape = %s, type = %s" % (k, str(shape), dtype)
+                
+            
+            
+if __name__ == '__main__':
+    from ase.lattice.cubic import FaceCenteredCubic
+    from ase.io import read, write
+    atoms = FaceCenteredCubic(size=(5, 5, 5), symbol='Au')
+    write('test.bundle', atoms)
+    atoms2 = read('test.bundle')
+    assert (atoms.get_positions() == atoms2.get_positions()).all()
+    assert (atoms.get_atomic_numbers() == atoms2.get_atomic_numbers()).all()
+    
+    
diff --git a/ase/io/castep.py b/ase/io/castep.py
new file mode 100644
index 0000000..a3474d2
--- /dev/null
+++ b/ase/io/castep.py
@@ -0,0 +1,597 @@
+# -*- coding: utf-8 -*-
+"""This module defines I/O routines with CASTEP files.
+The key idea is that all function accept or return  atoms objects.
+CASTEP specific parameters will be returned through the <atoms>.calc
+attribute.
+"""
+
+from numpy import sqrt, radians, sin, cos, matrix, array, cross, float32, dot
+import ase
+from ase.constraints import FixAtoms, FixCartesian
+from ase.parallel import paropen
+import os
+
+
+__all__ = [
+        'read_castep',
+        'read_cell',
+        'read_geom',
+        'read_param',
+        'read_seed',
+        'write_cell',
+        'write_param',
+        ]
+
+
+def write_cell(filename, atoms, positions_frac=False, castep_cell=None,
+    force_write=False):
+    """This CASTEP export function write minimal information to
+    a .cell file. If the atoms object is a trajectory, it will
+    take the last image.
+    """
+    if atoms is None:
+        print("Atoms object not initialized")
+        return  False
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            atoms = atoms[-1]
+
+    if os.path.isfile(filename) and not force_write:
+        print('ase.io.castep.write_param: Set optional argument')
+        print('force_write=True to overwrite %s.' % filename)
+        return False
+
+    fd = open(filename, 'w')
+    fd.write('#######################################################\n')
+    fd.write('#CASTEP cell file: %s\n' % filename)
+    fd.write('#Created using the Atomic Simulation Environment (ASE)#\n')
+    fd.write('#######################################################\n\n')
+    fd.write('%BLOCK LATTICE_CART\n')
+    cell = matrix(atoms.get_cell())
+    for line in atoms.get_cell():
+        fd.write('    %.10f %.10f %.10f\n' % tuple(line))
+    fd.write('%ENDBLOCK LATTICE_CART\n\n\n')
+
+    if positions_frac:
+        keyword = 'POSITIONS_FRAC'
+        positions = array(atoms.get_positions() * cell.I)
+
+    else:
+        keyword = 'POSITIONS_ABS'
+        positions = atoms.get_positions()
+
+    if atoms.get_initial_magnetic_moments().any():
+        pos_block = [('%s %8.6f %8.6f %8.6f SPIN=%4.2f' %
+            (x, y[0], y[1], y[2], m)) for (x, y, m)
+            in zip(atoms.get_chemical_symbols(),
+            positions,
+            atoms.get_initial_magnetic_moments())]
+    else:
+        pos_block = [('%s %8.6f %8.6f %8.6f' %
+            (x, y[0], y[1], y[2])) for (x, y)
+            in zip(atoms.get_chemical_symbols(),
+            positions)]
+
+    fd.write('%%BLOCK %s\n' % keyword)
+    for line in pos_block:
+        fd.write('    %s\n' % line)
+    fd.write('%%ENDBLOCK %s\n\n' % keyword)
+
+    # if atoms, has a CASTEP calculator attached, then only
+    # write constraints if really necessary
+    if hasattr(atoms, 'calc')\
+        and hasattr(atoms.calc, 'param')\
+        and hasattr(atoms.calc.param, 'task'):
+        task = atoms.calc.param.task
+        if atoms.calc.param.task.value is None:
+            suppress_constraints = True
+        elif task.value.lower() not in [
+                            'geometryoptimization',
+                            'moleculardynamics',
+                            'transitionstatesearch',
+                            'phonon']:
+            suppress_constraints = True
+        else:
+            suppress_constraints = False
+    else:
+        suppress_constraints = True
+
+    constraints = atoms.constraints
+    if len(constraints) and not suppress_constraints:
+        fd.write("%BLOCK IONIC_CONSTRAINTS \n")
+        count = 0
+        for constr in constraints:
+            if not isinstance(constr, FixAtoms)\
+                and not isinstance(constr, FixCartesian)\
+                and not suppress_constraints:
+                print('Warning: you have constraints in your atoms, that are')
+                print('         not supported by CASTEP')
+                break
+            if isinstance(constr, FixAtoms):
+                # sorry, for this complicated block
+                # reason is that constraint.index can either
+                # hold booleans or integers and in both cases
+                # it is an numpy array, so no simple comparison works
+                for n, val in enumerate(constr.index):
+                    if val.dtype.name.startswith('bool'):
+                        if not val:
+                            continue
+                        symbol = atoms.get_chemical_symbols()[n]
+                        nis = atoms.calc._get_number_in_species(n)
+                    elif val.dtype.name.startswith('int'):
+                        symbol = atoms.get_chemical_symbols()[val]
+                        nis = atoms.calc._get_number_in_species(val)
+                    else:
+                        raise UserWarning('Unrecognized index in' + \
+                                           ' constraint %s' % constr)
+                    fd.write("%6d %3s %3d   1 0 0 \n" % (count + 1,
+                                                         symbol,
+                                                         nis))
+                    fd.write("%6d %3s %3d   0 1 0 \n" % (count + 2,
+                                                         symbol,
+                                                         nis))
+                    fd.write("%6d %3s %3d   0 0 1 \n" % (count + 3,
+                                                         symbol,
+                                                         nis))
+                    count += 3
+            elif isinstance(constr, FixCartesian):
+                n = constr.a
+                symbol = atoms.get_chemical_symbols()[n]
+                nis = atoms.calc._get_number_in_species(n)
+                fix_cart = - constr.mask + 1
+                if fix_cart[0]:
+                    count += 1
+                    fd.write("%6d %3s %3d   1 0 0 \n" % (count, symbol, nis))
+                if fix_cart[1]:
+                    count += 1
+                    fd.write("%6d %3s %3d   0 1 0 \n" % (count, symbol, nis))
+                if fix_cart[2]:
+                    count += 1
+                    fd.write("%6d %3s %3d   0 0 1 \n" % (count, symbol, nis))
+        fd.write("%ENDBLOCK IONIC_CONSTRAINTS \n")
+
+    if castep_cell is None:
+        if hasattr(atoms, 'calc') and hasattr(atoms.calc, 'cell'):
+            castep_cell = atoms.calc.cell
+        else:
+            fd.close()
+            return True
+
+    for option in castep_cell._options.values():
+        if option.value is not None:
+            if option.type == 'Block':
+                fd.write('%%BLOCK %s\n' % option.keyword.upper())
+                fd.write(option.value)
+                fd.write('\n%%ENDBLOCK %s\n' % option.keyword.upper())
+            else:
+                fd.write('%s : %s\n' % (option.keyword.upper(), option.value))
+
+    fd.close()
+    return True
+
+
+def read_cell(filename, _=None):
+    """Read a .cell file and return an atoms object.
+    Any value found that does not fit the atoms API
+    will be stored in the atoms.calc attribute.
+    """
+
+    from ase.calculators.castep import Castep
+    calc = Castep()
+
+    fileobj = open(filename)
+    lines = fileobj.readlines()
+    fileobj.close()
+
+    def get_tokens(lines, l):
+        """Tokenizes one line of a *cell file."""
+        comment_chars = "#!"
+        while l < len(lines):
+            line = lines[l].strip()
+            if len(line) == 0:
+                l += 1
+                continue
+            elif any([line.startswith(comment_char)
+                      for comment_char in comment_chars]):
+                l += 1
+                continue
+            else:
+                for c in comment_chars:
+                    if c in line:
+                        icomment = min(line.index(c))
+                    else:
+                        icomment = len(line) 
+                tokens = line[:icomment].split()
+                return tokens, l + 1
+        tokens = ""
+        print("read_cell: Warning - get_tokens has not found any more tokens")
+        return tokens, l
+
+    lat = []
+    have_lat = False
+
+    pos = []
+    spec = []
+    constraints = []
+    raw_constraints = {}
+    have_pos = False
+    pos_frac = False
+
+    l = 0
+    while l < len(lines):
+        tokens, l = get_tokens(lines, l)
+        if not tokens:
+            continue
+        elif tokens[0].upper() == "%BLOCK":
+            if tokens[1].upper() == "LATTICE_CART" and not have_lat:
+                tokens, l = get_tokens(lines, l)
+                if len(tokens) == 1:
+                    print('read_cell: Warning - ignoring unit specifier in')
+                    print('%BLOCK LATTICE_CART (assuming Angstrom instead)')
+                    tokens, l = get_tokens(lines, l)
+                for _ in range(3):
+                    lat_vec = map(float, tokens[0:3])
+                    lat.append(lat_vec)
+                    tokens, l = get_tokens(lines, l)
+                if tokens[0].upper() != "%ENDBLOCK":
+                    print('read_cell: Warning - ignoring more than three')
+                    print('lattice vectors in invalid %BLOCK LATTICE_CART')
+                    print('%s ...' % tokens[0].upper())
+                have_lat = True
+
+            elif tokens[1].upper() == "LATTICE_ABC" and not have_lat:
+                tokens, l = get_tokens(lines, l)
+                if len(tokens) == 1:
+                    print('read_cell: Warning - ignoring unit specifier in')
+                    print('%BLOCK LATTICE_ABC (assuming Angstrom instead)')
+                    tokens, l = get_tokens(lines, l)
+                a, b, c = map(float, tokens[0:3])
+                tokens, l = get_tokens(lines, l)
+                alpha, beta, gamma = map(lambda phi: radians(float(phi)),
+                                                             tokens[0:3])
+                tokens, l = get_tokens(lines, l)
+                if tokens[0].upper() != "%ENDBLOCK":
+                    print('read_cell: Warning - ignoring additional lines in')
+                    print('invalid %BLOCK LATTICE_ABC')
+                lat_a = [a, 0, 0]
+                lat_b = [b * cos(gamma), b * sin(gamma), 0]
+                lat_c1 = c * cos(beta)
+                lat_c2 = c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma)
+                lat_c3 = sqrt(c * c - lat_c1 * lat_c1 - lat_c2 * lat_c2)
+                lat_c = [lat_c1, lat_c2, lat_c3]
+                lat = [lat_a, lat_b, lat_c]
+                have_lat = True
+
+            elif tokens[1].upper() == "POSITIONS_ABS" and not have_pos:
+                tokens, l = get_tokens(lines, l)
+                if len(tokens) == 1:
+                    print('read_cell: Warning - ignoring unit specifier in')
+                    print('%BLOCK POSITIONS_ABS(assuming Angstrom instead)')
+                    tokens, l = get_tokens(lines, l)
+                while len(tokens) == 4:
+                    spec.append(tokens[0])
+                    pos.append(map(float, tokens[1:4]))
+                    tokens, l = get_tokens(lines, l)
+                if tokens[0].upper() != "%ENDBLOCK":
+                    print('read_cell: Warning - ignoring invalid lines in')
+                    print('%%BLOCK POSITIONS_ABS:\n\t %s' % tokens)
+                have_pos = True
+
+            elif tokens[1].upper() == "POSITIONS_FRAC" and not have_pos:
+                pos_frac = True
+                tokens, l = get_tokens(lines, l)
+                while len(tokens) == 4:
+                    spec.append(tokens[0])
+                    pos.append(map(float, tokens[1:4]))
+                    tokens, l = get_tokens(lines, l)
+                if tokens[0].upper() != "%ENDBLOCK":
+                    print('read_cell: Warning - ignoring invalid lines')
+                    print('%%BLOCK POSITIONS_FRAC:\n\t %s' % tokens)
+                have_pos = True
+            elif tokens[1].upper() == 'SPECIES_POT':
+                tokens, l = get_tokens(lines, l)
+                while tokens and not tokens[0].upper() == '%ENDBLOCK':
+                    if len(tokens) == 2:
+                        calc.cell.species_pot = tuple(tokens)
+                    tokens, l = get_tokens(lines, l)
+            elif tokens[1].upper() == 'IONIC_CONSTRAINTS':
+
+                while True:
+                    if tokens and tokens[0].upper() == '%ENDBLOCK':
+                        break
+                    tokens, l = get_tokens(lines, l)
+                    if not len(tokens) == 6:
+                        continue
+                    _, species, nic, x, y, z = tokens
+                    nic = int(nic)
+                    if (species, nic) not in raw_constraints:
+                        raw_constraints[(species, nic)] = []
+                    raw_constraints[(species, nic)].append(array(
+                                                           [x, y, z]))
+            else:
+                print('Warning: the keyword %s is not' % tokens[1].upper())
+                print('         interpreted in cell files')
+                while not tokens[0].upper() == '%ENDBLOCK':
+                    tokens, l = get_tokens(lines, l)
+                #raise UserWarning
+        else:
+            key = tokens[0]
+            value = ' '.join(tokens[1:])
+            try:
+                calc.__setattr__(key, value)
+            except:
+                print("Problem setting calc.cell.%s = %s" % (key, value))
+                raise
+
+    if pos_frac:
+        atoms = ase.Atoms(
+            calculator=calc,
+            cell=lat,
+            pbc=True,
+            scaled_positions=pos,
+            symbols=spec,
+            )
+    else:
+        atoms = ase.Atoms(
+            calculator=calc,
+            cell=lat,
+            pbc=True,
+            positions=pos,
+            symbols=spec,
+            )
+
+    fixed_atoms = []
+    for (species, nic), value in raw_constraints.iteritems():
+        absolute_nr = atoms.calc._get_absolute_number(species, nic)
+        if len(value) == 3:
+            fixed_atoms.append(absolute_nr)
+        elif len(value) == 2:
+            constraint = ase.constraints.FixedLine(a=absolute_nr,
+                direction=cross(value[0], value[1]))
+            constraints.append(constraint)
+        elif len(value) == 1:
+            constraint = ase.constraints.FixedPlane(a=absolute_nr,
+                direction=array(value[0], dtype=float32))
+            constraints.append(constraint)
+        else:
+            print('Error: Found %s statements attached to atoms %s'
+    % (len(value), absolute_nr))
+    constraints.append(ase.constraints.FixAtoms(fixed_atoms))
+    atoms.set_constraint(constraints)
+
+    # needs to go here again to have the constraints in
+    # atoms.calc.atoms.constraints as well
+    atoms.calc.atoms = atoms
+    atoms.calc.push_oldstate()
+    return atoms
+
+# this actually does not belong here
+# think how one could join this with
+# the ase.calculators.castep.Castep.read()
+# in the future!
+
+
+def read_castep(filename, _=-1):
+    """Reads a .castep file and returns an atoms  object.
+    The calculator information will be stored in the calc attribute.
+    If more than one SCF step is found, a list of all steps
+    will be stored in the traj attribute.
+
+    Note that the index argument has no effect as of now.
+    """
+    from ase.calculators.singlepoint import SinglePointCalculator
+
+    fileobj = open(filename)
+    lines = fileobj.readlines()
+    fileobj.close()
+    traj = []
+    energy_total = None
+    energy_0K = None
+    for i, line in enumerate(lines):
+        if 'NB est. 0K energy' in line:
+            energy_0K = float(line.split()[6])
+        elif 'Final energy, E' in line:
+            energy_total = float(line.split()[4])
+        elif 'Unit Cell' in line:
+            cell = [x.split()[0:3] for x in lines[i + 3:i + 6]]
+            cell = array([[float(col) for col in row] for row in cell])
+        elif 'Cell Contents' in line:
+            geom_starts = i
+            start_found = False
+            for j, jline in enumerate(lines[geom_starts:]):
+                if jline.find('xxxxx') > 0 and start_found:
+                    geom_stop = j + geom_starts
+                    break
+                if jline.find('xxxx') > 0 and not start_found:
+                    geom_start = j + geom_starts + 4
+                    start_found = True
+            species = [line.split()[1] for line in lines[geom_start:geom_stop]]
+            geom = dot(array([[float(col) for col in line.split()[3:6]]
+                for line in lines[geom_start:geom_stop]]), cell)
+        elif 'Writing model to' in line:
+            atoms = ase.Atoms(
+                cell=cell,
+                pbc=True,
+                positions=geom,
+                symbols=''.join(species),
+                )
+            # take 0K energy where available, else total energy
+            if energy_0K:
+                energy = energy_0K
+            else:
+                energy = energy_total
+            # generate a minimal single-point calculator
+            sp_calc = SinglePointCalculator(atoms=atoms,
+                                            energy=energy,
+                                            forces=None,
+                                            magmoms=None,
+                                            stress=None,
+                                            )
+            atoms.set_calculator(sp_calc)
+            traj.append(atoms)
+
+    return traj
+
+
+def read_param(filename, calc=None):
+    """Reads a param file. If an Castep object is passed as the
+    second argument, the parameter setings are merged into
+    the existing object and returned. Otherwise a new Castep()
+    calculator instance gets created and returned.
+    Parameters:
+        filename: the .param file. Only opens reading
+        calc: [Optional] calculator object to hang
+            parameters onto
+    """
+    if calc is None:
+        calc = ase.calculators.castep.Castep(check_castep_version=False)
+    calc.merge_param(filename)
+    return calc
+
+
+def write_param(filename, param, check_checkfile=False,
+                                 force_write=False,
+                                 interface_options=None):
+    """Writes a CastepParam object to a CASTEP .param file
+
+    Parameters:
+        filename: the location of the file to write to. If it
+        exists it will be overwritten without warning. If it
+        doesn't it will be created.
+        param: a CastepParam instance
+        check_checkfile : if set to True, write_param will
+            only write continuation or reuse statement
+            if a restart file exists in the same directory
+    """
+    if os.path.isfile(filename) and not force_write:
+        print('ase.io.castep.write_param: Set optional argument')
+        print('force_write=True to overwrite %s.' % filename)
+        return False
+
+    out = paropen(filename, 'w')
+    out.write('#######################################################\n')
+    out.write('#CASTEP param file: %s\n' % filename)
+    out.write('#Created using the Atomic Simulation Environment (ASE)#\n')
+    if interface_options is not None:
+        out.write('# Internal settings of the calculator\n')
+        out.write('# This can be switched off by settings\n')
+        out.write('# calc._export_settings = False\n')
+        out.write('# If stated, this will be automatically processed\n')
+        out.write('# by ase.io.castep.read_seed()\n')
+        for option, value in sorted(interface_options.iteritems()):
+            out.write('# ASE_INTERFACE %s : %s\n' % (option, value))
+    out.write('#######################################################\n\n')
+    for keyword, opt in sorted(param._options.iteritems()):
+        if opt.type == 'Defined':
+            if opt.value is not None:
+                out.write('%s\n' % (option))
+        elif opt.value is not None:
+            if keyword in ['continuation', 'reuse'] and check_checkfile:
+                if opt.value == 'default':
+                    if not os.path.exists('%s.%s'\
+                        % (os.path.splitext(filename)[0], 'check')):
+                        continue
+                elif not os.path.exists(opt.value):
+                    continue
+            out.write('%s : %s\n'
+                % (keyword, opt.value))
+    out.close()
+
+
+def read_geom(filename, _=-1):
+    """Reads a .geom file produced by the CASTEP GeometryOptimization task and
+    returns an atoms  object.
+    The information about total free energy and forces of each atom for every
+    relaxation step will be stored for further analysis especially in a
+    single-point calculator.
+    Note that everything in the .geom file is in atomic units, which has
+    been conversed to commonly used unit angstrom(length) and eV (energy).
+
+    Note that the index argument has no effect as of now.
+
+    Contribution by Wei-Bing Zhang. Thanks!
+    """
+    from ase.calculators.singlepoint import SinglePointCalculator
+    fileobj = open(filename)
+    txt = fileobj.readlines()
+    fileobj.close()
+    traj = []
+
+    # Source: CODATA2002, used by default
+    # in CASTEP 5.01
+    # but check with your version in case of error
+    # ase.units is based on CODATA1986/
+    # here we hard-code from http://physics.nist.gov/cuu/Document/all_2002.pdf
+    Hartree = 27.211384565719481
+    Bohr = 0.5291772108
+
+    print('N.B.: Energy in .geom file is not 0K extrapolated.')
+    for i, line in enumerate(txt):
+        if line.find("<-- E") > 0:
+            start_found = True
+            energy = float(line.split()[0]) * Hartree
+            cell = [x.split()[0:3] for x in txt[i + 1:i + 4]]
+            cell = array([[float(col) * Bohr for col in row] for row in
+                cell])
+        if line.find('<-- R') > 0 and start_found:
+            start_found = False
+            geom_start = i
+            for i, line in enumerate(txt[geom_start:]):
+                if line.find('<-- F') > 0:
+                    geom_stop = i + geom_start
+                    break
+            species = [line.split()[0] for line in
+                txt[geom_start:geom_stop]]
+            geom = array([[float(col) * Bohr for col in
+                line.split()[2:5]] for line in txt[geom_start:geom_stop]])
+            forces = array([[float(col) * Hartree / Bohr for col in
+                line.split()[2:5]] for line in
+                    txt[geom_stop:geom_stop + (geom_stop - geom_start)]])
+            image = ase.Atoms(species, geom, cell=cell, pbc=True)
+            image.set_calculator(SinglePointCalculator(energy, forces, None,
+                None, image))
+            traj.append(image)
+
+    return traj
+
+
+def read_seed(seed, new_seed=None, ignore_internal_keys=False):
+    """A wrapper around the CASTEP Calculator in conjunction with
+    read_cell and read_param. Basically this can be used to reuse
+    a previous calculation which results in a triple of
+    cell/param/castep file. The label of the calculation if pre-
+    fixed with cop_of_ and everything else will be recycled as
+    much as possible from the addressed calculation.
+    """
+
+    directory = os.path.abspath(os.path.dirname(seed))
+    seed = os.path.basename(seed)
+
+    paramfile = os.path.join(directory, '%s.param' % seed)
+    cellfile = os.path.join(directory, '%s.cell' % seed)
+    castepfile = os.path.join(directory, '%s.castep' % seed)
+
+    atoms = read_cell(cellfile)
+    atoms.calc._directory = directory
+    atoms.calc._rename_existing_dir = False
+    atoms.calc._castep_pp_path = directory
+    atoms.calc.merge_param(paramfile,
+                           ignore_internal_keys=ignore_internal_keys)
+    if new_seed is None:
+        atoms.calc._label = 'copy_of_%s' % seed
+    else:
+        atoms.calc._label = str(new_seed)
+    if os.path.isfile(castepfile):
+        # _set_atoms needs to be True here
+        # but we set it right back to False
+        atoms.calc._set_atoms = True
+        atoms.calc.read(castepfile)
+        atoms.calc._set_atoms = False
+        # sync the top-level object with the
+        # one attached to the calculator
+        atoms = atoms.calc.atoms
+    else:
+        print('Corresponding CASTEP not found.')
+    atoms.calc.push_oldstate()
+
+    return atoms
diff --git a/ase/io/cfg.py b/ase/io/cfg.py
new file mode 100644
index 0000000..634ee11
--- /dev/null
+++ b/ase/io/cfg.py
@@ -0,0 +1,253 @@
+import numpy as np
+
+import ase
+
+from ase.parallel import paropen
+
+
+cfg_default_fields = np.array( [ 'positions', 'momenta', 'numbers', 'magmoms' ] )
+
+def write_cfg(f, a):
+    """Write atomic configuration to a CFG-file (native AtomEye format).
+       See: http://mt.seas.upenn.edu/Archive/Graphics/A/
+    """
+    if isinstance(f, str):
+        f = paropen(f, 'w')
+
+    f.write('Number of particles = %i\n' % len(a))
+    f.write('A = 1.0 Angstrom\n')
+    cell = a.get_cell()
+    for i in range(3):
+        for j in range(3):
+            f.write('H0(%1.1i,%1.1i) = %f A\n' % ( i + 1, j + 1, cell[i, j] ))
+
+    entry_count  = 3
+    for x in a.arrays.keys():
+        if not x in cfg_default_fields:
+            if len(a.get_array(x).shape) == 1:
+                entry_count += 1
+            else:
+                entry_count += a.get_array(x).shape[1]
+
+    vels = a.get_velocities()
+    if type(vels) == np.ndarray:
+        entry_count += 3
+    else:
+        f.write('.NO_VELOCITY.\n')
+
+    f.write('entry_count = %i\n' % entry_count)
+
+    i = 0
+    for name, aux in a.arrays.iteritems():
+        if not name in cfg_default_fields:
+            if len(aux.shape) == 1:
+                f.write('auxiliary[%i] = %s [a.u.]\n' % ( i, name ))
+                i += 1
+            else:
+                for j in range(aux.shape[1]):
+                    f.write('auxiliary[%i] = %s_%1.1i [a.u.]\n' % ( i, name, j ))
+                    i += 1
+
+    # Distinct elements
+    spos = a.get_scaled_positions()
+    for i in a:
+        el  = i.symbol
+
+        f.write('%f\n' % ase.data.atomic_masses[ase.data.chemical_symbols.index(el)])
+        f.write('%s\n' % el)
+
+        x, y, z = spos[i.index, :]
+        s =  '%e %e %e ' % ( x, y, z )
+
+        if type(vels) == np.ndarray:
+            vx, vy, vz  = vels[i.index, :]
+            s  = s + ' %e %e %e ' % ( vx, vy, vz )
+
+        for name, aux in a.arrays.iteritems():
+            if not name in cfg_default_fields:
+                if len(aux.shape) == 1:
+                    s += ' %e' % aux[i.index]
+                else:
+                    s += ( aux.shape[1]*' %e' ) % tuple(aux[i.index].tolist())
+                         
+        f.write('%s\n' % s)
+
+
+default_color = {
+    'H': [ 0.800, 0.800, 0.800 ],
+    'C': [ 0.350, 0.350, 0.350 ],
+    'O': [ 0.800, 0.200, 0.200 ]
+    }
+
+default_radius = {
+    'H': 0.435,
+    'C': 0.655,
+    'O': 0.730
+    }
+       
+
+
+def write_clr(f, atoms):
+    """Write extra color and radius code to a CLR-file (for use with AtomEye).
+       Hit F12 in AtomEye to use.
+       See: http://mt.seas.upenn.edu/Archive/Graphics/A/
+    """
+    color   = None
+    radius  = None
+    if atoms.has('color'):
+        color  = atoms.get_array('color')
+    if atoms.has('radius'):
+        radius  = atoms.get_array('radius')
+
+    if color is None:
+        color  = np.zeros([len(atoms),3], dtype=float)
+        for a in atoms:
+            color[a.index, :]  = default_color[a.symbol]
+
+    if radius is None:
+        radius  = np.zeros(len(atoms), dtype=float)
+        for a in atoms:
+            radius[a.index]  = default_radius[a.symbol]
+
+    radius.shape = (-1, 1)
+
+    if isinstance(f, str):
+        f = paropen(f, 'w')   
+    for c1, c2, c3, r in np.append(color, radius, axis=1):
+        f.write('%f %f %f %f\n' % ( c1, c2, c3, r ))
+
+
+###
+
+def read_key_val(f):
+    if isinstance(f, str):
+        l = f
+    else:
+        l = f.readline()
+    s = l.split('=')
+    if len(s) != 2:
+        raise RuntimeError("Line '%s' is not of the form 'key = value'." % l[:-1])
+    return ( s[0].strip(), s[1].strip() )
+
+
+def read_str_key(f, key, key2=None):
+    in_key, val  = read_key_val(f)
+    if key2 is None:
+        if key.upper() != in_key.upper():
+            raise RuntimeError("Key '%s' expected, '%s' found." % ( key, in_key ))
+    else:
+        if key.upper() != in_key.upper() and key2.upper() != in_key.upper():
+            raise RuntimeError("Key '%s' or '%s' expected, '%s' found." % ( key, key2, in_key ))
+    return val
+
+
+def read_int_key(f, key):
+    vals  = read_str_key(f, key).split()
+    # Ignore units
+    return int(vals[0])
+
+
+def read_float_key(f, key):
+    vals  = read_str_key(f, key).split()
+    # Ignore units
+    return float(vals[0])
+
+
+###
+
+def read_cfg(f):
+    """Read atomic configuration from a CFG-file (native AtomEye format).
+       See: http://mt.seas.upenn.edu/Archive/Graphics/A/
+    """
+    if isinstance(f, str):
+        f  = open(f)
+
+    nat   = read_int_key(f, 'Number of particles')
+    unit  = read_float_key(f, 'A')
+
+    cell  = np.zeros( [ 3, 3 ] )
+    for i in range(3):
+        for j in range(3):
+            cell[i, j]  = read_float_key(f, 'H0(%1.1i,%1.1i)' % (i + 1, j + 1))
+
+    l     = f.readline()
+    vels  = None
+    if l.strip() == '.NO_VELOCITY.':
+        l     = f.readline()
+    else:
+        vels  = np.zeros( [ nat, 3 ] )
+
+    naux      = read_int_key(l, 'entry_count') - 3
+    if vels is not None:
+        naux -= 3
+    aux       = np.zeros( [ nat, naux ] )
+
+    auxstrs   = [ ]
+    for i in range(naux):
+        s  = read_str_key(f, 'auxiliary[%1.1i]' % i, 'auxiliary[%2.2i]' % i)
+        auxstrs += [ s[:s.find('[')].strip() ]
+
+    spos     = np.zeros( [ nat, 3 ] )
+    masses   = np.zeros( nat )
+    syms     = [ '' for i in range(nat) ]
+
+    i  = 0
+    s  = f.readline().split()
+    while l:
+        mass   = float(s[0])
+        sym    = f.readline().strip()
+
+        l      = f.readline()
+        s      = l.split()
+
+        while l and len(s) > 1:
+            masses[i]  = mass
+            syms[i]    = sym
+            props      = [ float(x) for x in s ]
+
+            spos[i, :] = props[0:3]
+            off        = 3
+            if vels is not None:
+                off         = 6
+                vels[i, :]  = props[3:6]
+
+            aux[i, :]  = props[off:]
+
+            i  += 1
+
+            l  = f.readline()
+            if l:
+                s  = l.split()
+                
+    if vels is None:
+        a = ase.Atoms(
+            symbols           = syms,
+            masses            = masses,
+            scaled_positions  = spos,
+            cell              = cell,
+            pbc               = True
+        )
+    else:
+        a = ase.Atoms(
+            symbols           = syms,
+            masses            = masses,
+            scaled_positions  = spos,
+            momenta           = masses.reshape(-1,1)*vels,
+            cell              = cell,
+            pbc               = True
+        )
+
+    i  = 0
+    while i < naux:
+        auxstr  = auxstrs[i]
+
+        if auxstr[-2:] == '_x':
+            a.set_array(auxstr[:-2], aux[:, i:i+3])
+
+            i  += 3
+        else:
+            a.set_array(auxstr, aux[:, i])
+
+            i  += 1
+
+    return a
diff --git a/ase/io/cif.py b/ase/io/cif.py
new file mode 100644
index 0000000..b15fb5e
--- /dev/null
+++ b/ase/io/cif.py
@@ -0,0 +1,372 @@
+"""Module to read and write atoms in cif file format.
+
+See http://www.iucr.org/resources/cif/spec/version1.1/cifsyntax for a
+description of the file format.  STAR extensions as save frames,
+global blocks, nested loops and multi-data values are not supported.
+"""
+
+import shlex
+import re
+
+import numpy as np
+
+from ase.parallel import paropen
+from ase.lattice.spacegroup import crystal
+from ase.lattice.spacegroup.spacegroup import spacegroup_from_data
+
+
+def get_lineno(fileobj):
+    """Returns the line number of current line in fileobj."""
+    pos = fileobj.tell()
+    try:
+        fileobj.seek(0)
+        s = fileobj.read(pos)
+        lineno = s.count('\n') 
+    finally:
+        fileobj.seek(pos)
+    return lineno
+
+
+def unread_line(fileobj):
+    """Unread the last line read from *fileobj*."""
+
+    # If previous line ends with CRLF, we have to back up one extra 
+    # character before entering the loop below
+    if fileobj.tell() > 2:
+        fileobj.seek(-2, 1)
+        if fileobj.read(2) == '\r\n':
+            fileobj.seek(-1, 1)
+
+    while True:
+        if fileobj.tell() == 0:
+            break
+        fileobj.seek(-2, 1)
+        if fileobj.read(1) in ('\n', '\r'):
+            break
+        
+
+def convert_value(value):
+    """Convert CIF value string to corresponding python type."""
+    value = value.strip()
+    if re.match('(".*")|(\'.*\')$', value):
+        return value[1:-1]
+    elif re.match(r'[+-]?\d+$', value):
+        return int(value)
+    elif re.match(r'[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$', value):
+        return float(value)
+    elif re.match(r'[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?\(\d+\)$', 
+                  value):
+        return float(value[:value.index('(')])  # strip off uncertainties
+    else:
+        return value
+
+
+def parse_multiline_string(fileobj, line):
+    """Parse semicolon-enclosed multiline string and return it."""
+    assert line[0] == ';'
+    lines = [line[1:].lstrip()]
+    while True:
+        line = fileobj.readline().strip()
+        if line == ';':
+            break
+        lines.append(line)
+    return '\n'.join(lines).strip()
+
+
+def parse_singletag(fileobj, line):
+    """Parse a CIF tag (entries starting with underscore). Returns
+    a key-value pair."""
+    kv = line.split(None, 1)
+    if len(kv) == 1:
+        key = line
+        line = fileobj.readline().strip()
+        while not line or line[0] == '#':
+            line = fileobj.readline().strip()
+        if line[0] == ';':
+            value = parse_multiline_string(fileobj, line)
+        else:
+            value = line
+    else:
+        key, value = kv
+    return key, convert_value(value)
+
+
+def parse_loop(fileobj):
+    """Parse a CIF loop. Returns a dict with column tag names as keys
+    and a lists of the column content as values."""
+    header = []
+    line = fileobj.readline().strip()
+    while line.startswith('_'):
+        header.append(line.lower())
+        line = fileobj.readline().strip()
+    columns = dict([(h, []) for h in header])
+
+    tokens = []
+    while True:
+        lowerline = line.lower()
+        if (not line or 
+            line.startswith('_') or 
+            lowerline.startswith('data_') or 
+            lowerline.startswith('loop_')):
+            break
+        if line.startswith('#'):
+            line = fileobj.readline().strip()
+            continue
+        if line.startswith(';'):
+            t = [parse_multiline_string(fileobj, line)]
+        else:
+            t = shlex.split(line)
+
+        line = fileobj.readline().strip()
+
+        tokens.extend(t)
+        if len(tokens) < len(columns):
+            continue
+        assert len(tokens) == len(header)
+
+        for h, t in zip(header, tokens):
+            columns[h].append(convert_value(t))
+        tokens = []
+    if line:
+        unread_line(fileobj)
+    return columns
+
+
+def parse_items(fileobj, line):
+    """Parse a CIF data items and return a dict with all tags."""
+    tags = {}
+    while True:
+        line = fileobj.readline()
+        if not line:
+            break
+        line = line.strip()
+        lowerline = line.lower()
+        if not line or line.startswith('#'):
+            continue
+        elif line.startswith('_'):
+            key, value = parse_singletag(fileobj, line)
+            tags[key.lower()] = value
+        elif lowerline.startswith('loop_'):
+            tags.update(parse_loop(fileobj))
+        elif lowerline.startswith('data_'):
+            unread_line(fileobj)
+            break
+        elif line.startswith(';'):
+            temp = parse_multiline_string(fileobj, line)
+        else:
+            raise ValueError('%s:%d: Unexpected CIF file entry: "%s"'%(
+                    fileobj.name, get_lineno(fileobj), line))
+    return tags
+
+
+def parse_block(fileobj, line):
+    """Parse a CIF data block and return a tuple with the block name
+    and a dict with all tags."""
+    assert line.lower().startswith('data_')
+    blockname = line.split('_', 1)[1].rstrip()
+    tags = parse_items(fileobj, line)
+    return blockname, tags
+
+
+def parse_cif(fileobj):
+    """Parse a CIF file. Returns a list of blockname and tag
+    pairs. All tag names are converted to lower case."""
+    if isinstance(fileobj, basestring):
+        fileobj = open(fileobj)
+
+    blocks = []
+    while True:
+        line = fileobj.readline()
+        if not line:
+            break
+        line = line.strip()
+        if not line or line.startswith('#'):
+            continue
+        blocks.append(parse_block(fileobj, line))
+    return blocks
+
+
+def tags2atoms(tags, store_tags=False, **kwargs):
+    """Returns an Atoms object from a cif tags dictionary.  If
+    *store_tags* is true, the *info* attribute of the returned Atoms
+    object will be populated with all the cif tags.  Keyword arguments
+    are passed to the Atoms constructor."""
+    a = tags['_cell_length_a']
+    b = tags['_cell_length_b']
+    c = tags['_cell_length_c']
+    alpha = tags['_cell_angle_alpha']
+    beta = tags['_cell_angle_beta']
+    gamma = tags['_cell_angle_gamma']
+
+    scaled_positions = np.array([tags['_atom_site_fract_x'], 
+                                 tags['_atom_site_fract_y'], 
+                                 tags['_atom_site_fract_z']]).T
+
+    symbols = []
+    if '_atom_site_type_symbol' in tags:
+        labels = tags['_atom_site_type_symbol']
+    else:
+        labels = tags['_atom_site_label']
+    for s in labels:
+        # Strip off additional labeling on chemical symbols
+        m = re.search(r'([A-Z][a-z]?)', s)  
+        symbol = m.group(0)
+        symbols.append(symbol)
+
+    # Symmetry specification, see
+    # http://www.iucr.org/resources/cif/dictionaries/cif_sym for a
+    # complete list of official keys.  In addition we also try to
+    # support some commonly used depricated notations
+    no = None
+    if '_space_group.it_number' in tags:
+        no = tags['_space_group.it_number']
+    elif '_space_group_it_number' in tags:
+        no = tags['_space_group_it_number']
+    elif '_symmetry_int_tables_number' in tags:
+        no = tags['_symmetry_int_tables_number']
+
+    symbolHM = None
+    if '_space_group.Patterson_name_h-m' in tags:
+        symbolHM = tags['_space_group.patterson_name_h-m']
+    elif '_symmetry_space_group_name_h-m' in tags:
+        symbolHM = tags['_symmetry_space_group_name_h-m']
+
+    sitesym = None
+    if '_space_group_symop.operation_xyz' in tags:
+        sitesym = tags['_space_group_symop.operation_xyz']
+    elif '_symmetry_equiv_pos_as_xyz' in tags:
+        sitesym = tags['_symmetry_equiv_pos_as_xyz']
+        
+    spacegroup = 1
+    if sitesym is not None:
+        spacegroup = spacegroup_from_data(no=no, symbol=symbolHM,
+                                          sitesym=sitesym)
+    elif no is not None:
+        spacegroup = no
+    elif symbolHM is not None:
+        spacegroup = symbolHM
+    else:
+        spacegroup = 1
+
+    if store_tags:
+        info = tags.copy()
+        if 'info' in kwargs:
+            info.update(kwargs['info'])
+        kwargs['info'] = info
+
+    atoms = crystal(symbols, basis=scaled_positions, 
+                    cellpar=[a, b, c, alpha, beta, gamma],
+                    spacegroup=spacegroup, **kwargs)
+    return atoms
+    
+
+def read_cif(fileobj, index=-1, store_tags=False, **kwargs):
+    """Read Atoms object from CIF file. *index* specifies the data
+    block number or name (if string) to return.  
+
+    If *index* is None or a slice object, a list of atoms objects will
+    be returned. In the case of *index* is *None* or *slice(None)*,
+    only blocks with valid crystal data will be included.
+
+    If *store_tags* is true, the *info* attribute of the returned
+    Atoms object will be populated with all tags in the corresponding
+    cif data block.
+
+    Keyword arguments are passed on to the Atoms constructor."""
+    blocks = parse_cif(fileobj)
+    if isinstance(index, str):
+        tags = dict(blocks)[index]
+        return tags2atoms(tags, **kwargs)
+    elif isinstance(index, int):
+        name, tags = blocks[index]
+        return tags2atoms(tags, **kwargs)
+    elif index is None or index == slice(None):
+        # Return all CIF blocks with valid crystal data
+        images = []
+        for name, tags in blocks:
+            try:
+                atoms = tags2atoms(tags)
+                images.append(atoms)
+            except KeyError:
+                pass
+        if not images:
+            # No block contained a a valid atoms object
+            # Provide an useful error by try converting the first
+            # block to atoms
+            name, tags = blocks[0]
+            tags2atoms(tags)
+        return images
+    else:
+        return [tags2atoms(tags) for name, tags in blocks[index]]
+
+
+def write_cif(fileobj, images):
+    """Write *images* to CIF file."""
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    for i, atoms in enumerate(images):
+        fileobj.write('data_image%d\n' % i)
+
+        from numpy import arccos, pi, dot
+        from numpy.linalg import norm
+
+        cell = atoms.cell
+        a = norm(cell[0])
+        b = norm(cell[1])
+        c = norm(cell[2])
+        alpha = arccos(dot(cell[1], cell[2])/(b*c))*180./pi
+        beta = arccos(dot(cell[0], cell[2])/(a*c))*180./pi
+        gamma = arccos(dot(cell[0], cell[1])/(a*b))*180./pi
+
+        fileobj.write('_cell_length_a       %g\n' % a)
+        fileobj.write('_cell_length_b       %g\n' % b)
+        fileobj.write('_cell_length_c       %g\n' % c)
+        fileobj.write('_cell_angle_alpha    %g\n' % alpha)
+        fileobj.write('_cell_angle_beta     %g\n' % beta)
+        fileobj.write('_cell_angle_gamma    %g\n' % gamma)
+        fileobj.write('\n')
+
+        if atoms.pbc.all():
+            fileobj.write('_symmetry_space_group_name_H-M    %s\n' % 'P 1')
+            fileobj.write('_symmetry_int_tables_number       %d\n' % 1)
+            fileobj.write('\n')
+
+            fileobj.write('loop_\n')
+            fileobj.write('  _symmetry_equiv_pos_as_xyz\n')
+            fileobj.write("  'x, y, z'\n")
+            fileobj.write('\n')
+
+        fileobj.write('loop_\n')
+        fileobj.write('  _atom_site_label\n')
+        fileobj.write('  _atom_site_occupancy\n')
+        fileobj.write('  _atom_site_fract_x\n')
+        fileobj.write('  _atom_site_fract_y\n')
+        fileobj.write('  _atom_site_fract_z\n')
+        fileobj.write('  _atom_site_thermal_displace_type\n')
+        fileobj.write('  _atom_site_B_iso_or_equiv\n')
+        fileobj.write('  _atom_site_type_symbol\n')
+
+        scaled = atoms.get_scaled_positions()
+        no = {}
+        for i, atom in enumerate(atoms):
+            symbol = atom.symbol
+            if symbol in no:
+                no[symbol] += 1
+            else:
+                no[symbol] = 1
+            fileobj.write(
+                '  %-8s %6.4f %7.5f  %7.5f  %7.5f  %4s  %6.3f  %s\n'%(
+                    '%s%d' % (symbol, no[symbol]), 
+                    1.0, 
+                    scaled[i][0], 
+                    scaled[i][1], 
+                    scaled[i][2],
+                    'Biso',
+                    1.0,
+                    symbol))
+
+    
diff --git a/ase/io/cmdft.py b/ase/io/cmdft.py
new file mode 100644
index 0000000..4b22393
--- /dev/null
+++ b/ase/io/cmdft.py
@@ -0,0 +1,28 @@
+from math import pi, cos, sin, sqrt, acos
+import numpy as np
+
+from ase.atom import Atom
+from ase.atoms import Atoms
+from ase.parallel import paropen
+from ase.units import Bohr
+
+
+def read_I_info(fileobj, index=-1):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+
+    del lines[0]
+
+    finished = False
+    s = Atoms()
+    while not finished:
+        w = lines.pop(0).split()
+        if w[0].startswith('"'):
+            position = Bohr * np.array([float(w[3]), float(w[4]), float(w[5])])
+            s.append(Atom(w[0].replace('"',''), position))
+        else:
+            finished = True
+
+    return s
diff --git a/ase/io/cmr_io.py b/ase/io/cmr_io.py
new file mode 100755
index 0000000..63a1cba
--- /dev/null
+++ b/ase/io/cmr_io.py
@@ -0,0 +1,70 @@
+import numpy as np
+from ase.atoms import Atoms
+from ase.calculators.singlepoint import SinglePointCalculator
+from ase.data import atomic_numbers
+
+def get_atoms(cmr_data):
+    if type(cmr_data)==str:
+        raise RuntimeError('cmr db-file: the specified cmr group file does not contain any images, only references.\n'+
+                           'This error could be caused by an older version of CMR - or a group file containing only references to other db-files.')
+    positions = cmr_data.get('ase_positions')
+    numbers = cmr_data.get('ase_atomic_numbers')
+    symbols = cmr_data.get('ase_chemical_symbols')
+    cell = cmr_data.get('ase_cell')
+    pbc = cmr_data.get('ase_pbc')
+    tags = np.array(cmr_data.get('ase_tags'))
+    magmoms = np.array(cmr_data.get('ase_magnetic_moments'))
+    energy = cmr_data.get('ase_potential_energy')
+
+    forces = cmr_data.get('ase_forces')
+
+    
+    if numbers is None and not symbols is None:
+        numbers = [atomic_numbers[x] for x in symbols]
+
+    if numbers is None or positions is None:
+        raise RuntimeError('cmr db-file: there is no or not enough ase data available in the specified db-file.')
+    atoms = Atoms(positions=positions,
+                  numbers=numbers,
+                  cell=cell,
+                  pbc=pbc)
+
+    if tags.any():
+        atoms.set_tags(list(tags))
+
+    if magmoms.any():
+        atoms.set_initial_magnetic_moments(magmoms)
+    else:
+        magmoms = None
+
+    atoms.calc = SinglePointCalculator(energy, forces, None, magmoms,
+                                           atoms)
+    return atoms
+
+def read_db(filename, index):
+    import cmr
+    r = cmr.read(filename)
+    if not r.has_key("ase_positions") and r.is_group():
+        hashes = r.get_member_hashes()
+        hashes = hashes[index]
+        if len(hashes)==0:
+            raise RuntimeError('cmr db-file: could not find any group members.\n'+
+                               'This error could be caused by an older version of CMR - or a group file containing only references to other db-files.')
+        if type(hashes)==list:
+            return [get_atoms(r.get_xmldata(hash)) for hash in hashes]
+        return get_atoms(r.get_xmldata(hashes))
+    else:
+        return get_atoms(r)
+    
+    
+
+def write_db(filename, images, **kwargs):
+    try:
+        import cmr
+        cmr.atoms2cmr(images, **kwargs).write(filename)
+    except:
+        raise
+        raise NotAvailable('CMR version>0.3.2 is required')
+    
+
+
diff --git a/ase/io/cube.py b/ase/io/cube.py
new file mode 100644
index 0000000..7d3299d
--- /dev/null
+++ b/ase/io/cube.py
@@ -0,0 +1,104 @@
+"""
+IO support for the Gaussian cube format.
+
+See the format specifications on:
+http://local.wasp.uwa.edu.au/~pbourke/dataformats/cube/
+"""
+
+
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.units import Bohr
+from ase.parallel import paropen
+
+
+def write_cube(fileobj, atoms, data=None):
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+        
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            raise ValueError('Can only write one configuration '
+                             'to a cube file!')
+        atoms = atoms[0]
+
+    if data is None:
+        data = np.ones((2, 2, 2))
+    data = np.asarray(data)
+
+    if data.dtype == complex:
+        data = np.abs(data)
+
+    fileobj.write('cube file from ase\n')
+    fileobj.write('OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z\n')
+
+    cell = atoms.get_cell()
+    shape = np.array(data.shape)
+
+    corner = np.zeros(3)
+    for i in range(3):
+        if shape[i] % 2 == 1:
+            shape[i] += 1
+            corner += cell[i] / shape[i] / Bohr
+
+    fileobj.write('%5d%12.6f%12.6f%12.6f\n' % (len(atoms), corner[0],
+                                               corner[1], corner[2]))
+
+    for i in range(3):
+        n = data.shape[i]
+        d = cell[i] / shape[i] / Bohr
+        fileobj.write('%5d%12.6f%12.6f%12.6f\n' % (n, d[0], d[1], d[2]))
+
+    positions = atoms.get_positions() / Bohr
+    numbers = atoms.get_atomic_numbers()
+    for Z, (x, y, z) in zip(numbers, positions):
+        fileobj.write('%5d%12.6f%12.6f%12.6f%12.6f\n' % (Z, 0.0, x, y, z)) 
+
+    data.tofile(fileobj, sep='\n', format='%e')
+
+
+def read_cube(fileobj, index=-1, read_data=False):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    readline = fileobj.readline
+    readline()
+    axes = ['XYZ'.index(s[0]) for s in readline().split()[2::3]]
+    if axes == []:
+        axes = [0, 1, 2]
+    line = readline().split()
+    natoms = int(line[0])
+    corner = [Bohr * float(x) for x in line[1:]]
+
+    cell = np.empty((3, 3))
+    shape = []
+    for i in range(3):
+        n, x, y, z = [float(s) for s in readline().split()]
+        shape.append(n)
+        if n % 2 == 1:
+            n += 1
+        cell[i] = n * Bohr * np.array([x, y, z])
+        
+    numbers = np.empty(natoms, int)
+    positions = np.empty((natoms, 3))
+    for i in range(natoms):
+        line = readline().split()
+        numbers[i] = int(line[0])
+        positions[i] = [float(s) for s in line[2:]]
+
+    positions *= Bohr
+    atoms = Atoms(numbers=numbers, positions=positions, cell=cell)
+
+    if read_data:
+        data = np.array([float(s)
+                         for s in fileobj.read().split()]).reshape(shape)
+        if axes != [0, 1, 2]:
+            data = data.transpose(axes).copy()
+        return data, atoms
+
+    return atoms
+
+
+def read_cube_data(fileobj):
+    return read_cube(fileobj, read_data=True)
diff --git a/ase/io/dacapo.py b/ase/io/dacapo.py
new file mode 100644
index 0000000..bd64feb
--- /dev/null
+++ b/ase/io/dacapo.py
@@ -0,0 +1,85 @@
+import numpy as np
+
+from ase.calculators.singlepoint import SinglePointCalculator
+from ase.atom import Atom
+from ase.atoms import Atoms
+
+
+def read_dacapo_text(fileobj):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+    i = lines.index(' Structure:             A1           A2            A3\n')
+    cell = np.array([[float(w) for w in line.split()[2:5]]
+                     for line in lines[i + 1:i + 4]]).transpose()
+    i = lines.index(' Structure:  >>         Ionic positions/velocities ' +
+                    'in cartesian coordinates       <<\n')
+    atoms = []
+    for line in lines[i + 4:]:
+        words = line.split()
+        if len(words) != 9:
+            break
+        Z, x, y, z = words[2:6]
+        atoms.append(Atom(int(Z), [float(x), float(y), float(z)]))
+
+    atoms = Atoms(atoms, cell=cell.tolist())
+
+    try:
+        i = lines.index(
+            ' DFT:  CPU time                           Total energy\n')
+    except ValueError:
+        pass
+    else:
+        column = lines[i + 3].split().index('selfcons') - 1
+        try:
+            i2 = lines.index(' ANALYSIS PART OF CODE\n', i)
+        except ValueError:
+            pass
+        else:
+            while i2 > i:
+                if lines[i2].startswith(' DFT:'):
+                    break
+                i2 -= 1
+            energy = float(lines[i2].split()[column])
+            atoms.set_calculator(SinglePointCalculator(energy, None, None,
+                                                       None, atoms))
+
+    return atoms
+
+
+
+def read_dacapo(filename):
+    from ase.io.pupynere import NetCDFFile
+
+    nc = NetCDFFile(filename)
+    dims = nc.dimensions
+    vars = nc.variables
+
+    cell = vars['UnitCell'][-1]
+    try:
+        magmoms = vars['InitialAtomicMagneticMoment'][:]
+    except KeyError:
+        magmoms = None
+    try:
+        tags = vars['AtomTags'][:]
+    except KeyError:
+        tags = None
+    atoms = Atoms(scaled_positions=vars['DynamicAtomPositions'][-1],
+                  symbols=[(a + b).strip() 
+                           for a, b in vars['DynamicAtomSpecies'][:]],
+                  cell=cell,
+                  magmoms=magmoms,
+                  tags=tags,
+                  pbc=True)
+
+    try:
+        energy = vars['TotalEnergy'][-1] 
+        force = vars['DynamicAtomForces'][-1] 
+    except KeyError: 
+        energy = None 
+        force = None 
+    calc = SinglePointCalculator(energy,force,None, None, atoms)  ### Fixme magmoms
+    atoms.set_calculator(calc)
+        
+    return atoms
diff --git a/ase/io/dftb.py b/ase/io/dftb.py
new file mode 100644
index 0000000..b2c5d5c
--- /dev/null
+++ b/ase/io/dftb.py
@@ -0,0 +1,126 @@
+from ase.atoms import Atoms
+
+def read_dftb(filename='dftb_in.hsd'):
+    """Method to read coordinates form DFTB+ input file dftb_in.hsd
+    additionally read information about fixed atoms
+    and periodic boundary condition
+    """
+    from ase import Atoms
+
+    if isinstance(filename, str):
+        myfile = open(filename)
+
+    lines = myfile.readlines()
+    atoms_pos = []
+    atom_symbols = []
+    type_names = []
+    my_pbc = False
+    mycell = []
+
+
+    for iline, line in enumerate(lines):
+        if (line.strip().startswith('#')):
+            pass
+        else:
+            if ('TypeNames' in line):
+                col = line.split()
+                for i in range(3, len(col)-1):
+                    type_names.append(col[i].strip("\""))
+            elif ('Periodic' in line):
+                if ('Yes' in line):
+                    my_pbc = True
+            elif ('LatticeVectors' in line):
+                for imycell in range(3):
+                    extraline = lines[iline+imycell+1]
+                    cols = extraline.split()
+                    mycell.append(\
+                        [float(cols[0]),float(cols[1]),float(cols[2])])
+            else:
+                pass
+
+    start_reading_coords = False
+    stop_reading_coords = False
+    for line in lines:
+        if (line.strip().startswith('#')):
+            pass
+        else:
+            if ('TypesAndCoordinates' in line):
+                start_reading_coords = True
+            if start_reading_coords:
+                if ('}' in line):
+                    stop_reading_coords = True
+            if (start_reading_coords and not(stop_reading_coords)
+            and not 'TypesAndCoordinates' in line):
+                typeindexstr, xxx, yyy, zzz = line.split()[:4]
+                typeindex = int(typeindexstr)
+                symbol = type_names[typeindex-1]
+                atom_symbols.append(symbol)
+                atoms_pos.append([float(xxx), float(yyy), float(zzz)])
+
+            
+    if type(filename) == str:
+        myfile.close()
+
+    atoms = Atoms(positions = atoms_pos, symbols = atom_symbols, 
+                  cell = mycell, pbc = my_pbc)
+
+
+    return atoms
+
+
+def write_dftb(filename, atoms):
+    """Method to write atom structure in DFTB+ format
+       (gen format)
+    """
+    import numpy as np
+
+    #sort
+    atoms.set_masses()
+    masses = atoms.get_masses()
+    indexes = np.argsort(masses)
+    atomsnew = Atoms()
+    for i in indexes:
+        atomsnew = atomsnew + atoms[i]
+
+    if isinstance(filename, str):
+        myfile = open(filename, 'w')
+    else: # Assume it's a 'file-like object'
+        myfile = filename
+
+    ispbc = atoms.get_pbc()
+    box = atoms.get_cell()
+    
+    if (any(ispbc)):
+        myfile.write('%8d %2s \n' %(len(atoms), 'S'))
+    else:
+        myfile.write('%8d %2s \n' %(len(atoms), 'C'))
+
+    chemsym = atomsnew.get_chemical_symbols()
+    allchem = ''
+    for i in chemsym:
+        if i not in allchem:
+            allchem = allchem + i + ' '
+    myfile.write(allchem+' \n')
+
+    coords = atomsnew.get_positions()
+    itype = 1
+    for iatom, coord in enumerate(coords):
+        if iatom > 0:
+            if chemsym[iatom] != chemsym[iatom-1]:
+                itype = itype+1
+        myfile.write('%5i%5i  %19.16f %19.16f %19.16f \n' \
+                    %(iatom+1, itype, 
+                      coords[iatom][0], coords[iatom][1], coords[iatom][2]))
+    # write box
+    if (any(ispbc)):
+        #dftb dummy
+        myfile.write(' %19.16f %19.16f %19.16f \n' %(0, 0, 0))
+        myfile.write(' %19.16f %19.16f %19.16f \n' 
+                %( box[0][0], box[0][1], box[0][2]))
+        myfile.write(' %19.16f %19.16f %19.16f \n' 
+                %( box[1][0], box[1][1], box[1][2]))
+        myfile.write(' %19.16f %19.16f %19.16f \n' 
+                %( box[2][0], box[2][1], box[2][2]))
+
+    if type(filename) == str:    
+        myfile.close()
diff --git a/ase/io/eps.py b/ase/io/eps.py
new file mode 100644
index 0000000..f84263d
--- /dev/null
+++ b/ase/io/eps.py
@@ -0,0 +1,213 @@
+import time
+from math import sqrt
+
+import numpy as np
+
+from ase.utils import rotate
+from ase.data import covalent_radii
+from ase.data.colors import jmol_colors
+
+
+class EPS:
+    def __init__(self, atoms,
+                 rotation='', show_unit_cell=False, radii=None,
+                 bbox=None, colors=None, scale=20):
+        self.numbers = atoms.get_atomic_numbers()
+        self.colors = colors
+        if colors is None:
+            self.colors = jmol_colors[self.numbers]
+
+        if radii is None:
+            radii = covalent_radii[self.numbers]
+        elif type(radii) is float:
+            radii = covalent_radii[self.numbers] * radii
+        else:
+            radii = np.array(radii)
+
+        natoms = len(atoms)
+
+        if isinstance(rotation, str):
+            rotation = rotate(rotation)
+
+        A = atoms.get_cell()
+        if show_unit_cell > 0:
+            L, T, D = self.cell_to_lines(A)
+            C = np.empty((2, 2, 2, 3))
+            for c1 in range(2):
+                for c2 in range(2):
+                    for c3 in range(2):
+                        C[c1, c2, c3] = np.dot([c1, c2, c3], A)
+            C.shape = (8, 3)
+            C = np.dot(C, rotation)  # Unit cell vertices
+        else:
+            L = np.empty((0, 3))
+            T = None
+            D = None
+            C = None
+
+        nlines = len(L)
+
+        X = np.empty((natoms + nlines, 3))
+        R = atoms.get_positions()
+        X[:natoms] = R
+        X[natoms:] = L
+
+        r2 = radii**2
+        for n in range(nlines):
+            d = D[T[n]]
+            if ((((R - L[n] - d)**2).sum(1) < r2) &
+                (((R - L[n] + d)**2).sum(1) < r2)).any():
+                T[n] = -1
+
+        X = np.dot(X, rotation)
+        R = X[:natoms]
+
+        if bbox is None:
+            X1 = (R - radii[:, None]).min(0)
+            X2 = (R + radii[:, None]).max(0)
+            if show_unit_cell == 2:
+                X1 = np.minimum(X1, C.min(0))
+                X2 = np.maximum(X2, C.max(0))
+            M = (X1 + X2) / 2
+            S = 1.05 * (X2 - X1)
+            w = scale * S[0]
+            if w > 500:
+                w = 500
+                scale = w / S[0]
+            h = scale * S[1]
+            offset = np.array([scale * M[0] - w / 2, scale * M[1] - h / 2, 0])
+        else:
+            w = (bbox[2] - bbox[0]) * scale
+            h = (bbox[3] - bbox[1]) * scale
+            offset = np.array([bbox[0], bbox[1], 0]) * scale
+
+        self.w = w
+        self.h = h
+
+        X *= scale
+        X -= offset
+
+        if nlines > 0:
+            D = np.dot(D, rotation)[:, :2] * scale
+
+        if C is not None:
+            C *= scale
+            C -= offset
+
+        A = np.dot(A, rotation)
+        A *= scale
+
+        self.A = A
+        self.X = X
+        self.D = D
+        self.T = T
+        self.C = C
+        self.natoms = natoms
+        self.d = 2 * scale * radii
+
+    def cell_to_lines(self, A):
+        nlines = 0
+        nn = []
+        for c in range(3):
+            d = sqrt((A[c]**2).sum())
+            n = max(2, int(d / 0.3))
+            nn.append(n)
+            nlines += 4 * n
+
+        X = np.empty((nlines, 3))
+        T = np.empty(nlines, int)
+        D = np.zeros((3, 3))
+
+        n1 = 0
+        for c in range(3):
+            n = nn[c]
+            dd = A[c] / (4 * n - 2)
+            D[c] = dd
+            P = np.arange(1, 4 * n + 1, 4)[:, None] * dd
+            T[n1:] = c
+            for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]:
+                n2 = n1 + n
+                X[n1:n2] = P + i * A[(c + 1) % 3] + j * A[(c + 2) % 3]
+                n1 = n2
+
+        return X, T, D
+
+    def write(self, filename):
+        self.filename = filename
+        self.write_header()
+        self.write_body()
+        self.write_trailer()
+
+    def write_header(self):
+        import matplotlib
+        if matplotlib.__version__ <= '0.8':
+            raise RuntimeError('Your version of matplotlib (%s) is too old' %
+                               matplotlib.__version__)
+
+        from matplotlib.backends.backend_ps import RendererPS, \
+             GraphicsContextPS, psDefs
+
+        self.fd = open(self.filename, 'w')
+        self.fd.write('%!PS-Adobe-3.0 EPSF-3.0\n')
+        self.fd.write('%%Creator: G2\n')
+        self.fd.write('%%CreationDate: %s\n' % time.ctime(time.time()))
+        self.fd.write('%%Orientation: portrait\n')
+        bbox = (0, 0, self.w, self.h)
+        self.fd.write('%%%%BoundingBox: %d %d %d %d\n' % bbox)
+        self.fd.write('%%EndComments\n')
+
+        Ndict = len(psDefs)
+        self.fd.write('%%BeginProlog\n')
+        self.fd.write('/mpldict %d dict def\n' % Ndict)
+        self.fd.write('mpldict begin\n')
+        for d in psDefs:
+            d = d.strip()
+            for l in d.split('\n'):
+                self.fd.write(l.strip() + '\n')
+        self.fd.write('%%EndProlog\n')
+
+        self.fd.write('mpldict begin\n')
+        self.fd.write('%d %d 0 0 clipbox\n' % (self.w, self.h))
+
+        self.renderer = RendererPS(self.w, self.h, self.fd)
+
+    def write_body(self):
+        try:
+            from matplotlib.path import Path
+        except ImportError:
+            Path = None
+            from matplotlib.patches import Circle, Polygon
+        else:
+            from matplotlib.patches import Circle, PathPatch
+
+        indices = self.X[:, 2].argsort()
+        for a in indices:
+            xy = self.X[a, :2]
+            if a < self.natoms:
+                r = self.d[a] / 2
+                if ((xy[1] + r > 0) and (xy[1] - r < self.h) and
+                    (xy[0] + r > 0) and (xy[0] - r < self.w)):
+                    circle = Circle(xy, r, facecolor=self.colors[a])
+                    circle.draw(self.renderer)
+            else:
+                a -= self.natoms
+                c = self.T[a]
+                if c != -1:
+                    hxy = self.D[c]
+                    if Path is None:
+                        line = Polygon((xy + hxy, xy - hxy))
+                    else:
+                        line = PathPatch(Path((xy + hxy, xy - hxy)))
+                    line.draw(self.renderer)
+
+    def write_trailer(self):
+        self.fd.write('end\n')
+        self.fd.write('showpage\n')
+        self.fd.close()
+
+
+def write_eps(filename, atoms, **parameters):
+    if isinstance(atoms, list):
+        assert len(atoms) == 1
+        atoms = atoms[0]
+    EPS(atoms, **parameters).write(filename)
diff --git a/ase/io/etsf.py b/ase/io/etsf.py
new file mode 100644
index 0000000..09abf0f
--- /dev/null
+++ b/ase/io/etsf.py
@@ -0,0 +1,101 @@
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.units import Bohr
+from ase.io.pupynere import NetCDFFile
+
+
+class ETSFReader:
+    def __init__(self, filename):
+        self.nc = NetCDFFile(filename, 'r')
+
+    def read_atoms(self):
+        var = self.nc.variables
+        cell = var['primitive_vectors']
+        assert cell.units == 'atomic units'
+        species = var['atom_species'][:]
+        spos = var['reduced_atom_positions'][:]
+        numbers = var['atomic_numbers'][:]
+        return Atoms(numbers=numbers[species - 1],
+                     scaled_positions=spos,
+                     cell=cell[:] * Bohr,
+                     pbc=True)
+
+
+class ETSFWriter:
+    def __init__(self, filename):
+        from Scientific.IO.NetCDF import NetCDFFile
+        self.nc = NetCDFFile(filename, 'w')
+
+        self.nc.file_format = 'ETSF Nanoquanta'
+        self.nc.file_format_version = np.array([3.3], dtype=np.float32)
+        self.nc.Conventions = 'http://www.etsf.eu/fileformats/'
+        self.nc.history = 'File generated by ASE'
+
+    def write_atoms(self, atoms):
+        specie_a = np.empty(len(atoms), np.int32)
+        nspecies = 0
+        species = {}
+        numbers = []
+        for a, Z in enumerate(atoms.get_atomic_numbers()):
+            if Z not in species:
+                species[Z] = nspecies
+                nspecies += 1
+                numbers.append(Z)
+            specie_a[a] = species[Z]
+            
+        dimensions = [
+            ('character_string_length', 80),
+            ('number_of_atoms', len(atoms)),
+            ('number_of_atom_species', nspecies),
+            ('number_of_cartesian_directions', 3),
+            ('number_of_reduced_dimensions', 3),
+            ('number_of_vectors', 3)]
+
+        for name, size in dimensions:
+            self.nc.createDimension(name, size)
+
+        var = self.add_variable
+        
+        var('primitive_vectors',
+            ('number_of_vectors', 'number_of_cartesian_directions'),
+            atoms.cell / Bohr, units='atomic units')
+        var('atom_species', ('number_of_atoms',), specie_a + 1)
+        var('reduced_atom_positions',
+            ('number_of_atoms', 'number_of_reduced_dimensions'),
+            atoms.get_scaled_positions())
+        var('atomic_numbers', ('number_of_atom_species',),
+            np.array(numbers, dtype=float))
+
+    def close(self):
+        self.nc.close()
+    
+    def add_variable(self, name, dims, data=None, **kwargs):
+        if data is None:
+            char = 'd'
+        else:
+            if isinstance(data, np.ndarray):
+                char = data.dtype.char
+            elif isinstance(data, float):
+                char = 'd'
+            elif isinstance(data, int):
+                char = 'i'
+            else:
+                char = 'c'
+
+        var = self.nc.createVariable(name, char, dims)
+        for attr, value in kwargs.items():
+            setattr(var, attr, value)
+        if data is not None:
+            if len(dims) == 0:
+                var.assignValue(data)
+            else:
+                if char == 'c':
+                    if len(dims) == 1:
+                        var[:len(data)] = data
+                    else:
+                        for i, x in enumerate(data):
+                            var[i, :len(x)] = x
+                else:
+                    var[:] = data
+        return var
diff --git a/ase/io/exciting.py b/ase/io/exciting.py
new file mode 100644
index 0000000..b69d3fb
--- /dev/null
+++ b/ase/io/exciting.py
@@ -0,0 +1,143 @@
+"""
+This is the Implementation of the exciting I/O Functions
+The functions are called with read write usunf the format "exi"
+
+The module depends on lxml  http://codespeak.net/lxml/
+"""
+from math import pi, cos, sin, sqrt, acos
+
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.parallel import paropen
+from ase.units import Bohr
+
+
+def read_exciting(fileobj, index=-1):
+    """Reads structure from exiting xml file.
+    
+    Parameters
+    ----------
+    fileobj: file object
+        File handle from which data should be read.
+        
+    Other parameters
+    ----------------
+    index: integer -1
+        Not used in this implementation.
+    """
+    from lxml import etree as ET
+    #parse file into element tree
+    doc = ET.parse(fileobj)
+    root = doc.getroot()
+    speciesnodes = root.find('structure').getiterator('species') 
+ 
+    symbols = []
+    positions = []
+    basevects = []
+    atoms = None
+    #collect data from tree
+    for speciesnode in speciesnodes:
+        symbol = speciesnode.get('speciesfile').split('.')[0]
+        natoms = speciesnode.getiterator('atom')
+        for atom in natoms:
+            x, y, z = atom.get('coord').split()
+            positions.append([float(x), float(y), float(z)])
+            symbols.append(symbol)
+    # scale unit cell accorting to scaling attributes
+    if doc.xpath('//crystal/@scale'):
+        scale = float(str(doc.xpath('//crystal/@scale')[0]))
+    else:
+        scale = 1
+        
+    if doc.xpath('//crystal/@stretch'):
+        a, b, c = doc.xpath('//crystal/@scale')[0].split()
+        stretch = np.array([float(a),float(b),float(c)])
+    else:    
+        stretch = np.array([1.0, 1.0, 1.0])
+    basevectsn = doc.xpath('//basevect/text()') 
+    for basevect in basevectsn:
+        x, y, z = basevect.split()
+        basevects.append(np.array([float(x) * Bohr * stretch[0],
+                                   float(y) * Bohr * stretch[1], 
+                                   float(z) * Bohr * stretch[2]
+                                   ]) * scale)
+    atoms = Atoms(symbols=symbols, cell=basevects)
+ 
+    atoms.set_scaled_positions(positions)
+    if 'molecule' in root.find('structure').attrib.keys():
+        if root.find('structure').attrib['molecule']:
+            atoms.set_pbc(False)
+    else:
+        atoms.set_pbc(True)
+        
+    return atoms
+
+def write_exciting(fileobj, images):
+    """writes exciting input structure in XML
+    
+    Parameters
+    ----------
+    fileobj : File object
+        Filehandle to which data should be written
+    images : Atom Object or List of Atoms objects
+        This function will write the first Atoms object to file 
+    
+    Returns
+    -------
+    """
+    from lxml import etree as ET
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+    root = atoms2etree(images)
+    fileobj.write(ET.tostring(root, method='xml', 
+                              pretty_print=True,
+                              xml_declaration=True,
+                              encoding='UTF-8'))
+
+def atoms2etree(images):
+    """This function creates the XML DOM corresponding
+     to the structure for use in write and calculator
+    
+    Parameters
+    ----------
+    
+    images : Atom Object or List of Atoms objects
+        This function will create a 
+    
+    Returns
+    -------
+    root : etree object
+        Element tree of exciting input file containing the structure
+    """
+    from lxml import etree as ET
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    root = ET.Element('input')
+    title = ET.SubElement(root, 'title')
+    title.text = ''
+    structure = ET.SubElement(root, 'structure')
+    crystal= ET.SubElement(structure, 'crystal')
+    atoms = images[0]
+    for vec in atoms.cell:
+        basevect = ET.SubElement(crystal, 'basevect')
+        basevect.text = '%.14f %.14f %.14f' % tuple(vec / Bohr)
+                             
+    species = {}
+    symbols = []
+    for aindex, symbol in enumerate(atoms.get_chemical_symbols()):
+        if symbol in species:
+            species[symbol].append(aindex)
+        else:
+            species[symbol] = [aindex]
+            symbols.append(symbol)
+    scaled = atoms.get_scaled_positions()
+    for symbol in symbols:
+        speciesnode = ET.SubElement(structure, 'species',
+                                    speciesfile='%s.xml' % symbol,
+                                    chemicalSymbol=symbol)
+        for a in species[symbol]:
+           atom = ET.SubElement(speciesnode, 'atom',
+                                coord='%.14f %.14f %.14f' % tuple(scaled[a]))
+    return root
diff --git a/ase/io/findsym.py b/ase/io/findsym.py
new file mode 100644
index 0000000..544c4d5
--- /dev/null
+++ b/ase/io/findsym.py
@@ -0,0 +1,44 @@
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+def write_findsym(fileobj, images):
+
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    symbols = images[0].get_chemical_symbols()
+    natoms = len(symbols)
+
+    for atoms in images:
+        formula  = atoms.get_chemical_symbols(True)
+        accuracy = 1.0e-4
+
+        # Write Comment
+        fileobj.write('%s\n' % formula)
+        fileobj.write('%f   accuracy\n' % accuracy)
+        fileobj.write('1    vectors in cartesian coordinates\n')
+
+        # Write cartesian coordinates of vectors
+        for x, y, z in atoms.cell:
+            fileobj.write('%22.15f %22.15f %22.15f\n' % (x, y, z))
+
+        fileobj.write('1    no known centering\n')
+
+        fileobj.write('1 0 0 \n')
+        fileobj.write('0 1 0 \n')
+        fileobj.write('0 0 1 \n')
+
+        fileobj.write('%d\n' % natoms)
+
+        numbers  = atoms.get_atomic_numbers()
+        for n in numbers:
+            fileobj.write('%d ' % (n))
+
+        fileobj.write('\n')
+
+        for x, y, z in atoms.get_positions():
+            fileobj.write('%22.15f %22.15f %22.15f\n' % (x, y, z))
+
diff --git a/ase/io/fortranfile.py b/ase/io/fortranfile.py
new file mode 100644
index 0000000..b0f39a7
--- /dev/null
+++ b/ase/io/fortranfile.py
@@ -0,0 +1,266 @@
+# Copyright 2008-2010 Neil Martinsen-Burrell
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+"""Defines a file-derived class to read/write Fortran unformatted files.
+
+The assumption is that a Fortran unformatted file is being written by
+the Fortran runtime as a sequence of records.  Each record consists of
+an integer (of the default size [usually 32 or 64 bits]) giving the
+length of the following data in bytes, then the data itself, then the
+same integer as before.
+
+Examples
+--------
+
+To use the default endian and precision settings, one can just do:
+
+>>> f = FortranFile('filename')
+>>> x = f.readReals()
+
+One can read arrays with varying precisions:
+
+>>> f = FortranFile('filename')
+>>> x = f.readInts('h')
+>>> y = f.readInts('q')
+>>> z = f.readReals('f')
+
+Where the format codes are those used by Python's struct module.
+
+One can change the default endian-ness and header precision:
+
+>>> f = FortranFile('filename', endian='>', header_prec='l')
+
+for a file with little-endian data whose record headers are long
+integers.
+"""
+
+__docformat__ = "restructuredtext en"
+
+import numpy
+    
+try:
+    file
+except NameError:
+    # For python3 compatibility
+    from io import FileIO as file
+
+try:
+    bytes
+except NameError:
+    # For python2.x compatibility, I think it would have been nicer to
+    # "from __future__ import unicode_literals" and used b'' instead of bytes()
+    bytes = str
+    
+
+class FortranFile(file):
+
+    """File with methods for dealing with fortran unformatted data files"""
+
+    def _get_header_length(self):
+        return numpy.dtype(self._header_prec).itemsize
+    _header_length = property(fget=_get_header_length)
+
+    def _set_endian(self,c):
+        """Set endian to big (c='>') or little (c='<') or native (c='=')
+
+        :Parameters:
+          `c` : string
+            The endian-ness to use when reading from this file.
+        """
+        if c in '<>@=':
+            if c == '@':
+                c = '='
+            self._endian = c
+        else:
+            raise ValueError('Cannot set endian-ness')
+    def _get_endian(self):
+        return self._endian
+    ENDIAN = property(fset=_set_endian,
+                      fget=_get_endian,
+                      doc="Possible endian values are '<', '>', '@', '='"
+                     )
+
+    def _set_header_prec(self, prec):
+        if prec in 'hilq':
+            self._header_prec = prec
+        else:
+            raise ValueError('Cannot set header precision')
+    def _get_header_prec(self):
+        return self._header_prec
+    HEADER_PREC = property(fset=_set_header_prec,
+                           fget=_get_header_prec,
+                           doc="Possible header precisions are 'h', 'i', 'l', 'q'"
+                          )
+
+    def __init__(self, fname, endian='@', header_prec='i', *args, **kwargs):
+        """Open a Fortran unformatted file for writing.
+        
+        Parameters
+        ----------
+        endian : character, optional
+            Specify the endian-ness of the file.  Possible values are
+            '>', '<', '@' and '='.  See the documentation of Python's
+            struct module for their meanings.  The deafult is '>' (native
+            byte order)
+        header_prec : character, optional
+            Specify the precision used for the record headers.  Possible
+            values are 'h', 'i', 'l' and 'q' with their meanings from
+            Python's struct module.  The default is 'i' (the system's
+            default integer).
+
+        """
+        file.__init__(self, fname, *args, **kwargs)
+        self.ENDIAN = endian
+        self.HEADER_PREC = header_prec
+
+    def _read_exactly(self, num_bytes):
+        """Read in exactly num_bytes, raising an error if it can't be done."""
+        data = bytes()
+        while True:
+            l = len(data)
+            if l == num_bytes:
+                return data
+            else:
+                read_data = self.read(num_bytes - l)
+            if read_data == bytes():
+                raise IOError('Could not read enough data.'
+                              '  Wanted %d bytes, got %d.' % (num_bytes, l))
+            data += read_data
+
+    def _read_check(self):
+        return numpy.fromstring(self._read_exactly(self._header_length),
+                                dtype=self.ENDIAN+self.HEADER_PREC
+                               )[0]
+
+    def _write_check(self, number_of_bytes):
+        """Write the header for the given number of bytes"""
+        self.write(numpy.array(number_of_bytes, 
+                               dtype=self.ENDIAN+self.HEADER_PREC,).tostring()
+                  )
+
+    def readRecord(self):
+        """Read a single fortran record"""
+        l = self._read_check()
+        data_str = self._read_exactly(l)
+        check_size = self._read_check()
+        if check_size != l:
+            raise IOError('Error reading record from data file')
+        return data_str
+
+    def writeRecord(self,s):
+        """Write a record with the given bytes.
+
+        Parameters
+        ----------
+        s : the string to write
+
+        """
+        length_bytes = len(s)
+        self._write_check(length_bytes)
+        self.write(s)
+        self._write_check(length_bytes)
+
+    def readString(self):
+        """Read a string."""
+        return self.readRecord()
+
+    def writeString(self,s):
+        """Write a string
+
+        Parameters
+        ----------
+        s : the string to write
+        
+        """
+        self.writeRecord(s)
+
+    _real_precisions = 'df'
+
+    def readReals(self, prec='f'):
+        """Read in an array of real numbers.
+        
+        Parameters
+        ----------
+        prec : character, optional
+            Specify the precision of the array using character codes from
+            Python's struct module.  Possible values are 'd' and 'f'.
+            
+        """
+        
+        _numpy_precisions = {'d': numpy.float64,
+                             'f': numpy.float32
+                            }
+
+        if prec not in self._real_precisions:
+            raise ValueError('Not an appropriate precision')
+            
+        data_str = self.readRecord()
+        return numpy.fromstring(data_str, dtype=self.ENDIAN+prec)
+
+    def writeReals(self, reals, prec='f'):
+        """Write an array of floats in given precision
+
+        Parameters
+        ----------
+        reals : array
+            Data to write
+        prec` : string
+            Character code for the precision to use in writing
+        """
+        if prec not in self._real_precisions:
+            raise ValueError('Not an appropriate precision')
+        
+        nums = numpy.array(reals, dtype=self.ENDIAN+prec)
+        self.writeRecord(nums.tostring())
+    
+    _int_precisions = 'hilq'
+
+    def readInts(self, prec='i'):
+        """Read an array of integers.
+        
+        Parameters
+        ----------
+        prec : character, optional
+            Specify the precision of the data to be read using 
+            character codes from Python's struct module.  Possible
+            values are 'h', 'i', 'l' and 'q'
+            
+        """
+        if prec not in self._int_precisions:
+            raise ValueError('Not an appropriate precision')
+            
+        data_str = self.readRecord()
+        return numpy.fromstring(data_str, dtype=self.ENDIAN+prec)
+
+    def writeInts(self, ints, prec='i'):
+        """Write an array of integers in given precision
+
+        Parameters
+        ----------
+        reals : array
+            Data to write
+        prec : string
+            Character code for the precision to use in writing
+        """
+        if prec not in self._int_precisions:
+            raise ValueError('Not an appropriate precision')
+        
+        nums = numpy.array(ints, dtype=self.ENDIAN+prec)
+        self.writeRecord(nums.tostring())
diff --git a/ase/io/gen.py b/ase/io/gen.py
new file mode 100644
index 0000000..44622cf
--- /dev/null
+++ b/ase/io/gen.py
@@ -0,0 +1,134 @@
+"""Extension to ASE: read and write structures in GEN format
+
+Refer to DFTB+ manual for GEN format description.
+
+Note: GEN format only supports single snapshot.
+"""
+
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+
+def read_gen(fileobj):
+    """Read structure in GEN format (refer to DFTB+ manual).
+       Multiple snapshot are not allowed. """
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    image = Atoms()    
+    lines = fileobj.readlines()
+    line = lines[0].split()
+    natoms = int(line[0])
+    if line[1] == 'S':
+        supercell = True
+    elif line[1] == 'C':
+        supercell = False
+    else:
+        raise IOError('Error in line #1: only C (Cluster) or S (Supercell) ' +
+                      'are valid options')
+
+    # Read atomic symbols
+    line = lines[1].split()
+    # Define a dictionary with symbols-id
+    symboldict = dict()
+    symbolid = 1
+    for symb in line:
+        symboldict[symbolid] = symb
+        symbolid += 1
+
+    # Read atoms (GEN format supports only single snapshot)
+    del lines[:2]
+    positions = []
+    symbols = []
+    for line in lines[:natoms]:
+        dummy, symbolid, x, y, z = line.split()[:5]
+        symbols.append(symboldict[int(symbolid)])
+        positions.append([float(x), float(y), float(z)])
+    image = Atoms(symbols=symbols, positions=positions)
+    del lines[:natoms]
+
+    # If Supercell, parse periodic vectors
+    if not supercell:
+        return image
+    else:
+        # Dummy line: line after atom positions is not uniquely defined 
+        # in gen implementations, and not necessary in DFTB package
+        del lines[:1]
+        image.set_pbc([True, True, True])
+        p = []
+        for i in range(3):
+            x, y, z = lines[i].split()[:3]
+            p.append([float(x), float(y), float(z)])
+        image.set_cell([(p[0][0], p[0][1], p[0][2]), (p[1][0], p[1][1], 
+                p[1][2]), (p[2][0], p[2][1], p[2][2])])
+        return image
+        
+
+def write_gen(fileobj, images):
+    """Write structure in GEN format (refer to DFTB+ manual).
+       Multiple snapshots are not allowed. """
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    # Images is kept in a list but a size > 0 is not allowed
+    # as GEN format doesn't support multiple snapshots.
+    # Images is used as a list for consistency with the other 
+    # output modules
+    if len(images) != 1:
+        raise ValueError('images contains more than one structure\n' +
+                         'GEN format supports only single snapshot output')
+
+    symbols = images[0].get_chemical_symbols()
+
+    # Define a dictionary with symbols-id
+    symboldict = dict() 
+    for sym in symbols:
+        if not (sym in symboldict):
+            symboldict[sym] = len(symboldict) + 1
+    # An ordered symbol list is needed as ordered dictionary
+    # is just available in python 2.7
+    orderedsymbols = list(['null'] * len(symboldict.keys()))
+    for sym in symboldict.keys():
+        orderedsymbols[symboldict[sym] - 1] = sym
+    
+    
+    # Check whether the structure is periodic
+    # GEN cannot describe periodicity in one or two direction,
+    # a periodic structure is considered periodic in all the
+    # directions. If your structure is not periodical in all
+    # the directions, be sure you have set big periodicity
+    # vectors in the non-periodic directions
+    if images[0].pbc.any():
+        pb_flag = 'S'
+    else:
+        pb_flag = 'C'
+
+    natoms = len(symbols)
+    ind = 0
+    for atoms in images:
+        fileobj.write('%d  %-5s\n' % (natoms, pb_flag))
+        for s in orderedsymbols:
+            fileobj.write('%-5s' % s)
+        fileobj.write('\n')
+        for sym, (x, y, z) in zip(symbols, atoms.get_positions()):
+            ind += 1
+            symbolid = symboldict[sym]
+            fileobj.write('%-6d %d %22.15f %22.15f %22.15f\n' % (ind, 
+                          symbolid, x, y, z))
+    if images[0].pbc.any():
+        fileobj.write('%22.15f %22.15f %22.15f \n' % (0.0, 0.0, 0.0))
+        fileobj.write('%22.15f %22.15f %22.15f \n' % 
+                      (images[0].get_cell()[0][0], 
+                       images[0].get_cell()[0][1], 
+                       images[0].get_cell()[0][2]))
+        fileobj.write('%22.15f %22.15f %22.15f \n' % 
+                      (images[0].get_cell()[1][0], 
+                       images[0].get_cell()[1][1], 
+                       images[0].get_cell()[1][2]))
+        fileobj.write('%22.15f %22.15f %22.15f \n' % 
+                      (images[0].get_cell()[2][0], 
+                       images[0].get_cell()[2][1], 
+                       images[0].get_cell()[2][2]))
diff --git a/ase/io/gpawtext.py b/ase/io/gpawtext.py
new file mode 100644
index 0000000..261adf2
--- /dev/null
+++ b/ase/io/gpawtext.py
@@ -0,0 +1,148 @@
+import numpy as np
+from ase.atoms import Atom, Atoms
+from ase.calculators.singlepoint import SinglePointDFTCalculator
+from ase.calculators.singlepoint import SinglePointKPoint
+
+def read_gpaw_text(fileobj, index=-1):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    def index_startswith(lines, string):
+        for i, line in enumerate(lines):
+            if line.startswith(string):
+                return i
+        raise ValueError
+
+    lines = fileobj.readlines()
+    images = []
+    while True:
+        try:
+            i = lines.index('Unit Cell:\n')
+        except ValueError:
+            pass
+        else:
+            cell = []
+            pbc = []
+            for line in lines[i + 3:i + 6]:
+                words = line.split()
+                if len(words) == 5:  # old format
+                    cell.append(float(words[2]))
+                    pbc.append(words[1] == 'yes')
+                else:                # new format with GUC
+                    cell.append([float(word) for word in words[3:6]])
+                    pbc.append(words[2] == 'yes')
+            
+        try:
+            i = lines.index('Positions:\n')
+        except ValueError:
+            break
+
+        atoms = Atoms(cell=cell, pbc=pbc)
+        for line in lines[i + 1:]:
+            words = line.split()
+            if len(words) != 5:
+                break
+            n, symbol, x, y, z = words
+            symbol = symbol.split('.')[0]
+            atoms.append(Atom(symbol, [float(x), float(y), float(z)]))
+        lines = lines[i + 5:]
+        try:
+            i = lines.index('-------------------------\n')
+        except ValueError:
+            e = None
+        else:
+            line = lines[i + 9]
+            assert line.startswith('Zero Kelvin:')
+            e = float(line.split()[-1])
+        try:
+            ii = index_startswith(lines, 'Fermi Level:')
+        except ValueError:
+            eFermi = None
+        else:
+            try:
+                eFermi = float(lines[ii].split()[2])
+            except ValueError: # we have two Fermi levels
+                fields = lines[ii].split()
+                def strip(string):
+                    for rubbish in '[],':
+                        string = string.replace(rubbish, '')
+                    return string
+                eFermi = [float(strip(fields[2])),
+                          float(strip(fields[3])) ]
+        # read Eigenvalues and occupations
+        ii1 = ii2 = 1e32
+        try:
+            ii1 = index_startswith(lines, ' Band   Eigenvalues  Occupancy')
+        except ValueError:
+            pass
+        try:
+            ii2 = index_startswith(lines, ' Band  Eigenvalues  Occupancy')
+        except ValueError:
+            pass
+        ii = min(ii1, ii2)
+        if ii == 1e32:
+            kpts = None
+        else:
+            ii += 1
+            words = lines[ii].split()
+            vals = []
+            while(len(words) > 2):
+                vals.append([float(word) for word in words])
+                ii += 1
+                words = lines[ii].split()
+            vals = np.array(vals).transpose()
+            kpts = [SinglePointKPoint(0, 0)]
+            kpts[0].eps_n = vals[1]
+            kpts[0].f_n = vals[2]
+            if vals.shape[0] > 3:
+                kpts.append(SinglePointKPoint(0, 1))
+                kpts[1].eps_n = vals[3]
+                kpts[1].f_n = vals[4]
+        # read charge
+        try:
+            ii = index_startswith(lines, 'Total Charge:')
+        except ValueError:
+            q = None
+        else:
+            q = float(lines[ii].split()[2])
+        try:
+            ii = index_startswith(lines, 'Local Magnetic Moments')
+        except ValueError:
+            magmoms = None
+        else:
+            magmoms = []
+            for i in range(ii + 1, ii + 1 + len(atoms)):
+                iii, magmom = lines[i].split()[:2]
+                magmoms.append(float(magmom))
+        try:
+            ii = lines.index('Forces in eV/Ang:\n')
+        except ValueError:
+            f = None
+        else:
+            f = []
+            for i in range(ii + 1, ii + 1 + len(atoms)):
+                try:
+                    x, y, z = lines[i].split()[-3:]
+                    f.append((float(x), float(y), float(z)))
+                except (ValueError, IndexError), m:
+                    raise IOError('Malformed GPAW log file: %s' % m)
+
+        if len(images) > 0 and e is None:
+            break
+
+        if e is not None or f is not None:
+            calc = SinglePointDFTCalculator(e, f, None, magmoms, atoms, eFermi)
+            if kpts is not None:
+                calc.kpts = kpts
+            atoms.set_calculator(calc)
+        if q is not None and len(atoms) > 0:
+            n = len(atoms)
+            atoms.set_charges([q / n] * n)
+
+        images.append(atoms)
+        lines = lines[i:]
+
+    if len(images) == 0:
+        raise IOError('Corrupted GPAW-text file!')
+    
+    return images[index]
diff --git a/ase/io/iwm.py b/ase/io/iwm.py
new file mode 100644
index 0000000..b3d95e7
--- /dev/null
+++ b/ase/io/iwm.py
@@ -0,0 +1,45 @@
+from math import pi, cos, sin, sqrt, acos
+import numpy as np
+
+from ase.data import chemical_symbols
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+iwm_symbols = {'1' : 'C',
+               '2' : 'Au',
+               '5' : 'Ag'}
+
+def read_iwm(fileobj, index=-1):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+    L1 = lines[1].split()
+    if len(L1) == 1:
+        del lines[:3]
+        natoms = int(L1[0])
+    else:
+        natoms = len(lines)
+    images = []
+
+    positions = []
+    symbols = []
+    for line in lines[:natoms]:
+        symbol, mass, x, y, z = line.split()[:5]
+        if symbol in iwm_symbols:
+            symbols.append(iwm_symbols[symbol])
+        else:
+            symbols.append(chemical_symbols[int(symbol)])
+        positions.append([float(x), float(y), float(z)])
+    
+    del(lines[natoms:3 * natoms + 3])
+    
+    cell = []
+    for line in lines[natoms:natoms+3]:
+        x, y, z = line.split()[:3]
+        cell.append(np.array([float(x), float(y), float(z)]))
+      
+    images.append(Atoms(symbols=symbols, positions=positions, cell=cell))
+
+    return images[index]
+
diff --git a/ase/io/lammps.py b/ase/io/lammps.py
new file mode 100644
index 0000000..f505ec6
--- /dev/null
+++ b/ase/io/lammps.py
@@ -0,0 +1,100 @@
+from ase.atoms import Atoms
+from ase.quaternions import Quaternions
+from ase.parallel import paropen
+
+def read_lammps_dump(fileobj, index=-1):
+    """Method which reads a LAMMPS dump file."""
+    if isinstance(fileobj, str):
+        f = paropen(fileobj)
+    else:
+        f = fileobj
+
+    # load everything into memory
+    lines = f.readlines()
+
+    natoms = 0
+    images = []
+
+    while len(lines) > natoms:
+        line = lines.pop(0)
+
+        if 'ITEM: TIMESTEP' in line:
+            n_atoms = 0
+            lo = [] ; hi = [] ; tilt = []
+            id = [] ; type = []
+            positions = []
+            velocities = [] 
+            forces = []
+            quaternions = []
+
+        if 'ITEM: NUMBER OF ATOMS' in line:
+            line = lines.pop(0)
+            natoms = int(line.split()[0])
+            
+        if 'ITEM: BOX BOUNDS' in line:
+            # save labels behind "ITEM: BOX BOUNDS" in triclinic case (>=lammps-7Jul09)
+            tilt_items = line.split()[3:]
+            for i in range(3):
+                line = lines.pop(0)
+                fields = line.split()
+                lo.append(float(fields[0]))
+                hi.append(float(fields[1]))
+                if (len(fields) >= 3):
+                    tilt.append(float(fields[2]))
+
+            # determine cell tilt (triclinic case!)
+            if (len(tilt) >= 3):
+                # for >=lammps-7Jul09 use labels behind "ITEM: BOX BOUNDS" to assign tilt (vector) elements ...
+                if (len(tilt_items) >= 3):
+                    xy = tilt[tilt_items.index('xy')]
+                    xz = tilt[tilt_items.index('xz')]
+                    yz = tilt[tilt_items.index('yz')]
+                # ... otherwise assume default order in 3rd column (if the latter was present)
+                else:
+                    xy = tilt[0]
+                    xz = tilt[1]
+                    yz = tilt[2]
+            else:
+                xy = xz = yz = 0
+            xhilo = (hi[0] - lo[0]) - xy - xz
+            yhilo = (hi[1] - lo[1]) - yz
+            zhilo = (hi[2] - lo[2])
+
+            cell = [[xhilo,0,0],[xy,yhilo,0],[xz,yz,zhilo]]
+
+        def add_quantity(fields, var, labels):
+            for label in labels:
+                if label not in atom_attributes:
+                    return
+            var.append([float(fields[atom_attributes[label]])
+                        for label in labels])
+                
+        if 'ITEM: ATOMS' in line:
+            # (reliably) identify values by labels behind "ITEM: ATOMS" - requires >=lammps-7Jul09
+            # create corresponding index dictionary before iterating over atoms to (hopefully) speed up lookups...
+            atom_attributes = {}
+            for (i, x) in enumerate(line.split()[2:]):
+                atom_attributes[x] = i
+            for n in range(natoms):
+                line = lines.pop(0)
+                fields = line.split()
+                id.append( int(fields[atom_attributes['id']]) )
+                type.append( int(fields[atom_attributes['type']]) )
+                add_quantity(fields, positions, ['x', 'y', 'z'])
+                add_quantity(fields, velocities, ['vx', 'vy', 'vz'])
+                add_quantity(fields, forces, ['fx', 'fy', 'fz'])
+                add_quantity(fields, quaternions, ['c_q[1]', 'c_q[2]',
+                                                   'c_q[3]', 'c_q[4]'])
+
+            if len(quaternions):
+                images.append(Quaternions(symbols=type,
+                                          positions=positions,
+                                          cell=cell,
+                                          quaternions=quaternions))
+            else:
+                images.append(Atoms(symbols=type,
+                                    positions=positions,
+                                    cell=cell))
+
+    return images[index]
+
diff --git a/ase/io/mol.py b/ase/io/mol.py
new file mode 100644
index 0000000..ba49574
--- /dev/null
+++ b/ase/io/mol.py
@@ -0,0 +1,23 @@
+from math import pi, cos, sin, sqrt, acos
+
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+
+def read_mol(fileobj, index=-1):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+    del(lines[:3])
+    L1 = lines[0].split()
+    del(lines[0])
+    natoms = int(L1[0])
+    positions = []
+    symbols = []
+    for line in lines[:natoms]:
+            x, y, z, symbol = line.split()[:4]
+            symbols.append(symbol)
+            positions.append([float(x), float(y), float(z)])
+    return Atoms(symbols=symbols, positions=positions)
+
diff --git a/ase/io/netcdf.py b/ase/io/netcdf.py
new file mode 100644
index 0000000..2a81e8c
--- /dev/null
+++ b/ase/io/netcdf.py
@@ -0,0 +1,112 @@
+"""Read and write ASE2's netCDF trajectory files."""
+
+from ase.io.pupynere import NetCDFFile
+from ase.atoms import Atoms
+from ase.calculators.singlepoint import SinglePointCalculator
+
+
+def read_netcdf(filename, index=-1):
+    nc = NetCDFFile(filename)
+    dims = nc.dimensions
+    vars = nc.variables
+
+    positions = vars['CartesianPositions']
+    numbers = vars['AtomicNumbers'][:]
+    pbc = vars['BoundaryConditions'][:]
+    cell = vars['UnitCell']
+    tags = vars['Tags'][:]
+    if not tags.any():
+        tags = None
+    magmoms = vars['MagneticMoments'][:]
+    if not magmoms.any():
+        magmoms = None
+
+    nimages = positions.shape[0]
+
+    attach_calculator = False
+    if 'PotentialEnergy' in vars:
+        energy = vars['PotentialEnergy']
+        attach_calculator = True
+    else:
+        energy = nimages * [None]
+
+    if 'CartesianForces' in vars:
+        forces = vars['CartesianForces']
+        attach_calculator = True
+    else:
+        forces = nimages * [None]
+
+    if 'Stress' in vars:
+        stress = vars['Stress']
+        attach_calculator = True
+    else:
+        stress = nimages * [None]
+
+    if isinstance(index, int):
+        indices = [index]
+    else:
+        indices = range(nimages)[index]
+
+    images = []
+    for i in indices:
+        atoms = Atoms(positions=positions[i],
+                      numbers=numbers,
+                      cell=cell[i],
+                      pbc=pbc,
+                      tags=tags, magmoms=magmoms)
+
+        if attach_calculator:
+            calc = SinglePointCalculator(energy[i], forces[i], stress[i],
+                                         None, atoms) ### Fixme magmoms
+            atoms.set_calculator(calc)
+            
+        images.append(atoms)
+        
+    if isinstance(index, int):
+        return images[0]
+    else:
+        return images
+
+
+class LOA:
+    def __init__(self, images):
+        self.set_atoms(images[0])
+
+    def __len__(self):
+        return len(self.atoms)
+    
+    def set_atoms(self, atoms):
+        self.atoms = atoms
+        
+    def GetPotentialEnergy(self):
+        return self.atoms.get_potential_energy()
+
+    def GetCartesianForces(self):
+        return self.atoms.get_forces()
+
+    def GetUnitCell(self):
+        return self.atoms.get_cell()
+
+    def GetAtomicNumbers(self):
+        return self.atoms.get_atomic_numbers()
+
+    def GetCartesianPositions(self):
+        return self.atoms.get_positions()
+
+    def GetBoundaryConditions(self):
+        return self.atoms.get_pbc()
+    
+
+def write_netcdf(filename, images):
+    from ASE.Trajectories.NetCDFTrajectory import NetCDFTrajectory
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+        
+    loa = LOA(images)
+    traj = NetCDFTrajectory(filename, loa)
+    for atoms in images:
+        loa.set_atoms(atoms)
+        traj.Update()
+    traj.Close()
+
diff --git a/ase/io/nwchem.py b/ase/io/nwchem.py
new file mode 100644
index 0000000..7a8ee7e
--- /dev/null
+++ b/ase/io/nwchem.py
@@ -0,0 +1,65 @@
+from cStringIO import StringIO
+from ase.atoms import Atoms
+from ase.io.xyz import read_xyz
+
+def read_nwchem(filename):
+    """Method to read geometry from a nwchem output
+    """
+    from ase import Atoms, Atom
+
+    if isinstance(filename, str):
+        f = open(filename)
+
+    lines = f.readlines()
+
+    i = 0
+    while i < len(lines):
+        if lines[i].find('XYZ format geometry') >=0:
+            natoms = int(lines[i + 2].split()[0])
+            string = ''
+            for j in range(2, natoms + 4):
+                xyzstring = lines[i + j]
+                symbol = xyzstring.split()[0].strip()
+                # replace bq ghost with X: MDTMP can we do better?
+                if symbol.startswith('bq'):
+                    xyzstring = xyzstring.replace(symbol, 'X')
+                string += xyzstring
+            atoms = read_xyz(StringIO(string))
+            i += natoms + 4
+        else:
+            i += 1
+
+    if type(filename) == str:
+        f.close()
+
+    return atoms
+
+def write_nwchem(filename, atoms, geometry=None):
+    """Method to write nwchem coord file
+    """
+
+    import numpy as np
+
+    if isinstance(filename, str):
+        f = open(filename, 'w')
+    else: # Assume it's a 'file-like object'
+        f = filename
+
+    # autosym and autoz are defaults
+    # http://www.nwchem-sw.org/index.php/Geometry
+    # geometry noautoz results in higher memory demand!
+    # http://www.emsl.pnl.gov/docs/nwchem/nwchem-support/2010/10/0060.RE:_NWCHEM_Geometry_problem_fwd_
+    if geometry is not None:
+        f.write('geometry ' + str(geometry) + '\n')
+    else:
+        f.write('geometry\n')
+    for atom in atoms:
+        if atom.tag == -71: # 71 is ascii G (Ghost)
+            symbol = 'bq' + atom.symbol
+        else:
+            symbol = atom.symbol
+        f.write('  ' + symbol + ' ' +
+                str(atom.position[0]) + ' ' +
+                str(atom.position[1]) + ' ' +
+                str(atom.position[2]) + '\n' )
+    f.write('end\n')
diff --git a/ase/io/pdb.py b/ase/io/pdb.py
new file mode 100644
index 0000000..a0ad46f
--- /dev/null
+++ b/ase/io/pdb.py
@@ -0,0 +1,73 @@
+import numpy as np 
+
+from ase.atoms import Atom, Atoms
+from ase.parallel import paropen
+
+"""Module to read and write atoms in PDB file format"""
+
+
+def read_pdb(fileobj, index=-1):
+    """Read PDB files.
+
+    The format is assumed to follow the description given in
+    http://www.wwpdb.org/documentation/format32/sect9.html."""
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    images = []
+    atoms = Atoms()
+    for line in fileobj.readlines():
+        if line.startswith('ATOM') or line.startswith('HETATM'):
+            try:
+                # Atom name is arbitrary and does not necessarily contain the element symbol.
+                # The specification requires the element symbol to be in columns 77+78.
+                symbol = line[76:78].strip().lower().capitalize()
+                words = line[30:55].split()
+                position = np.array([float(words[0]), 
+                                     float(words[1]),
+                                     float(words[2])])
+                atoms.append(Atom(symbol, position))
+            except:
+                pass
+        if line.startswith('ENDMDL'):
+            images.append(atoms)
+            atoms = Atoms()
+    if len(images) == 0:
+        images.append(atoms)
+    return images[index]
+
+def write_pdb(fileobj, images):
+    """Write images to PDB-file.
+
+    The format is assumed to follow the description given in
+    http://www.wwpdb.org/documentation/format32/sect9.html."""
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    if images[0].get_pbc().any():
+        from ase.lattice.spacegroup.cell import cell_to_cellpar
+        cellpar = cell_to_cellpar( images[0].get_cell())
+        # ignoring Z-value, using P1 since we have all atoms defined explicitly
+        format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n'
+        fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3], cellpar[4], cellpar[5]))
+
+    #         1234567 123 6789012345678901   89   67   456789012345678901234567 890
+    format = 'ATOM  %5d %4s MOL     1    %8.3f%8.3f%8.3f  1.00  0.00          %2s  \n'
+
+    # RasMol complains if the atom index exceeds 100000. There might
+    # be a limit of 5 digit numbers in this field.
+    MAXNUM = 100000
+
+    symbols = images[0].get_chemical_symbols()
+    natoms = len(symbols)
+    
+    for n,atoms in enumerate(images):
+        fileobj.write('MODEL     '+str(n+1)+'\n')
+        p = atoms.get_positions()
+        for a in range(natoms):
+            x, y, z = p[a]
+            fileobj.write(format % (a % MAXNUM, symbols[a], x, y, z, symbols[a].rjust(2)))
+        fileobj.write('ENDMDL\n')
diff --git a/ase/io/plt.py b/ase/io/plt.py
new file mode 100644
index 0000000..652f011
--- /dev/null
+++ b/ase/io/plt.py
@@ -0,0 +1,60 @@
+import numpy as np
+
+from ase.atoms import Atoms
+
+def write_plt(filename, atoms, data):
+    if isinstance(atoms, Atoms):
+        cell = atoms.get_cell()
+    else:
+        cell = np.asarray(atoms, float)
+
+    if cell.ndim == 2:
+        c = cell.copy()
+        cell = c.diagonal()
+        c.flat[::4] = 0.0
+        if c.any():
+            raise ValueError('Unit cell must be orthorhombic!')
+
+    f = open(filename, 'w')
+    np.array([3, 4], np.int32).tofile(f)
+
+    dims = np.array(data.shape, np.int32)
+    dims[::-1].tofile(f)
+
+    for n, L in zip(dims[::-1], cell[::-1]):
+        if n % 2 == 0:
+            d = L / n
+            np.array([0.0, L - d], np.float32).tofile(f)
+        else:
+            d = L / (n + 1)
+            np.array([d, L - d], np.float32).tofile(f)
+
+    if data.dtype == complex:
+        data = np.abs(data)
+    data.astype(np.float32).T.tofile(f)
+    f.close()
+
+def read_plt(fileobj):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj, 'rb')
+        
+    # dummy numbers
+    np.fromfile(fileobj, dtype=np.int32, count=2)
+    # read dimensions
+    dims = np.fromfile(fileobj, dtype=np.int32, count=3)
+    size = dims[0] * dims[1] * dims[2]
+
+    # read cell
+    cell = np.zeros((3,3), np.float32)
+    for c in range(3):
+        beg, Lmd = np.fromfile(fileobj, dtype=np.float32, count=2)
+        n = dims[c]
+        if n % 2 == 0:
+            cell[2 - c, 2 - c] = Lmd / (1 - 1. / n)
+        else:
+           cell[2 - c, 2 - c] = Lmd / (1 - 1. / (n + 1))
+
+    # read data
+    data = np.fromfile(fileobj, dtype=np.float32)
+    return data.reshape(dims).T, cell
+    
diff --git a/ase/io/png.py b/ase/io/png.py
new file mode 100644
index 0000000..97d4f62
--- /dev/null
+++ b/ase/io/png.py
@@ -0,0 +1,40 @@
+from ase.io.eps import EPS
+
+
+class PNG(EPS):
+    def write_header(self):
+        from matplotlib.backends.backend_agg import RendererAgg
+
+        try:
+            from matplotlib.transforms import Value
+        except ImportError:
+            dpi = 72
+        else:
+            dpi = Value(72)
+
+        self.renderer = RendererAgg(self.w, self.h, dpi)
+
+        #self.gc = GraphicsContextBase()
+        #self.gc.set_linewidth(2)
+
+    def write_trailer(self):
+        renderer = self.renderer
+        if hasattr(renderer._renderer, 'write_png'):
+            # Old version of matplotlib:
+            renderer._renderer.write_png(self.filename)
+        else:
+            x = renderer._renderer.buffer_rgba(0, 0)
+            from matplotlib import _png
+            _png.write_png(renderer._renderer.buffer_rgba(0, 0),
+                           renderer.width, renderer.height,
+                           self.filename, 72)
+
+
+def write_png(filename, atoms, **parameters):
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            raise RuntimeError("Don't know how to save more than "+
+                               "one image to PNG image!")
+        else:
+            atoms = atoms[0]
+    PNG(atoms, **parameters).write(filename)
diff --git a/ase/io/pov.py b/ase/io/pov.py
new file mode 100644
index 0000000..38feea0
--- /dev/null
+++ b/ase/io/pov.py
@@ -0,0 +1,281 @@
+"""
+Module for povray file format support.
+
+See http://www.povray.org/ for details on the format.
+"""
+import os
+
+import numpy as np
+
+from ase.io.eps import EPS
+from ase.data import chemical_symbols
+from ase.constraints import FixAtoms
+
+def pa(array):
+    """Povray array syntax"""
+    return '<% 6.2f, % 6.2f, % 6.2f>' % tuple(array)
+
+
+def pc(array):
+    """Povray color syntax"""
+    if type(array) == str:
+        return 'color ' + array
+    if type(array) == float:
+        return 'rgb <%.2f>*3' % array
+    if len(array) == 3:
+        return 'rgb <%.2f, %.2f, %.2f>' % tuple(array)
+    if len(array) == 4: # filter
+        return 'rgbf <%.2f, %.2f, %.2f, %.2f>' % tuple(array)
+    if len(array) == 5: # filter and transmit
+        return 'rgbft <%.2f, %.2f, %.2f, %.2f, %.2f>' % tuple(array)
+
+
+def get_bondpairs(atoms, radius=1.1):
+    """Get all pairs of bonding atoms
+
+    Return all pairs of atoms which are closer than radius times the
+    sum of their respective covalent radii.  The pairs are returned as
+    tuples::
+
+      (a, b, (i1, i2, i3))
+
+    so that atoms a bonds to atom b displaced by the vector::
+
+        _     _     _
+      i c + i c + i c ,
+       1 1   2 2   3 3
+
+    where c1, c2 and c3 are the unit cell vectors and i1, i2, i3 are
+    integers."""
+    
+    from ase.data import covalent_radii
+    from ase.calculators.neighborlist import NeighborList
+    cutoffs = radius * covalent_radii[atoms.numbers]
+    nl = NeighborList(cutoffs=cutoffs, self_interaction=False)
+    nl.update(atoms)
+    bondpairs = []
+    for a in range(len(atoms)):
+        indices, offsets = nl.get_neighbors(a)
+        bondpairs.extend([(a, a2, offset)
+                          for a2, offset in zip(indices, offsets)])
+    return bondpairs
+
+
+class POVRAY(EPS):
+    default_settings = {
+        # x, y is the image plane, z is *out* of the screen
+        'display'        : True, # Display while rendering
+        'pause'          : True, # Pause when done rendering (only if display)
+        'transparent'    : True, # Transparent background
+        'canvas_width'   : None, # Width of canvas in pixels
+        'canvas_height'  : None, # Height of canvas in pixels 
+        'camera_dist'    : 50.,  # Distance from camera to front atom
+        'image_plane'    : None, # Distance from front atom to image plane
+        'camera_type'    : 'orthographic', # perspective, ultra_wide_angle
+        'point_lights'   : [],             # [[loc1, color1], [loc2, color2],...]
+        'area_light'     : [(2., 3., 40.), # location
+                            'White',       # color
+                            .7, .7, 3, 3], # width, height, Nlamps_x, Nlamps_y
+        'background'     : 'White',        # color
+        'textures'       : None, # Length of atoms list of texture names
+        'celllinewidth'  : 0.05, # Radius of the cylinders representing the cell
+        'bondlinewidth'  : 0.10, # Radius of the cylinders representing the bonds
+        'bondatoms'      : [],   # [[atom1, atom2], ... ] pairs of bonding atoms
+        'exportconstraints' : False}  # honour FixAtom requests and mark relevant atoms?
+
+    def __init__(self, atoms, scale=1.0, **parameters):
+        for k, v in self.default_settings.items():
+            setattr(self, k, parameters.pop(k, v))
+        EPS.__init__(self, atoms, scale=scale, **parameters)
+        constr = atoms.constraints
+        self.constrainatoms = []
+        for c in constr:
+            if isinstance(c,FixAtoms):
+                for n,i in enumerate(c.index):
+                    if i: self.constrainatoms += [n]
+
+    def cell_to_lines(self, A):
+        return np.empty((0, 3)), None, None
+
+    def write(self, filename, **settings):
+        # Determine canvas width and height
+        ratio = float(self.w) / self.h
+        if self.canvas_width is None:
+            if self.canvas_height is None:
+                self.canvas_width = min(self.w * 15, 640)
+            else:
+                self.canvas_width = self.canvas_height * ratio
+        elif self.canvas_height is not None:
+            raise RuntimeError, "Can't set *both* width and height!"
+
+        # Distance to image plane from camera
+        if self.image_plane is None:
+            if self.camera_type == 'orthographic':
+                self.image_plane = 1 - self.camera_dist
+            else:
+                self.image_plane = 0
+        self.image_plane += self.camera_dist
+
+        # Produce the .ini file
+        if filename.endswith('.pov'):
+            ini = open(filename[:-4] + '.ini', 'w').write
+        else:
+            ini = open(filename + '.ini', 'w').write
+        ini('Input_File_Name=%s\n' % filename)
+        ini('Output_to_File=True\n')
+        ini('Output_File_Type=N\n')
+        ini('Output_Alpha=%s\n' % self.transparent)
+        ini('; if you adjust Height, and width, you must preserve the ratio\n')
+        ini('; Width / Height = %s\n' % repr(ratio))
+        ini('Width=%s\n' % self.canvas_width)
+        ini('Height=%s\n' % (self.canvas_width / ratio))
+        ini('Antialias=True\n')
+        ini('Antialias_Threshold=0.1\n')
+        ini('Display=%s\n' % self.display)
+        ini('Pause_When_Done=%s\n' % self.pause)
+        ini('Verbose=False\n')
+        del ini
+
+        # Produce the .pov file
+        w = open(filename, 'w').write
+        w('#include "colors.inc"\n')
+        w('#include "finish.inc"\n')
+        w('\n')
+        w('global_settings {assumed_gamma 1 max_trace_level 6}\n')
+        w('background {%s}\n' % pc(self.background))
+        w('camera {%s\n' % self.camera_type)
+        w('  right -%.2f*x up %.2f*y\n' % (self.w, self.h))
+        w('  direction %.2f*z\n' % self.image_plane)
+        w('  location <0,0,%.2f> look_at <0,0,0>}\n' % self.camera_dist)
+        for loc, rgb in self.point_lights:
+            w('light_source {%s %s}\n' % (pa(loc), pc(rgb)))
+
+        if self.area_light is not None:
+            loc, color, width, height, nx, ny = self.area_light
+            w('light_source {%s %s\n' % (pa(loc), pc(color)))
+            w('  area_light <%.2f, 0, 0>, <0, %.2f, 0>, %i, %i\n' % (
+                width, height, nx, ny))
+            w('  adaptive 1 jitter}\n')
+
+        w('\n')
+        w('#declare simple = finish {phong 0.7}\n')
+        w('#declare pale = finish {'
+          'ambient .5 '
+          'diffuse .85 '   
+          'roughness .001 '
+          'specular 0.200 }\n')
+        w('#declare intermediate = finish {'
+          'ambient 0.3 '
+          'diffuse 0.6 '
+          'specular 0.10 '
+          'roughness 0.04 }\n')
+        w('#declare vmd = finish {'
+          'ambient .0 '
+          'diffuse .65 '
+          'phong 0.1 '
+          'phong_size 40. '
+          'specular 0.500 }\n')
+        w('#declare jmol = finish {'
+          'ambient .2 '
+          'diffuse .6 '
+          'specular 1 '
+          'roughness .001 '
+          'metallic}\n')
+        w('#declare ase2 = finish {'
+          'ambient 0.05 '
+          'brilliance 3 '
+          'diffuse 0.6 '
+          'metallic '
+          'specular 0.70 '
+          'roughness 0.04 '
+          'reflection 0.15}\n')
+        w('#declare ase3 = finish {'
+          'ambient .15 '
+          'brilliance 2 '
+          'diffuse .6 '
+          'metallic '
+          'specular 1. '
+          'roughness .001 '
+          'reflection .0}\n')
+        w('#declare glass = finish {'
+          'ambient .05 '
+          'diffuse .3 '
+          'specular 1. '
+          'roughness .001}\n')
+        w('#declare Rcell = %.3f;\n' % self.celllinewidth)
+        w('#declare Rbond = %.3f;\n' % self.bondlinewidth)
+        w('\n')
+        w('#macro atom(LOC, R, COL, FIN)\n')
+        w('  sphere{LOC, R texture{pigment{COL} finish{FIN}}}\n')
+        w('#end\n')
+        w('#macro constrain(LOC, R, COL, FIN)\n')
+        w('union{torus{R, Rcell rotate 45*z texture{pigment{COL} finish{FIN}}}\n')
+        w('      torus{R, Rcell rotate -45*z texture{pigment{COL} finish{FIN}}}\n')
+        w('      translate LOC}\n')
+        w('#end\n')
+        w('\n')
+        
+        z0 = self.X[:, 2].max()
+        self.X -= (self.w / 2, self.h / 2, z0)
+
+        # Draw unit cell
+        if self.C is not None:
+            self.C -= (self.w / 2, self.h / 2, z0)
+            self.C.shape = (2, 2, 2, 3)
+            for c in range(3):
+                for j in ([0, 0], [1, 0], [1, 1], [0, 1]):
+                    w('cylinder {')
+                    for i in range(2):
+                        j.insert(c, i)
+                        w(pa(self.C[tuple(j)]) + ', ')
+                        del j[c]
+                    w('Rcell pigment {Black}}\n')
+
+        # Draw atoms
+        a = 0
+        for loc, dia, color in zip(self.X, self.d, self.colors):
+            tex = 'ase3'
+            if self.textures is not None:
+                tex = self.textures[a]
+            w('atom(%s, %.2f, %s, %s) // #%i \n' % (
+                pa(loc), dia / 2., pc(color), tex, a))
+            a += 1
+
+        # Draw atom bonds
+        for pair in self.bondatoms:
+            if len(pair) == 2:
+                a, b = pair
+                offset = (0, 0, 0)
+            else:
+                a, b, offset = pair
+            R = np.dot(offset, self.A)
+            mida = 0.5 * (self.X[a] + self.X[b] + R)
+            midb = 0.5 * (self.X[a] + self.X[b] - R)
+            if self.textures is not None:
+                texa = self.textures[a]
+                texb = self.textures[b]
+            else:
+                texa = texb = 'ase3'
+            w('cylinder {%s, %s, Rbond texture{pigment {%s} finish{%s}}}\n' % (
+                pa(self.X[a]), pa(mida), pc(self.colors[a]), texa))
+            w('cylinder {%s, %s, Rbond texture{pigment {%s} finish{%s}}}\n' % (
+                pa(self.X[b]), pa(midb), pc(self.colors[b]), texb))
+            
+        # Draw constraints if requested
+        if self.exportconstraints:
+            for a in self.constrainatoms:
+                dia    = self.d[a]
+                loc    = self.X[a]
+                w('constrain(%s, %.2f, Black, %s) // #%i \n' % (
+                    pa(loc), dia / 2., tex, a))
+
+def write_pov(filename, atoms, run_povray=False, **parameters):
+    if isinstance(atoms, list):
+        assert len(atoms) == 1
+        atoms = atoms[0]
+    assert 'scale' not in parameters
+    POVRAY(atoms, **parameters).write(filename)
+    if run_povray:
+        errcode = os.system('povray %s.ini 2> /dev/null' % filename[:-4])
+        if errcode != 0:
+            raise OSError('Povray failed with error code %d' % errcode)
diff --git a/ase/io/pupynere.py b/ase/io/pupynere.py
new file mode 100644
index 0000000..e8be249
--- /dev/null
+++ b/ase/io/pupynere.py
@@ -0,0 +1,614 @@
+"""
+NetCDF reader/writer module.
+
+This module implements the Scientific.IO.NetCDF API to read and create
+NetCDF files. The same API is also used in the PyNIO and pynetcdf
+modules, allowing these modules to be used interchangebly when working
+with NetCDF files. The major advantage of ``scipy.io.netcdf`` over other 
+modules is that it doesn't require the code to be linked to the NetCDF
+libraries as the other modules do.
+
+The code is based on the NetCDF file format specification
+(http://www.unidata.ucar.edu/software/netcdf/guide_15.html). A NetCDF
+file is a self-describing binary format, with a header followed by
+data. The header contains metadata describing dimensions, variables
+and the position of the data in the file, so access can be done in an
+efficient manner without loading unnecessary data into memory. We use
+the ``mmap`` module to create Numpy arrays mapped to the data on disk,
+for the same purpose.
+
+The structure of a NetCDF file is as follows:
+
+    C D F <VERSION BYTE> <NUMBER OF RECORDS>
+    <DIMENSIONS> <GLOBAL ATTRIBUTES> <VARIABLES METADATA>
+    <NON-RECORD DATA> <RECORD DATA>
+
+Record data refers to data where the first axis can be expanded at
+will. All record variables share a same dimension at the first axis,
+and they are stored at the end of the file per record, ie
+
+    A[0], B[0], ..., A[1], B[1], ..., etc,
+    
+so that new data can be appended to the file without changing its original
+structure. Non-record data are padded to a 4n bytes boundary. Record data
+are also padded, unless there is exactly one record variable in the file,
+in which case the padding is dropped.  All data is stored in big endian
+byte order.
+
+The Scientific.IO.NetCDF API allows attributes to be added directly to
+instances of ``netcdf_file`` and ``netcdf_variable``. To differentiate
+between user-set attributes and instance attributes, user-set attributes
+are automatically stored in the ``_attributes`` attribute by overloading
+``__setattr__``. This is the reason why the code sometimes uses
+``obj.__dict__['key'] = value``, instead of simply ``obj.key = value``;
+otherwise the key would be inserted into userspace attributes.
+
+To create a NetCDF file::
+
+    >>> import time
+    >>> f = netcdf_file('simple.nc', 'w')
+    >>> f.history = 'Created for a test'
+    >>> f.createDimension('time', 10)
+    >>> time = f.createVariable('time', 'i', ('time',))
+    >>> time[:] = range(10)
+    >>> time.units = 'days since 2008-01-01'
+    >>> f.close()
+
+To read the NetCDF file we just created::
+
+    >>> f = netcdf_file('simple.nc', 'r')
+    >>> print f.history
+    Created for a test
+    >>> time = f.variables['time']
+    >>> print time.units
+    days since 2008-01-01
+    >>> print time.shape
+    (10,)
+    >>> print time[-1]
+    9
+    >>> f.close()
+
+TODO: properly implement ``_FillValue``.
+"""
+
+__all__ = ['netcdf_file', 'netcdf_variable']
+
+
+from operator import mul
+from mmap import mmap, ACCESS_READ
+
+from numpy import fromstring, ndarray, dtype, empty, array, asarray
+from numpy import little_endian as LITTLE_ENDIAN
+
+
+ABSENT       = '\x00\x00\x00\x00\x00\x00\x00\x00' 
+ZERO         = '\x00\x00\x00\x00'
+NC_BYTE      = '\x00\x00\x00\x01'
+NC_CHAR      = '\x00\x00\x00\x02'
+NC_SHORT     = '\x00\x00\x00\x03'
+NC_INT       = '\x00\x00\x00\x04'
+NC_FLOAT     = '\x00\x00\x00\x05'
+NC_DOUBLE    = '\x00\x00\x00\x06'
+NC_DIMENSION = '\x00\x00\x00\n'
+NC_VARIABLE  = '\x00\x00\x00\x0b'
+NC_ATTRIBUTE = '\x00\x00\x00\x0c'
+
+
+TYPEMAP = { NC_BYTE:   ('b', 1),
+            NC_CHAR:   ('c', 1),
+            NC_SHORT:  ('h', 2),
+            NC_INT:    ('i', 4),
+            NC_FLOAT:  ('f', 4),
+            NC_DOUBLE: ('d', 8) }
+
+REVERSE = { 'b': NC_BYTE,
+            'c': NC_CHAR,
+            'h': NC_SHORT,
+            'i': NC_INT,
+            'f': NC_FLOAT,
+            'd': NC_DOUBLE,
+
+            # these come from asarray(1).dtype.char and asarray('foo').dtype.char,
+            # used when getting the types from generic attributes.
+            'l': NC_INT,
+            'S': NC_CHAR }
+
+
+class netcdf_file(object):
+    """
+    A ``netcdf_file`` object has two standard attributes: ``dimensions`` and
+    ``variables``. The values of both are dictionaries, mapping dimension
+    names to their associated lengths and variable names to variables,
+    respectively. Application programs should never modify these
+    dictionaries.
+
+    All other attributes correspond to global attributes defined in the
+    NetCDF file. Global file attributes are created by assigning to an
+    attribute of the ``netcdf_file`` object.
+
+    """
+    def __init__(self, filename, mode='r', mmap=True):
+        if not __debug__:
+            raise RuntimeError('Current version of pupynere does not ' +
+                               'work with -O option.  We need to update ' +
+                               'to version 1.0.7!')
+
+        self.filename = filename
+        self.use_mmap = mmap
+
+        assert mode in 'rw', "Mode must be either 'r' or 'w'."
+        self.mode = mode
+
+        self.dimensions = {}
+        self.variables = {}
+
+        self._dims = []
+        self._recs = 0
+        self._recsize = 0
+
+        self.fp = open(self.filename, '%sb' % mode)
+
+        self._attributes = {}
+
+        if mode is 'r':
+            self._read()
+
+    def __setattr__(self, attr, value):
+        # Store user defined attributes in a separate dict,
+        # so we can save them to file later.
+        try:
+            self._attributes[attr] = value
+        except AttributeError:
+            pass
+        self.__dict__[attr] = value
+
+    def close(self):
+        if not self.fp.closed:
+            try:
+                self.flush()
+            finally:
+                self.fp.close()
+    __del__ = close
+
+    def createDimension(self, name, length):
+        self.dimensions[name] = length
+        self._dims.append(name)
+
+    def createVariable(self, name, type, dimensions):
+        shape = tuple([self.dimensions[dim] for dim in dimensions]) 
+        shape_ = tuple([dim or 0 for dim in shape])  # replace None with 0 for numpy
+
+        if isinstance(type, basestring): type = dtype(type)
+        typecode, size = type.char, type.itemsize
+        dtype_ = '>%s' % typecode
+        if size > 1: dtype_ += str(size)
+
+        data = empty(shape_, dtype=dtype_)
+        self.variables[name] = netcdf_variable(data, typecode, shape, dimensions)
+        return self.variables[name]
+
+    def flush(self):
+        if self.mode is 'w':
+            self._write()
+    sync = flush
+
+    def _write(self):
+        self.fp.write('CDF')
+
+        self.__dict__['version_byte'] = 1
+        self.fp.write(array(1, '>b').tostring())
+
+        # Write headers and data.
+        self._write_numrecs()
+        self._write_dim_array()
+        self._write_gatt_array()
+        self._write_var_array()
+
+    def _write_numrecs(self):
+        # Get highest record count from all record variables.
+        for var in self.variables.values():
+            if var.isrec and len(var.data) > self._recs:
+                self.__dict__['_recs'] = len(var.data)
+        self._pack_int(self._recs)
+
+    def _write_dim_array(self):
+        if self.dimensions:
+            self.fp.write(NC_DIMENSION)
+            self._pack_int(len(self.dimensions))
+            for name in self._dims:
+                self._pack_string(name)
+                length = self.dimensions[name]
+                self._pack_int(length or 0)  # replace None with 0 for record dimension
+        else:
+            self.fp.write(ABSENT)
+
+    def _write_gatt_array(self):
+        self._write_att_array(self._attributes)
+
+    def _write_att_array(self, attributes):
+        if attributes:
+            self.fp.write(NC_ATTRIBUTE)
+            self._pack_int(len(attributes))
+            for name, values in attributes.items():
+                self._pack_string(name)
+                self._write_values(values)
+        else:
+            self.fp.write(ABSENT)
+
+    def _write_var_array(self):
+        if self.variables:
+            self.fp.write(NC_VARIABLE)
+            self._pack_int(len(self.variables))
+
+            # Sort variables non-recs first, then recs.
+            variables = self.variables.items()
+            if True: # Backwards compatible with Python versions < 2.4
+                keys = [(v._shape and not v.isrec, k) for k, v in variables]
+                keys.sort()
+                keys.reverse()
+                variables = [k for isrec, k in keys]
+            else: # Python version must be >= 2.4
+                variables.sort(key=lambda (k, v): v._shape and not v.isrec)
+                variables.reverse()
+                variables = [k for (k, v) in variables]
+
+            # Set the metadata for all variables.
+            for name in variables:
+                self._write_var_metadata(name)
+            # Now that we have the metadata, we know the vsize of
+            # each record variable, so we can calculate recsize.
+            self.__dict__['_recsize'] = sum([
+                    var._vsize for var in self.variables.values()
+                    if var.isrec])
+            # Set the data for all variables.
+            for name in variables:
+                self._write_var_data(name)
+        else:
+            self.fp.write(ABSENT)
+
+    def _write_var_metadata(self, name):
+        var = self.variables[name]
+
+        self._pack_string(name)
+        self._pack_int(len(var.dimensions))
+        for dimname in var.dimensions:
+            dimid = self._dims.index(dimname)
+            self._pack_int(dimid)
+
+        self._write_att_array(var._attributes)
+
+        nc_type = REVERSE[var.typecode()]
+        self.fp.write(nc_type)
+
+        if not var.isrec:
+            vsize = var.data.size * var.data.itemsize
+            vsize += -vsize % 4
+        else:  # record variable
+            try:
+                vsize = var.data[0].size * var.data.itemsize
+            except IndexError:
+                vsize = 0
+            rec_vars = len([var for var in self.variables.values()
+                    if var.isrec])
+            if rec_vars > 1:
+                vsize += -vsize % 4
+        self.variables[name].__dict__['_vsize'] = vsize
+        self._pack_int(vsize)
+
+        # Pack a bogus begin, and set the real value later.
+        self.variables[name].__dict__['_begin'] = self.fp.tell()
+        self._pack_begin(0)
+
+    def _write_var_data(self, name):
+        var = self.variables[name]
+        
+        # Set begin in file header.
+        the_beguine = self.fp.tell()
+        self.fp.seek(var._begin)
+        self._pack_begin(the_beguine)
+        self.fp.seek(the_beguine)
+
+        # Write data.
+        if not var.isrec:
+            self.fp.write(var.data.tostring())    
+            count = var.data.size * var.data.itemsize
+            self.fp.write('0' * (var._vsize - count))
+        else:  # record variable
+            # Handle rec vars with shape[0] < nrecs.
+            if self._recs > len(var.data):
+                shape = (self._recs,) + var.data.shape[1:]
+                var.data.resize(shape)
+
+            pos0 = pos = self.fp.tell()
+            for rec in var.data:
+                # Apparently scalars cannot be converted to big endian. If we
+                # try to convert a ``=i4`` scalar to, say, '>i4' the dtype
+                # will remain as ``=i4``.
+                if not rec.shape and (rec.dtype.byteorder == '<' or
+                        (rec.dtype.byteorder == '=' and LITTLE_ENDIAN)):
+                    rec = rec.byteswap()
+                self.fp.write(rec.tostring())
+                # Padding
+                count = rec.size * rec.itemsize
+                self.fp.write('0' * (var._vsize - count))
+                pos += self._recsize
+                self.fp.seek(pos)
+            self.fp.seek(pos0 + var._vsize)
+
+    def _write_values(self, values):
+        values = asarray(values) 
+        values = values.astype(values.dtype.newbyteorder('>'))
+
+        nc_type = REVERSE[values.dtype.char]
+        self.fp.write(nc_type)
+
+        if values.dtype.char == 'S':
+            nelems = values.itemsize
+        else:
+            nelems = values.size
+        self._pack_int(nelems)
+
+        if not values.shape and (values.dtype.byteorder == '<' or
+                (values.dtype.byteorder == '=' and LITTLE_ENDIAN)):
+            values = values.byteswap()
+        self.fp.write(values.tostring())
+        count = values.size * values.itemsize
+        self.fp.write('0' * (-count % 4))  # pad
+
+    def _read(self):
+        # Check magic bytes and version
+        assert self.fp.read(3) == 'CDF', "Error: %s is not a valid NetCDF 3 file" % self.filename
+        self.__dict__['version_byte'] = fromstring(self.fp.read(1), '>b')[0]
+
+        # Read file headers and set data.
+        self._read_numrecs()
+        self._read_dim_array()
+        self._read_gatt_array()
+        self._read_var_array()
+
+    def _read_numrecs(self):
+        self.__dict__['_recs'] = self._unpack_int()
+
+    def _read_dim_array(self):
+        assert self.fp.read(4) in [ZERO, NC_DIMENSION]
+        count = self._unpack_int()
+
+        for dim in range(count):
+            name = self._unpack_string()
+            length = self._unpack_int() or None  # None for record dimension
+            self.dimensions[name] = length
+            self._dims.append(name)  # preserve order
+
+    def _read_gatt_array(self):
+        for k, v in self._read_att_array().items():
+            self.__setattr__(k, v)
+
+    def _read_att_array(self):
+        assert self.fp.read(4) in [ZERO, NC_ATTRIBUTE]
+        count = self._unpack_int()
+
+        attributes = {}
+        for attr in range(count):
+            name = self._unpack_string()
+            attributes[name] = self._read_values()
+        return attributes
+
+    def _read_var_array(self):
+        assert self.fp.read(4) in [ZERO, NC_VARIABLE]
+
+        begin = 0
+        dtypes = {'names': [], 'formats': []}
+        rec_vars = []
+        count = self._unpack_int()
+        for var in range(count):
+            name, dimensions, shape, attributes, typecode, size, dtype_, begin_, vsize = self._read_var()
+            if shape and shape[0] is None:
+                rec_vars.append(name)
+                self.__dict__['_recsize'] += vsize
+                if begin == 0: begin = begin_
+                dtypes['names'].append(name)
+                dtypes['formats'].append(str(shape[1:]) + dtype_)
+
+                # Handle padding with a virtual variable.
+                if typecode in 'bch':
+                    actual_size = reduce(mul, (1,) + shape[1:]) * size
+                    padding = -actual_size % 4
+                    if padding:
+                        dtypes['names'].append('_padding_%d' % var)
+                        dtypes['formats'].append('(%d,)>b' % padding)
+
+                # Data will be set later.
+                data = None
+            else:
+                if self.use_mmap:
+                    mm = mmap(self.fp.fileno(), begin_+vsize, access=ACCESS_READ)
+                    data = ndarray.__new__(ndarray, shape, dtype=dtype_,
+                            buffer=mm, offset=begin_, order=0)
+                else:
+                    pos = self.fp.tell()
+                    self.fp.seek(begin_)
+                    data = fromstring(self.fp.read(vsize), dtype=dtype_)
+                    data.shape = shape
+                    self.fp.seek(pos)
+
+            # Add variable.
+            self.variables[name] = netcdf_variable(
+                    data, typecode, shape, dimensions, attributes)
+
+        if rec_vars:
+            # Remove padding when only one record variable.
+            if len(rec_vars) == 1:
+                dtypes['names'] = dtypes['names'][:1]
+                dtypes['formats'] = dtypes['formats'][:1]
+
+            # Build rec array.
+            if self.use_mmap:
+                mm = mmap(self.fp.fileno(), begin+self._recs*self._recsize, access=ACCESS_READ)
+                rec_array = ndarray.__new__(ndarray, (self._recs,), dtype=dtypes,
+                        buffer=mm, offset=begin, order=0)
+            else:
+                pos = self.fp.tell()
+                self.fp.seek(begin)
+                rec_array = fromstring(self.fp.read(self._recs*self._recsize), dtype=dtypes)
+                rec_array.shape = (self._recs,)
+                self.fp.seek(pos)
+
+            for var in rec_vars:
+                self.variables[var].__dict__['data'] = rec_array[var]
+
+    def _read_var(self):
+        name = self._unpack_string()
+        dimensions = []
+        shape = []
+        dims = self._unpack_int()
+        
+        for i in range(dims):
+            dimid = self._unpack_int()
+            dimname = self._dims[dimid]
+            dimensions.append(dimname)
+            dim = self.dimensions[dimname]
+            shape.append(dim)
+        dimensions = tuple(dimensions)
+        shape = tuple(shape)
+
+        attributes = self._read_att_array()
+        nc_type = self.fp.read(4)
+        vsize = self._unpack_int()
+        begin = [self._unpack_int, self._unpack_int64][self.version_byte-1]()
+
+        typecode, size = TYPEMAP[nc_type]
+        if typecode is 'c':
+            dtype_ = '>c'
+        else:
+            dtype_ = '>%s' % typecode
+            if size > 1: dtype_ += str(size)
+
+        return name, dimensions, shape, attributes, typecode, size, dtype_, begin, vsize
+
+    def _read_values(self):
+        nc_type = self.fp.read(4)
+        n = self._unpack_int()
+
+        typecode, size = TYPEMAP[nc_type]
+
+        count = n*size
+        values = self.fp.read(count)
+        self.fp.read(-count % 4)  # read padding
+
+        if typecode is not 'c':
+            values = fromstring(values, dtype='>%s%d' % (typecode, size))
+            if values.shape == (1,): values = values[0]
+        else:
+            values = values.rstrip('\x00') 
+        return values
+
+    def _pack_begin(self, begin):
+        if self.version_byte == 1:
+            self._pack_int(begin)
+        elif self.version_byte == 2:
+            self._pack_int64(begin)
+
+    def _pack_int(self, value):
+        self.fp.write(array(value, '>i').tostring())
+    _pack_int32 = _pack_int
+
+    def _unpack_int(self):
+        return int(fromstring(self.fp.read(4), '>i')[0])
+    _unpack_int32 = _unpack_int
+
+    def _pack_int64(self, value):
+        self.fp.write(array(value, '>q').tostring())
+
+    def _unpack_int64(self):
+        return int(fromstring(self.fp.read(8), '>q')[0])
+
+    def _pack_string(self, s):
+        count = len(s)
+        self._pack_int(count)
+        self.fp.write(s)
+        self.fp.write('0' * (-count % 4))  # pad
+
+    def _unpack_string(self):
+        count = self._unpack_int()
+        s = self.fp.read(count).rstrip('\x00')
+        self.fp.read(-count % 4)  # read padding
+        return s
+
+
+class netcdf_variable(object):
+    """
+    ``netcdf_variable`` objects are constructed by calling the method
+    ``createVariable`` on the netcdf_file object.
+
+    ``netcdf_variable`` objects behave much like array objects defined in
+    Numpy, except that their data resides in a file. Data is read by
+    indexing and written by assigning to an indexed subset; the entire
+    array can be accessed by the index ``[:]`` or using the methods
+    ``getValue`` and ``assignValue``. ``netcdf_variable`` objects also
+    have attribute ``shape`` with the same meaning as for arrays, but
+    the shape cannot be modified. There is another read-only attribute
+    ``dimensions``, whose value is the tuple of dimension names.
+
+    All other attributes correspond to variable attributes defined in
+    the NetCDF file. Variable attributes are created by assigning to an
+    attribute of the ``netcdf_variable`` object.
+
+    """
+    def __init__(self, data, typecode, shape, dimensions, attributes=None):
+        self.data = data
+        self._typecode = typecode
+        self._shape = shape
+        self.dimensions = dimensions
+
+        self._attributes = attributes or {}
+        for k, v in self._attributes.items():
+            self.__dict__[k] = v
+
+    def __setattr__(self, attr, value):
+        # Store user defined attributes in a separate dict,
+        # so we can save them to file later.
+        try:
+            self._attributes[attr] = value
+        except AttributeError:
+            pass
+        self.__dict__[attr] = value
+
+    def isrec(self):
+        return self.data.shape and not self._shape[0]
+    isrec = property(isrec)
+
+    def shape(self):
+        return self.data.shape
+    shape = property(shape)
+    
+    def getValue(self):
+        return self.data.item()
+
+    def assignValue(self, value):
+        self.data.itemset(value)
+
+    def typecode(self):
+        return self._typecode
+
+    def __getitem__(self, index):
+        return self.data[index]
+
+    def __setitem__(self, index, data):
+        # Expand data for record vars?
+        if self.isrec:
+            if isinstance(index, tuple):
+                rec_index = index[0]
+            else:
+                rec_index = index
+            if isinstance(rec_index, slice):
+                recs = (rec_index.start or 0) + len(data)
+            else:
+                recs = rec_index + 1
+            if recs > len(self.data):
+                shape = (recs,) + self._shape[1:]
+                self.data.resize(shape)
+        self.data[index] = data
+
+
+NetCDFFile = netcdf_file
+NetCDFVariable = netcdf_variable
diff --git a/ase/io/py.py b/ase/io/py.py
new file mode 100644
index 0000000..602ac62
--- /dev/null
+++ b/ase/io/py.py
@@ -0,0 +1,25 @@
+from ase.atoms import Atoms
+
+
+def write_py(fileobj, images, **kwargs):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj, 'w')
+
+    fileobj.write('from ase import Atoms\n\n')
+    fileobj.write('import numpy as np\n\n')
+    
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+    fileobj.write('images = [\n')
+
+    for image in images:
+        fileobj.write("    Atoms(symbols='%s',\n"
+                      "          pbc=np.%s,\n"
+                      "          cell=np.array(\n      %s,\n"
+                      "          positions=np.array(\n      %s),\n" % (
+            image.get_chemical_symbols(reduce=True),
+            repr(image.pbc),
+            repr(image.cell)[6:],
+            repr(image.positions)[6:]))
+        
+    fileobj.write(']')
diff --git a/ase/io/sdf.py b/ase/io/sdf.py
new file mode 100644
index 0000000..5ebf5d0
--- /dev/null
+++ b/ase/io/sdf.py
@@ -0,0 +1,23 @@
+from math import pi, cos, sin, sqrt, acos
+
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+def read_sdf(fileobj):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+    # first three lines header
+    del lines[:3]
+
+    # 
+    L1 = lines.pop(0).split()
+    natoms = int(L1[0])
+    positions = []
+    symbols = []
+    for line in lines[:natoms]:
+        x, y, z, symbol = line.split()[:4]
+        symbols.append(symbol)
+        positions.append([float(x), float(y), float(z)])
+    return Atoms(symbols=symbols, positions=positions)
diff --git a/ase/io/siesta.py b/ase/io/siesta.py
new file mode 100644
index 0000000..f4d4147
--- /dev/null
+++ b/ase/io/siesta.py
@@ -0,0 +1,243 @@
+from numpy import zeros
+from os import fstat
+from re import compile
+
+from ase.io.fortranfile import FortranFile
+
+
+def read_rho(fname):
+    "Read unformatted Siesta charge density file"
+
+    # TODO:
+    # 
+    # Handle formatted and NetCDF files.
+    #
+    # Siesta source code (at least 2.0.2) can possibly also 
+    # save RHO as a _formatted_ file (the source code seems
+    # prepared, but there seems to be no fdf-options for it though).
+    # Siesta >= 3 has support for saving RHO as a NetCDF file
+    # (according to manual)
+
+    fh = FortranFile(fname)
+    
+    # Read (but ignore) unit cell vectors
+    x = fh.readReals('d')
+    if len(x) != 3 * 3: 
+        raise IOError('Failed to read cell vectors')
+        
+    # Read number of grid points and spin components
+    x = fh.readInts()
+    if len(x) != 4:
+        raise IOError('Failed to read grid size')
+    gpts = x  # number of 'X', 'Y', 'Z', 'spin' gridpoints 
+    
+    rho = zeros(gpts)
+    for ispin in range(gpts[3]):
+        for n3 in range(gpts[2]):
+            for n2 in range(gpts[1]):
+                x = fh.readReals('f')
+                if len(x) != gpts[0]:
+                    raise IOError('Failed to read RHO[:,%i,%i,%i]' %
+                                  (n2, n3, ispin))
+                rho[:, n2, n3, ispin] = x
+    
+    fh.close()
+    return rho
+
+
+
+#
+# Helper functions for read_fdf
+#
+_label_strip_re = compile(r'[\s._-]')
+def _labelize(raw_label):
+    # Labels are case insensitive and -_. should be ignored, lower and strip it
+    return _label_strip_re.sub('', raw_label).lower()
+
+def _is_block(val):
+    # Tell whether value is a block-value or an ordinary value.
+    # A block is represented as a list of lists of strings,
+    # and a ordinary value is represented as a list of strings
+    if type(val) is list and \
+       len(val) > 0 and \
+       type(val[0]) is list:
+        return True
+    return False
+    
+def _get_stripped_lines(fd):
+    # Remove comments, leading blanks, and empty lines
+    return filter(None, [L.split('#')[0].strip() for L in fd])
+
+def _read_fdf_lines(file, inodes=[]):
+    # Read lines and resolve includes
+
+    if type(file) is str:
+        file = open(file, 'r')
+    fst = fstat(file.fileno())
+    inode = (fst.st_dev, fst.st_ino)
+    if inode in inodes:
+        raise IOError('Cyclic include in fdf file')
+    inodes = inodes + [inode]
+
+    lbz = _labelize
+    
+    lines = []
+    for L in _get_stripped_lines(file):
+        w0 = lbz(L.split(None, 1)[0])
+
+        if w0 == '%include':
+            # Include the contents of fname
+            fname = L.split(None, 1)[1].strip()
+            lines += _read_fdf_lines(fname, inodes)
+
+        elif '<' in L:
+            L, fname = L.split('<', 1)
+            w = L.split()
+            fname = fname.strip()
+
+            if w0 == '%block':
+                # "%block label < filename" means that the block contents should be read from filename 
+                if len(w) != 2:
+                    raise IOError('Bad %%block-statement "%s < %s"' % (L, fname))
+                label = lbz(w[1])
+                lines.append('%%block %s' % label)
+                lines += _get_stripped_lines(open(fname))
+                lines.append('%%endblock %s' % label)
+            else:
+                # "label < filename.fdf" means that the label (_only_ that label) is to be resolved from filename.fdf
+                label = lbz(w[0])
+                fdf = _read_fdf(fname, inodes)
+                if label in fdf:
+                    if _is_block(fdf[label]):
+                        lines.append('%%block %s' % label)
+                        lines += [' '.join(x) for x in fdf[label]]
+                        lines.append('%%endblock %s' % label)
+                    else:
+                        lines.append('%s %s' % (label, ' '.join(fdf[label])))
+                #else: label unresolved! One should possibly issue a warning about this!
+        else:
+            # Simple include line L
+            lines.append(L)
+    return lines
+
+#
+# The reason for creating a separate _read_fdf is simply to hide the inodes-argument
+#
+def _read_fdf(fname, inodes=[]):
+    # inodes is used to detect cyclic includes
+    fdf = {}
+    lbz = _labelize
+    lines = _read_fdf_lines(fname, inodes)
+    while lines:
+        w = lines.pop(0).split(None, 1)
+        if lbz(w[0]) == '%block':
+            # Block value
+            if len(w) == 2:
+                label = lbz(w[1])
+                content = []
+                while True:
+                    if len(lines) == 0:
+                        raise IOError('Unexpected EOF reached in %s, '
+                                      'un-ended block %s' % (fname, label))                            
+                    w = lines.pop(0).split()
+                    if lbz(w[0]) == '%endblock' and lbz(w[1]) == label:
+                        break
+                    content.append(w)
+                        
+                if not label in fdf:
+                    # Only first appearance of label is to be used
+                    fdf[label] = content
+            else:
+                raise IOError('%%block statement without label' )
+        else:
+            # Ordinary value
+            label = lbz(w[0])
+            if len(w) == 1:
+                # Siesta interpret blanks as True for logical variables
+                fdf[label] = []
+            else:
+                fdf[label] = w[1].split()
+    return fdf
+
+
+def read_fdf(fname):
+    """Read a siesta style fdf-file.
+
+    The data is returned as a dictionary
+    ( label:value ).
+    
+    All labels are converted to lower case characters and
+    are stripped of any '-', '_', or '.'.
+    
+    Ordinary values are stored as a list of strings (splitted on WS),
+    and block values are stored as list of lists of strings
+    (splitted per line, and on WS).
+    If a label occurres more than once, the first occurrence
+    takes precedence.
+
+    The implementation applies no intelligence, and does not
+    "understand" the data or the concept of units etc.
+    Values are never parsed in any way, just stored as
+    split strings.
+    
+    The implementation tries to comply with the fdf-format
+    specification as presented in the siesta 2.0.2 manual.
+
+    An fdf-dictionary could e.g. look like this::
+
+        {'atomiccoordinatesandatomicspecies': [
+              ['4.9999998', '5.7632392', '5.6095972', '1'],
+              ['5.0000000', '6.5518100', '4.9929091', '2'],
+              ['5.0000000', '4.9746683', '4.9929095', '2']],
+         'atomiccoordinatesformat': ['Ang'],
+         'chemicalspecieslabel': [['1', '8', 'O'],
+                                  ['2', '1', 'H']],
+         'dmmixingweight': ['0.1'],
+         'dmnumberpulay': ['5'],
+         'dmusesavedm': ['True'],
+         'latticeconstant': ['1.000000', 'Ang'],
+         'latticevectors': [
+              ['10.00000000', '0.00000000', '0.00000000'],
+              ['0.00000000', '11.52647800', '0.00000000'],
+              ['0.00000000', '0.00000000', '10.59630900']],
+         'maxscfiterations': ['120'],
+         'meshcutoff': ['2721.139566', 'eV'],
+         'numberofatoms': ['3'],
+         'numberofspecies': ['2'],
+         'paobasissize': ['dz'],
+         'solutionmethod': ['diagon'],
+         'systemlabel': ['H2O'],
+         'wavefunckpoints': [['0.0', '0.0', '0.0']],
+         'writedenchar': ['T'],
+         'xcauthors': ['PBE'],
+         'xcfunctional': ['GGA']}
+
+    """
+
+    return _read_fdf(fname)
+
+
+def read_struct(fname):
+    """Read a siesta struct file"""
+    from ase.atoms import Atoms, Atom
+
+    f = open(fname, 'r')
+
+    cell = []
+    for i in range(3):
+        cell.append([float(x) for x in f.readline().split()])
+
+    natoms = int(f.readline())
+
+    atoms = Atoms()
+    for atom in f:
+        Z, pos_x, pos_y, pos_z = atom.split()[1:]
+        atoms.append(Atom(int(Z), position = (float(pos_x), float(pos_y), float(pos_z))))
+
+    if len(atoms) != natoms:
+        raise IOError('Badly structured input file')
+    
+    atoms.set_cell(cell, scale_atoms = True)
+
+    return atoms
+    
diff --git a/ase/io/trajectory.py b/ase/io/trajectory.py
new file mode 100644
index 0000000..a97cf91
--- /dev/null
+++ b/ase/io/trajectory.py
@@ -0,0 +1,581 @@
+import os
+import sys
+import cPickle as pickle
+import warnings
+
+from ase.calculators.singlepoint import SinglePointCalculator
+from ase.atoms import Atoms
+from ase.parallel import rank, barrier
+from ase.utils import devnull
+
+
+class PickleTrajectory:
+    """Reads/writes Atoms objects into a .traj file."""
+    # Per default, write these quantities
+    write_energy = True
+    write_forces = True
+    write_stress = True
+    write_magmoms = True
+    write_momenta = True
+    write_info = True
+
+    def __init__(self, filename, mode='r', atoms=None, master=None,
+                 backup=True):
+        """A PickleTrajectory can be created in read, write or append mode.
+
+        Parameters:
+
+        filename:
+            The name of the parameter file.  Should end in .traj.
+
+        mode='r':
+            The mode.
+
+            'r' is read mode, the file should already exist, and
+            no atoms argument should be specified.
+
+            'w' is write mode.  If the file already exists, is it
+            renamed by appending .bak to the file name.  The atoms
+            argument specifies the Atoms object to be written to the
+            file, if not given it must instead be given as an argument
+            to the write() method.
+
+            'a' is append mode.  It acts a write mode, except that
+            data is appended to a preexisting file.
+
+        atoms=None:
+            The Atoms object to be written in write or append mode.
+
+        master=None:
+            Controls which process does the actual writing. The
+            default is that process number 0 does this.  If this
+            argument is given, processes where it is True will write.
+
+        backup=True:
+            Use backup=False to disable renaming of an existing file.
+        """
+
+        self.numbers = None
+        self.pbc = None
+        self.sanitycheck = True
+        self.pre_observers = []   # Callback functions before write
+        self.post_observers = []  # Callback functions after write
+        self.write_counter = 0    # Counter used to determine when callbacks
+                                  # are called
+
+        self.offsets = []
+        if master is None:
+            master = (rank == 0)
+        self.master = master
+        self.backup = backup
+        self.set_atoms(atoms)
+        self.open(filename, mode)
+
+    def open(self, filename, mode):
+        """Opens the file.
+
+        For internal use only.
+        """
+        self.fd = filename
+        if mode == 'r':
+            if isinstance(filename, str):
+                self.fd = open(filename, 'rb')
+            self.read_header()
+        elif mode == 'a':
+            exists = True
+            if isinstance(filename, str):
+                exists = os.path.isfile(filename)
+                if exists:
+                    self.fd = open(filename, 'rb')
+                    self.read_header()
+                    self.fd.close()
+                barrier()
+                if self.master:
+                    self.fd = open(filename, 'ab+')
+                else:
+                    self.fd = devnull
+        elif mode == 'w':
+            if self.master:
+                if isinstance(filename, str):
+                    if self.backup and os.path.isfile(filename):
+                        os.rename(filename, filename + '.bak')
+                    self.fd = open(filename, 'wb')
+            else:
+                self.fd = devnull
+        else:
+            raise ValueError('mode must be "r", "w" or "a".')
+
+    def set_atoms(self, atoms=None):
+        """Associate an Atoms object with the trajectory.
+
+        Mostly for internal use.
+        """
+        if atoms is not None and not hasattr(atoms, 'get_positions'):
+            raise TypeError('"atoms" argument is not an Atoms object.')
+        self.atoms = atoms
+
+    def read_header(self):
+        self.fd.seek(0)
+        try:
+            if self.fd.read(len('PickleTrajectory')) != 'PickleTrajectory':
+                raise IOError('This is not a trajectory file!')
+            d = pickle.load(self.fd)
+        except EOFError:
+            raise EOFError('Bad trajectory file.')
+
+        self.pbc = d['pbc']
+        self.numbers = d['numbers']
+        self.tags = d.get('tags')
+        self.masses = d.get('masses')
+        self.constraints = dict2constraints(d)
+        self.offsets.append(self.fd.tell())
+
+    def write(self, atoms=None):
+        """Write the atoms to the file.
+
+        If the atoms argument is not given, the atoms object specified
+        when creating the trajectory object is used.
+        """
+        self._call_observers(self.pre_observers)
+        if atoms is None:
+            atoms = self.atoms
+
+        if hasattr(atoms, 'interpolate'):
+            # seems to be a NEB
+            neb = atoms
+            assert not neb.parallel
+            try:
+                neb.get_energies_and_forces(all=True)
+            except AttributeError:
+                pass
+            for image in neb.images:
+                self.write(image)
+            return
+        
+        if len(self.offsets) == 0:
+            self.write_header(atoms)
+        else:
+            if (atoms.pbc != self.pbc).any():
+                raise ValueError('Bad periodic boundary conditions!')
+            elif self.sanitycheck and len(atoms) != len(self.numbers):
+                raise ValueError('Bad number of atoms!')
+            elif self.sanitycheck and (atoms.numbers != self.numbers).any():
+                raise ValueError('Bad atomic numbers!')
+            
+        if atoms.has('momenta'):
+            momenta = atoms.get_momenta()
+        else:
+            momenta = None
+
+        d = {'positions': atoms.get_positions(),
+             'cell': atoms.get_cell(),
+             'momenta': momenta}
+
+        if atoms.get_calculator() is not None:
+            if self.write_energy:
+                d['energy'] = atoms.get_potential_energy()
+            if self.write_forces:
+                assert self.write_energy
+                try:
+                    d['forces'] = atoms.get_forces(apply_constraint=False)
+                except NotImplementedError:
+                    pass
+            if self.write_stress:
+                assert self.write_energy
+                try:
+                    d['stress'] = atoms.get_stress()
+                except NotImplementedError:
+                    pass
+
+            if self.write_magmoms:
+                try:
+                    if atoms.calc.get_spin_polarized():
+                        d['magmoms'] = atoms.get_magnetic_moments()
+                except (NotImplementedError, AttributeError):
+                    pass
+
+        if 'magmoms' not in d and atoms.has('magmoms'):
+            d['magmoms'] = atoms.get_initial_magnetic_moments()
+
+        if self.write_info:
+            d['info'] = stringnify_info(atoms.info)
+            
+        if self.master:
+            pickle.dump(d, self.fd, protocol=-1)
+        self.fd.flush()
+        self.offsets.append(self.fd.tell())
+        self._call_observers(self.post_observers)
+        self.write_counter += 1
+
+    def write_header(self, atoms):
+        self.fd.write('PickleTrajectory')
+        if atoms.has('tags'):
+            tags = atoms.get_tags()
+        else:
+            tags = None
+        if atoms.has('masses'):
+            masses = atoms.get_masses()
+        else:
+            masses = None
+        d = {'version': 3,
+             'pbc': atoms.get_pbc(),
+             'numbers': atoms.get_atomic_numbers(),
+             'tags': tags,
+             'masses': masses,
+             'constraints': [],  # backwards compatibility
+             'constraints_string': pickle.dumps(atoms.constraints)}
+        pickle.dump(d, self.fd, protocol=-1)
+        self.header_written = True
+        self.offsets.append(self.fd.tell())
+
+        # Atomic numbers and periodic boundary conditions are only
+        # written once - in the header.  Store them here so that we can
+        # check that they are the same for all images:
+        self.numbers = atoms.get_atomic_numbers()
+        self.pbc = atoms.get_pbc()
+        
+    def close(self):
+        """Close the trajectory file."""
+        self.fd.close()
+
+    def __getitem__(self, i=-1):
+        if isinstance(i, slice):
+            return [self[j] for j in range(*i.indices(len(self)))]
+
+        N = len(self.offsets)
+        if 0 <= i < N:
+            self.fd.seek(self.offsets[i])
+            try:
+                d = pickle.load(self.fd)
+            except EOFError:
+                raise IndexError
+            if i == N - 1:
+                self.offsets.append(self.fd.tell())
+            try:
+                magmoms = d['magmoms']
+            except KeyError:
+                magmoms = None
+            atoms = Atoms(positions=d['positions'],
+                          numbers=self.numbers,
+                          cell=d['cell'],
+                          momenta=d['momenta'],
+                          magmoms=magmoms,
+                          tags=self.tags,
+                          masses=self.masses,
+                          pbc=self.pbc,
+                          info=unstringnify_info(d.get('info', {})),
+                          constraint=[c.copy() for c in self.constraints])
+            if 'energy' in d:
+                calc = SinglePointCalculator(
+                    d.get('energy', None), d.get('forces', None),
+                    d.get('stress', None), magmoms, atoms)
+                atoms.set_calculator(calc)
+            return atoms
+
+        if i >= N:
+            for j in range(N - 1, i + 1):
+                atoms = self[j]
+            return atoms
+
+        i = len(self) + i
+        if i < 0:
+            raise IndexError('Trajectory index out of range.')
+        return self[i]
+
+    def __len__(self):
+        if len(self.offsets) == 0:
+            return 0
+        N = len(self.offsets) - 1
+        while True:
+            self.fd.seek(self.offsets[N])
+            try:
+                pickle.load(self.fd)
+            except EOFError:
+                return N
+            self.offsets.append(self.fd.tell())
+            N += 1
+
+    def __iter__(self):
+        del self.offsets[1:]
+        return self
+
+    def next(self):
+        try:
+            return self[len(self.offsets) - 1]
+        except IndexError:
+            raise StopIteration
+
+    def guess_offsets(self):
+        size = os.path.getsize(self.fd.name)
+
+        while True:
+            self.fd.seek(self.offsets[-1])
+            try:
+                pickle.load(self.fd)
+            except:
+                raise EOFError('Damaged trajectory file.')
+            else:
+                self.offsets.append(self.fd.tell())
+
+            if self.offsets[-1] >= size:
+                break
+
+            if len(self.offsets) > 2:
+                step1 = self.offsets[-1] - self.offsets[-2]
+                step2 = self.offsets[-2] - self.offsets[-3]
+
+                if step1 == step2:
+                    m = int((size - self.offsets[-1]) / step1) - 1
+
+                    while m > 1:
+                        self.fd.seek(self.offsets[-1] + m * step1)
+                        try:
+                            pickle.load(self.fd)
+                        except:
+                            m = m / 2
+                        else:
+                            for i in range(m):
+                                self.offsets.append(self.offsets[-1] + step1)
+                            m = 0
+
+    def pre_write_attach(self, function, interval=1, *args, **kwargs):
+        """Attach a function to be called before writing begins.
+
+        function: The function or callable object to be called.
+
+        interval: How often the function is called.  Default: every time (1).
+
+        All other arguments are stored, and passed to the function.
+        """
+        if not callable(function):
+            raise ValueError('Callback object must be callable.')
+        self.pre_observers.append((function, interval, args, kwargs))
+
+    def post_write_attach(self, function, interval=1, *args, **kwargs):
+        """Attach a function to be called after writing ends.
+
+        function: The function or callable object to be called.
+
+        interval: How often the function is called.  Default: every time (1).
+
+        All other arguments are stored, and passed to the function.
+        """
+        if not callable(function):
+            raise ValueError('Callback object must be callable.')
+        self.post_observers.append((function, interval, args, kwargs))
+
+    def _call_observers(self, obs):
+        """Call pre/post write observers."""
+        for function, interval, args, kwargs in obs:
+            if self.write_counter % interval == 0:
+                function(*args, **kwargs)
+
+
+def stringnify_info(info):
+    """Return a stringnified version of the dict *info* that is
+    ensured to be picklable.  Items with non-string keys or
+    unpicklable values are dropped and a warning is issued."""
+    stringnified = {}
+    for k, v in info.items():
+        if not isinstance(k, basestring):
+            warnings.warn('Non-string info-dict key is not stored in ' +
+                          'trajectory: ' + repr(k), UserWarning)
+            continue
+        try:
+            # Should highest protocol be used here for efficiency?
+            # Protocol 2 seems not to raise an exception when one
+            # tries to pickle a file object, so by using that, we
+            # might end up with file objects in inconsistent states.
+            s = pickle.dumps(v)
+        except:
+            warnings.warn('Skipping not picklable info-dict item: ' +
+                          '"%s" (%s)' % (k, sys.exc_info()[1]), UserWarning)
+        else:
+            stringnified[k] = s
+    return stringnified
+
+
+def unstringnify_info(stringnified):
+    """Convert the dict *stringnified* to a dict with unstringnified
+    objects and return it.  Objects that cannot be unpickled will be
+    skipped and a warning will be issued."""
+    info = {}
+    for k, s in stringnified.items():
+        try:
+            v = pickle.loads(s)
+        except:
+            warnings.warn('Skipping not unpicklable info-dict item: ' +
+                          '"%s" (%s)' % (k, sys.exc_info()[1]), UserWarning)
+        else:
+            info[k] = v
+    return info
+
+
+def read_trajectory(filename, index=-1):
+    traj = PickleTrajectory(filename, mode='r')
+
+    if isinstance(index, int):
+        return traj[index]
+    else:
+        # Here, we try to read only the configurations we need to read
+        # and len(traj) should only be called if we need to as it will
+        # read all configurations!
+
+        # XXX there must be a simpler way?
+        step = index.step or 1
+        if step > 0:
+            start = index.start or 0
+            if start < 0:
+                start += len(traj)
+            stop = index.stop or len(traj)
+            if stop < 0:
+                stop += len(traj)
+        else:
+            if index.start is None:
+                start = len(traj) - 1
+            else:
+                start = index.start
+                if start < 0:
+                    start += len(traj)
+            if index.stop is None:
+                stop = -1
+            else:
+                stop = index.stop
+                if stop < 0:
+                    stop += len(traj)
+                    
+        return [traj[i] for i in range(start, stop, step)]
+
+
+def write_trajectory(filename, images):
+    """Write image(s) to trajectory.
+
+    Write also energy, forces, and stress if they are already
+    calculated."""
+
+    traj = PickleTrajectory(filename, mode='w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+        
+    for atoms in images:
+        # Avoid potentially expensive calculations:
+        calc = atoms.get_calculator()
+        if calc is not None:
+            if  hasattr(calc, 'calculation_required'):
+                if calc.calculation_required(atoms, ['energy']):
+                    traj.write_energy = False
+                if calc.calculation_required(atoms, ['forces']):
+                    traj.write_forces = False
+                if calc.calculation_required(atoms, ['stress']):
+                    traj.write_stress = False
+                if calc.calculation_required(atoms, ['magmoms']):
+                    traj.write_magmoms = False
+        else:
+            traj.write_energy = False
+            traj.write_forces = False
+            traj.write_stress = False
+            traj.write_magmoms = False
+            
+        traj.write(atoms)
+    traj.close()
+
+
+def dict2constraints(d):
+    """Convert dict unpickled from trajectory file to list of constraints."""
+
+    version = d.get('version', 1)
+
+    if version == 1:
+        return d['constraints']
+    elif version in (2, 3):
+        try:
+            return pickle.loads(d['constraints_string'])
+        except (AttributeError, KeyError, EOFError):
+            warnings.warn('Could not unpickle constraints!')
+            return []
+    else:
+        return []
+
+
+def print_trajectory_info(filename):
+    """Prints information about a PickleTrajectory file.
+
+    Mainly intended to be called from a command line tool.
+    """
+    f = open(filename)
+    hdr = 'PickleTrajectory'
+    x = f.read(len(hdr))
+    if x != hdr:
+        raise ValueError('Not a PickleTrajectory file!')
+    # Head header
+    header = pickle.load(f)
+    print('Header information of trajectory file %r:' % filename)
+    print('  Version: %d' % header.get('version', 1))
+    print('  Boundary conditions: %s' % header['pbc'])
+    print('  Atomic numbers: shape = %s, type = %s' %
+          (header['numbers'].shape, header['numbers'].dtype))
+    if header.get('tags') is None:
+        print('  Tags are absent.')
+    else:
+        print('  Tags: shape = %s, type = %s' %
+              (header['tags'].shape, header['tags'].dtype))
+    if header.get('masses') is None:
+        print('  Masses are absent.')
+    else:
+        print('  Masses: shape = %s, type = %s' %
+              (header['masses'].shape, header['masses'].dtype))
+    constraints = dict2constraints(header)
+    if constraints:
+        print('  %d constraints are present.' % len(constraints))
+    else:
+        print('  No constraints.')
+
+    after_header = f.tell()
+
+    # Read the first frame
+    frame = pickle.load(f)
+    print('Contents of first frame:')
+    for k, v in frame.items():
+        if hasattr(v, 'shape'):
+            print('  %s: shape = %s, type = %s' % (k, v.shape, v.dtype))
+        else:
+            print('  %s: %s' % (k, v))
+    after_frame = f.tell()
+    kB = 1024
+    MB = 1024 * kB
+    GB = 1024 * MB
+    framesize = after_frame - after_header
+    if framesize >= GB:
+        print('Frame size: %.2f GB' % (1.0 * framesize / GB))
+    elif framesize >= MB:
+        print('Frame size: %.2f MB' % (1.0 * framesize / MB))
+    else:
+        print('Frame size: %.2f kB' % (1.0 * framesize / kB))
+
+    # Print information about file size
+    try:
+        filesize = os.path.getsize(filename)
+    except IOError:
+        print('No information about the file size.')
+    else:
+        if filesize >= GB:
+            print('File size: %.2f GB' % (1.0 * filesize / GB))
+        elif filesize >= MB:
+            print('File size: %.2f MB' % (1.0 * filesize / MB))
+        else:
+            print('File size: %.2f kB' % (1.0 * filesize / kB))
+        
+        nframes = (filesize - after_header) // framesize
+        offset = nframes * framesize + after_header - filesize
+        if offset == 0:
+            if nframes == 1:
+                print('Trajectory contains 1 frame.')
+            else:
+                print('Trajectory contains %d frames.' % nframes)
+        else:
+            print('Trajectory appears to contain approximately %d frames,' %
+                  nframes)
+            print('but the file size differs by %d bytes from the expected' %
+                  -offset)
+            print('value.')
diff --git a/ase/io/turbomole.py b/ase/io/turbomole.py
new file mode 100644
index 0000000..fd99513
--- /dev/null
+++ b/ase/io/turbomole.py
@@ -0,0 +1,186 @@
+from ase.atoms import Atoms
+from ase.units import Bohr
+
+
+def read_turbomole(filename='coord'):
+    """Method to read turbomole coord file
+    
+    coords in bohr, atom types in lowercase, format:
+    $coord
+    x y z atomtype 
+    x y z atomtype f
+    $end
+    Above 'f' means a fixed atom.
+    """
+    from ase import Atoms, Atom
+    from ase.constraints import FixAtoms
+
+    if isinstance(filename, str):
+        f = open(filename)
+
+    lines = f.readlines()
+    atoms_pos = []
+    atom_symbols = []
+    myconstraints=[]
+    
+    # find $coord section;
+    # does not necessarily have to be the first $<something> in file...
+    start = lines.index('$coord') # raises ValueError if not found
+    for line in lines[start+1:]:
+        if line.startswith('$'): # start of new section
+            break
+        else:
+            x, y, z, symbolraw = line.split()[:4]
+            symbolshort=symbolraw.strip()
+            symbol=symbolshort[0].upper()+symbolshort[1:].lower()
+            #print symbol
+            atom_symbols.append(symbol)
+            atoms_pos.append([float(x)*Bohr, float(y)*Bohr, float(z)*Bohr])
+            cols = line.split()
+            if (len(cols) == 5):
+                fixedstr = line.split()[4].strip()
+                if (fixedstr == "f"):
+                    myconstraints.append(True)
+                else:
+                    myconstraints.append(False)
+            else:
+                myconstraints.append(False)
+            
+    if type(filename) == str:
+        f.close()
+
+    atoms = Atoms(positions = atoms_pos, symbols = atom_symbols, pbc = False)
+    c = FixAtoms(mask = myconstraints)
+    atoms.set_constraint(c)
+    #print c
+    
+
+    return atoms
+
+def read_turbomole_gradient(filename='gradient', index=-1):
+    """ Method to read turbomole gradient file """
+
+    if isinstance(filename, str):
+        f = open(filename)
+
+    # read entire file
+    lines = [x.strip() for x in f.readlines()]
+
+    # find $grad section
+    start = end = -1
+    for i, line in enumerate(lines):
+        if not line.startswith('$'):
+            continue
+        if line.split()[0] == '$grad':
+            start = i
+        elif start >= 0:
+            end = i
+            break
+
+    if end <= start:
+        raise RuntimeError('File %s does not contain a valid \'$grad\' section' % (filename))
+
+    def formatError():
+        raise RuntimeError('Data format in file %s does not correspond to known Turbomole gradient format' % (filename))
+
+
+    # trim lines to $grad
+    del lines[:start+1]
+    del lines[end-1-start:]
+
+    # Interpret $grad section
+    from ase import Atoms, Atom
+    from ase.calculators.singlepoint import SinglePointCalculator
+    from ase.units import Bohr
+    images = []
+    while len(lines): # loop over optimization cycles
+        # header line
+        # cycle =      1    SCF energy =     -267.6666811409   |dE/dxyz| =  0.157112
+        fields = lines[0].split('=')
+        try:
+            cycle = int(fields[1].split()[0])
+            energy = float(fields[2].split()[0])
+            gradient = float(fields[3].split()[0])
+        except (IndexError, ValueError):
+            formatError()
+        
+        # coordinates/gradient
+        atoms = Atoms()
+        forces = []
+        for line in lines[1:]:
+            fields = line.split()
+            if len(fields) == 4: # coordinates
+                # 0.00000000000000      0.00000000000000      0.00000000000000      c
+                try:
+                    symbol = fields[3].lower().capitalize()
+                    position = tuple([bohr2angstrom(float(x)) for x in fields[0:3] ])
+                except ValueError:
+                    formatError()
+                atoms.append(Atom(symbol, position))
+            elif len(fields) == 3: # gradients
+                #  -.51654903354681D-07  -.51654903206651D-07  0.51654903169644D-07
+                try:
+                    grad = [float(x.replace('D', 'E')) * Bohr for x in fields[0:3] ]
+                except ValueError:
+                    formatError()
+                forces.append(grad)
+            else: # next cycle
+                break
+
+        # calculator
+        calc = SinglePointCalculator(energy, forces, None, None, atoms)
+        atoms.set_calculator(calc)
+
+        # save frame
+        images.append(atoms)
+
+        # delete this frame from data to be handled
+        del lines[:2*len(atoms)+1]
+
+    return images[index]
+
+
+def write_turbomole(filename, atoms):
+    """Method to write turbomole coord file
+    """
+
+    import numpy as np
+    from ase.constraints import FixAtoms
+
+    if isinstance(filename, str):
+        f = open(filename, 'w')
+    else: # Assume it's a 'file-like object'
+        f = filename
+
+    coord = atoms.get_positions()
+    symbols = atoms.get_chemical_symbols()
+    printfixed = False
+
+    if atoms.constraints:
+        for constr in atoms.constraints:
+            if isinstance(constr, FixAtoms):
+                fix_index=constr.index
+                printfixed=True
+    #print sflags
+        
+    if (printfixed):
+        fix_str=[]
+        for i in fix_index:
+            if i == 1:
+                fix_str.append("f")
+            else:
+                fix_str.append(" ")
+
+
+    f.write("$coord\n")
+    if (printfixed):
+        for (x, y, z), s, fix in zip(coord,symbols,fix_str):
+            f.write('%20.14f  %20.14f  %20.14f      %2s  %2s \n' 
+                    % (x/Bohr, y/Bohr, z/Bohr, s.lower(), fix))
+
+    else:
+        for (x, y, z), s in zip(coord,symbols):
+            f.write('%20.14f  %20.14f  %20.14f      %2s \n' 
+                    % (x/Bohr, y/Bohr, z/Bohr, s.lower()))
+    f.write("$end\n")
+    
diff --git a/ase/io/v_sim.py b/ase/io/v_sim.py
new file mode 100644
index 0000000..dfd6717
--- /dev/null
+++ b/ase/io/v_sim.py
@@ -0,0 +1,87 @@
+"""
+This module contains functionality for reading an ASE
+Atoms object in V_Sim ascii format.
+
+"""
+
+import os
+
+def read_v_sim(filename='demo.ascii'):
+    """Import V_Sim input file.
+
+    Reads cell, atom positions, etc. from v_sim ascii file
+    """
+
+    from ase import Atoms, units
+    from ase.lattice.spacegroup import cell
+    import re
+
+    if isinstance(filename, str):
+        f = open(filename)
+    else: # Assume it's a file-like object
+        f = filename
+
+    comment = f.readline()
+
+    line = f.readline() + ' ' + f.readline()
+    box = line.split()
+    for i in range(len(box)):
+        box[i] = float(box[i])
+
+    keywords = []
+    positions= []
+    symbols  = []
+    unit     = 1.0
+
+    re_comment = re.compile('^\s*[#!]')
+    re_node    = re.compile('^\s*\S+\s+\S+\s+\S+\s+\S+')
+
+    while(True):
+        line = f.readline()
+
+        if line == '':
+            break # EOF
+
+        p = re_comment.match(line)
+        if p != None:
+            # remove comment character at the beginning of line
+            line = line[p.end():].replace(',', ' ').lower()
+            if line[:8] == "keyword:":
+                keywords.extend(line[8:].split())
+
+        elif(re_node.match(line)):
+            unit = 1.0
+            if not ("reduced" in keywords) :
+                if ("bohr" in keywords) or ("bohrd0" in keywords) or ("atomic" in keywords) or ("atomicd0" in keywords):
+                    unit = units.Bohr
+
+            fields = line.split()
+            positions.append([unit*float(fields[0]),
+                              unit*float(fields[1]),
+                              unit*float(fields[2])])
+            symbols.append(fields[3])
+
+    f.close()
+
+    if ("surface" in keywords) or ("freeBC" in keywords):
+        raise NotImplementedError
+
+    # create atoms object based on the information
+    if ("angdeg" in keywords) :
+        cell = cellpar_to_cell(box)
+    else:
+        unit = 1.0
+        if ("bohr" in keywords) or ("bohrd0" in keywords) or ("atomic" in keywords) or ("atomicd0" in keywords):
+            unit = units.Bohr
+        cell = [[unit*box[0],         0.0,         0.0],
+                [unit*box[1], unit*box[2],         0.0],
+                [unit*box[3], unit*box[4], unit*box[5]]]
+
+    if ("reduced" in keywords) :
+        atoms = Atoms(cell=cell, scaled_positions=positions)
+    else :
+        atoms = Atoms(cell=cell, positions=positions)
+
+    atoms.set_chemical_symbols(symbols)
+
+    return atoms
diff --git a/ase/io/vasp.py b/ase/io/vasp.py
new file mode 100644
index 0000000..8a26277
--- /dev/null
+++ b/ase/io/vasp.py
@@ -0,0 +1,422 @@
+"""
+This module contains functionality for reading and writing an ASE
+Atoms object in VASP POSCAR format.
+
+"""
+
+import os
+
+def get_atomtypes(fname):
+    """Given a file name, get the atomic symbols. 
+
+    The function can get this information from OUTCAR and POTCAR
+    format files.  The files can also be compressed with gzip or
+    bzip2.
+
+    """
+    atomtypes=[]
+    if fname.find('.gz') != -1:
+        import gzip
+        f = gzip.open(fname)
+    elif fname.find('.bz2') != -1:
+        import bz2
+        f = bz2.BZ2File(fname)
+    else:
+        f = open(fname)
+    for line in f:
+        if line.find('TITEL') != -1:
+            atomtypes.append(line.split()[3].split('_')[0].split('.')[0])
+    return atomtypes
+
+def atomtypes_outpot(posfname, numsyms):
+    """Try to retreive chemical symbols from OUTCAR or POTCAR
+    
+    If getting atomtypes from the first line in POSCAR/CONTCAR fails, it might
+    be possible to find the data in OUTCAR or POTCAR, if these files exist.
+
+    posfname -- The filename of the POSCAR/CONTCAR file we're trying to read
+    
+    numsyms -- The number of symbols we must find
+
+    """
+    import os.path as op
+    import glob
+
+    # First check files with exactly same name except POTCAR/OUTCAR instead
+    # of POSCAR/CONTCAR.
+    fnames = [posfname.replace('POSCAR', 'POTCAR').replace('CONTCAR', 
+                                                           'POTCAR')]
+    fnames.append(posfname.replace('POSCAR', 'OUTCAR').replace('CONTCAR',
+                                                               'OUTCAR'))
+    # Try the same but with compressed files
+    fsc = []
+    for fn in fnames:
+        fsc.append(fn + '.gz')
+        fsc.append(fn + '.bz2')
+    for f in fsc:
+        fnames.append(f)
+    # Finally try anything with POTCAR or OUTCAR in the name
+    vaspdir = op.dirname(posfname)
+    fs = glob.glob(vaspdir + '*POTCAR*')
+    for f in fs:
+        fnames.append(f)
+    fs = glob.glob(vaspdir + '*OUTCAR*')
+    for f in fs:
+        fnames.append(f)
+
+    tried = []
+    files_in_dir = os.listdir('.')
+    for fn in fnames:
+        if fn in files_in_dir:
+            tried.append(fn)
+            at = get_atomtypes(fn)
+            if len(at) == numsyms:
+                return at
+
+    raise IOError('Could not determine chemical symbols. Tried files ' 
+                  + str(tried))
+
+
+def get_atomtypes_from_formula(formula):
+    """Return atom types from chemical formula (optionally prepended
+    with and underscore).
+    """
+    from ase.atoms import string2symbols
+    symbols = string2symbols(formula.split('_')[0])
+    atomtypes = [symbols[0]]
+    for s in symbols[1:]:
+        if s != atomtypes[-1]: atomtypes.append(s)
+    return atomtypes
+
+
+def read_vasp(filename='CONTCAR'):
+    """Import POSCAR/CONTCAR type file.
+
+    Reads unitcell, atom positions and constraints from the POSCAR/CONTCAR
+    file and tries to read atom types from POSCAR/CONTCAR header, if this fails
+    the atom types are read from OUTCAR or POTCAR file.
+    """
+ 
+    from ase import Atoms, Atom
+    from ase.constraints import FixAtoms, FixScaled
+    from ase.data import chemical_symbols
+    import numpy as np
+
+    if isinstance(filename, str):
+        f = open(filename)
+    else: # Assume it's a file-like object
+        f = filename
+
+    # First line should contain the atom symbols , eg. "Ag Ge" in
+    # the same order
+    # as later in the file (and POTCAR for the full vasp run)
+    atomtypes = f.readline().split()
+
+    # Sometimes the first line in POSCAR/CONTCAR is of the form
+    # "CoP3_In-3.pos". Check for this case and extract atom types
+    if len(atomtypes) == 1 and '_' in atomtypes[0]:
+        atomtypes = get_atomtypes_from_formula(atomtypes[0])
+
+    lattice_constant = float(f.readline().split()[0])
+
+    # Now the lattice vectors
+    a = []
+    for ii in range(3):
+        s = f.readline().split()
+        floatvect = float(s[0]), float(s[1]), float(s[2])
+        a.append(floatvect)
+
+    basis_vectors = np.array(a) * lattice_constant
+
+    # Number of atoms. Again this must be in the same order as
+    # in the first line
+    # or in the POTCAR or OUTCAR file
+    atom_symbols = []
+    numofatoms = f.readline().split()
+    #vasp5.1 has an additional line which gives the atom types
+    #the following try statement skips this line
+    try:
+        int(numofatoms[0])
+    except ValueError:
+        numofatoms = f.readline().split()
+
+    # check for comments in numofatoms line and get rid of them if necessary
+    commentcheck = np.array(['!' in s for s in numofatoms])
+    if commentcheck.any():
+        # only keep the elements up to the first including a '!':
+        numofatoms = numofatoms[:np.arange(len(numofatoms))[commentcheck][0]]
+        
+    numsyms = len(numofatoms)
+    if len(atomtypes) < numsyms:
+        # First line in POSCAR/CONTCAR didn't contain enough symbols.
+        atomtypes = atomtypes_outpot(f.name, numsyms)
+    else:
+        try:
+            for atype in atomtypes[:numsyms]:
+                if not atype in chemical_symbols:
+                    raise KeyError
+        except KeyError:
+            atomtypes = atomtypes_outpot(f.name, numsyms)
+
+    for i, num in enumerate(numofatoms):
+        numofatoms[i] = int(num)
+        [atom_symbols.append(atomtypes[i]) for na in xrange(numofatoms[i])]
+
+    # Check if Selective dynamics is switched on
+    sdyn = f.readline()
+    selective_dynamics = sdyn[0].lower() == "s"
+
+    # Check if atom coordinates are cartesian or direct
+    if selective_dynamics:
+        ac_type = f.readline()
+    else:
+        ac_type = sdyn
+    cartesian = ac_type[0].lower() == "c" or ac_type[0].lower() == "k"
+    tot_natoms = sum(numofatoms)
+    atoms_pos = np.empty((tot_natoms, 3))
+    if selective_dynamics:
+        selective_flags = np.empty((tot_natoms, 3), dtype=bool)
+    for atom in xrange(tot_natoms):
+        ac = f.readline().split()
+        atoms_pos[atom] = (float(ac[0]), float(ac[1]), float(ac[2]))
+        if selective_dynamics:
+            curflag = []
+            for flag in ac[3:6]:
+                curflag.append(flag == 'F')
+            selective_flags[atom] = curflag
+    # Done with all reading
+    if type(filename) == str:
+        f.close()
+    if cartesian:
+        atoms_pos *= lattice_constant
+    atoms = Atoms(symbols = atom_symbols, cell = basis_vectors, pbc = True)
+    if cartesian:
+        atoms.set_positions(atoms_pos)
+    else:
+        atoms.set_scaled_positions(atoms_pos)
+    if selective_dynamics:
+        constraints = []
+        indices = []
+        for ind, sflags in enumerate(selective_flags):
+            if sflags.any() and not sflags.all():
+                constraints.append(FixScaled(atoms.get_cell(), ind, sflags))
+            elif sflags.all():
+                indices.append(ind)
+        if indices:
+            constraints.append(FixAtoms(indices))
+        if constraints:
+            atoms.set_constraint(constraints)
+    return atoms
+
+def read_vasp_out(filename='OUTCAR',index = -1):
+    """Import OUTCAR type file.
+
+    Reads unitcell, atom positions, energies, and forces from the OUTCAR file
+    and attempts to read constraints (if any) from CONTCAR/POSCAR, if present. 
+    """
+    import os
+    import numpy as np
+    from ase.calculators.singlepoint import SinglePointCalculator
+    from ase import Atoms, Atom
+
+    try:          # try to read constraints, first from CONTCAR, then from POSCAR
+        constr = read_vasp('CONTCAR').constraints
+    except:
+        try:
+            constr = read_vasp('POSCAR').constraints
+        except:
+            constr = None
+
+    if isinstance(filename, str):
+        f = open(filename)
+    else: # Assume it's a file-like object
+        f = filename
+    data    = f.readlines()
+    natoms  = 0
+    images  = []
+    atoms   = Atoms(pbc = True, constraint = constr)
+    energy  = 0
+    species = []
+    species_num = []
+    symbols = []
+    ecount = 0
+    poscount = 0
+    for n,line in enumerate(data):
+        if 'POTCAR:' in line:
+            temp = line.split()[2]
+            for c in ['.','_','1']:
+                if c in temp:
+                    temp = temp[0:temp.find(c)]
+            species += [temp]
+        if 'ions per type' in line:
+            species = species[:len(species)/2]
+            temp = line.split()
+            for ispecies in range(len(species)):
+                species_num += [int(temp[ispecies+4])]
+                natoms += species_num[-1]
+                for iatom in range(species_num[-1]): symbols += [species[ispecies]]
+        if 'direct lattice vectors' in line:
+            cell = []
+            for i in range(3):
+                temp = data[n+1+i].split()
+                cell += [[float(temp[0]), float(temp[1]), float(temp[2])]]
+            atoms.set_cell(cell)
+        if 'FREE ENERGIE OF THE ION-ELECTRON SYSTEM' in line:
+            energy = float(data[n+2].split()[4])
+            if ecount < poscount:
+                # reset energy for LAST set of atoms, not current one - VASP 5.11? and up
+                images[-1].calc.energy = energy
+            ecount += 1
+        if 'POSITION          ' in line:
+            forces = []
+            for iatom in range(natoms):
+                temp    = data[n+2+iatom].split()
+                atoms  += Atom(symbols[iatom],[float(temp[0]),float(temp[1]),float(temp[2])])
+                forces += [[float(temp[3]),float(temp[4]),float(temp[5])]]
+                atoms.set_calculator(SinglePointCalculator(energy,forces,None,None,atoms))
+            images += [atoms]
+            atoms = Atoms(pbc = True, constraint = constr)
+            poscount += 1
+
+    # return requested images, code borrowed from ase/io/trajectory.py
+    if isinstance(index, int):
+        return images[index]
+    else:
+        step = index.step or 1
+        if step > 0:
+            start = index.start or 0
+            if start < 0:
+                start += len(images)
+            stop = index.stop or len(images)
+            if stop < 0:
+                stop += len(images)
+        else:
+            if index.start is None:
+                start = len(images) - 1
+            else:
+                start = index.start
+                if start < 0:
+                    start += len(images)
+            if index.stop is None:
+                stop = -1
+            else:
+                stop = index.stop
+                if stop < 0:
+                    stop += len(images)
+        return [images[i] for i in range(start, stop, step)]
+
+def write_vasp(filename, atoms, label='', direct=False, sort=None, symbol_count = None, long_format=True):
+    """Method to write VASP position (POSCAR/CONTCAR) files.
+
+    Writes label, scalefactor, unitcell, # of various kinds of atoms,
+    positions in cartesian or scaled coordinates (Direct), and constraints
+    to file. Cartesian coordiantes is default and default label is the 
+    atomic species, e.g. 'C N H Cu'.
+    """
+    
+    import numpy as np
+    from ase.constraints import FixAtoms, FixScaled
+
+    if isinstance(filename, str):
+        f = open(filename, 'w')
+    else: # Assume it's a 'file-like object'
+        f = filename
+    
+    if isinstance(atoms, (list, tuple)):
+        if len(atoms) > 1:
+            raise RuntimeError("Don't know how to save more than "+
+                               "one image to VASP input")
+        else:
+            atoms = atoms[0]
+
+    # Write atom positions in scaled or cartesian coordinates
+    if direct:
+        coord = atoms.get_scaled_positions()
+    else:
+        coord = atoms.get_positions()
+
+    if atoms.constraints:
+        sflags = np.zeros((len(atoms), 3), dtype=bool)
+        for constr in atoms.constraints:
+            if isinstance(constr, FixScaled):
+                sflags[constr.a] = constr.mask
+            elif isinstance(constr, FixAtoms):
+                sflags[constr.index] = [True, True, True]
+
+    if sort:
+        ind = np.argsort(atoms.get_chemical_symbols())
+        symbols = np.array(atoms.get_chemical_symbols())[ind]
+        coord = coord[ind]
+        if atoms.constraints:
+            sflags = sflags[ind]
+    else:
+        symbols = atoms.get_chemical_symbols()
+
+    # Create a list sc of (symbol, count) pairs
+    if symbol_count:
+        sc = symbol_count
+    else:
+        sc = []
+        psym = symbols[0]
+        count = 0
+        for sym in symbols:
+            if sym != psym:
+                sc.append((psym, count))
+                psym = sym
+                count = 1
+            else:
+                count += 1
+        sc.append((psym, count))
+
+    # Create the label
+    if label == '':
+        for sym, c in sc:
+            label += '%2s ' % sym
+    f.write(label + '\n')
+
+    # Write unitcell in real coordinates and adapt to VASP convention 
+    # for unit cell
+    # ase Atoms doesn't store the lattice constant separately, so always
+    # write 1.0.
+    f.write('%19.16f\n' % 1.0)
+    if long_format:
+        latt_form = ' %21.16f'
+    else:
+        latt_form = ' %11.6f'
+    for vec in atoms.get_cell():
+        f.write(' ')
+        for el in vec:
+            f.write(latt_form % el)
+        f.write('\n')
+
+    # Numbers of each atom
+    for sym, count in sc:
+        f.write(' %3i' % count)
+    f.write('\n')
+
+    if atoms.constraints:
+        f.write('Selective dynamics\n')
+
+    if direct:
+        f.write('Direct\n')
+    else:
+        f.write('Cartesian\n')
+
+    if long_format:
+        cform = ' %19.16f'
+    else:
+        cform = ' %9.6f'
+    for iatom, atom in enumerate(coord):
+        for dcoord in atom:
+            f.write(cform % dcoord)
+        if atoms.constraints:
+            for flag in sflags[iatom]:
+                if flag:
+                    s = 'F'
+                else:
+                    s = 'T'
+                f.write('%4s' % s)
+        f.write('\n')
+
+    if type(filename) == str:
+        f.close()
diff --git a/ase/io/vnl.py b/ase/io/vnl.py
new file mode 100644
index 0000000..85c407a
--- /dev/null
+++ b/ase/io/vnl.py
@@ -0,0 +1,37 @@
+import numpy as np
+
+from ase.atoms import Atoms
+
+
+class VNL:
+    def __setstate__(self, data):
+        self.data = data
+
+def ac(shape, typecode, data, endian):
+    x = np.fromstring(data, typecode)
+    try:
+        x.shape = shape
+    except ValueError:
+        x = x[::2].copy()
+        x.shape = shape
+        
+    if np.LittleEndian != endian: 
+        return x.byteswap() 
+    else: 
+        return x 
+
+class VNLUnpickler(pickle.Unpickler):
+    def find_class(self, module, name):
+        if module == 'VNLATKStorage.Core.Sample':
+            return VNL
+        if name == 'array_constructor':
+            return ac
+        return pickle.Unpickler.find_class(self, module, name)
+    
+def read_vnl(filename):
+    from cStringIO import StringIO
+    vnl = VNLUnpickler(StringIO(ZipFile(filename).read('0_object'))).load()
+    conf = vnl.data['__properties__']['Atomic Configuration'].data
+    numbers = conf['_dataarray_']
+    positions = conf['_positions_'].data['_dataarray_']
+    return Atoms(numbers=numbers, positions=positions)
diff --git a/ase/io/vtkxml.py b/ase/io/vtkxml.py
new file mode 100644
index 0000000..d8de580
--- /dev/null
+++ b/ase/io/vtkxml.py
@@ -0,0 +1,195 @@
+import numpy as np
+#from Numeric import asarray as Numeric_asarray
+
+#from ase.units import Bohr
+#from ase.parallel import paropen
+
+fast = False
+
+# -------------------------------------------------------------------
+
+from vtk import vtkStructuredPoints, vtkDoubleArray, vtkXMLImageDataWriter
+
+def write_vti(filename, atoms, data):
+
+    #if isinstance(fileobj, str):
+    #    fileobj = paropen(fileobj, 'w')
+        
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            raise ValueError('Can only write one configuration to a VTI file!')
+        atoms = atoms[0]
+
+    if data is None:
+        raise ValueError('VTK XML Image Data (VTI) format requires data!')
+
+    data = np.asarray(data)
+
+    if data.dtype == complex:
+        data = np.abs(data)
+
+    cell = atoms.get_cell()
+
+    assert np.all(cell==np.diag(cell.diagonal())), 'Unit cell must be orthogonal'
+
+    bbox = np.array(zip(np.zeros(3),cell.diagonal())).ravel()
+
+    # Create a VTK grid of structured points
+    spts = vtkStructuredPoints()
+    spts.SetWholeBoundingBox(bbox)
+    spts.SetDimensions(data.shape)
+    spts.SetSpacing(cell.diagonal() / data.shape)
+    #spts.SetSpacing(paw.gd.h_c * Bohr)
+
+    #print 'paw.gd.h_c * Bohr=',paw.gd.h_c * Bohr
+    #print 'atoms.cell.diagonal() / data.shape=', cell.diagonal()/data.shape
+    #assert np.all(paw.gd.h_c * Bohr==cell.diagonal()/data.shape)
+
+    #s = paw.wfs.kpt_u[0].psit_nG[0].copy()
+    #data = paw.get_pseudo_wave_function(band=0, kpt=0, spin=0, pad=False)
+    #spts.point_data.scalars = data.swapaxes(0,2).flatten()
+    #spts.point_data.scalars.name = 'scalars'
+
+    # Allocate a VTK array of type double and copy data
+    da = vtkDoubleArray()
+    da.SetName('scalars')
+    da.SetNumberOfComponents(1)
+    da.SetNumberOfTuples(np.prod(data.shape))
+
+    for i,d in enumerate(data.swapaxes(0,2).flatten()):
+        da.SetTuple1(i,d)
+
+    # Assign the VTK array as point data of the grid
+    spd = spts.GetPointData() # type(spd) is vtkPointData
+    spd.SetScalars(da)
+
+    """
+    from vtk.util.vtkImageImportFromArray import vtkImageImportFromArray
+    iia = vtkImageImportFromArray()
+    #iia.SetArray(Numeric_asarray(data.swapaxes(0,2).flatten()))
+    iia.SetArray(Numeric_asarray(data))
+    ida = iia.GetOutput()
+    ipd = ida.GetPointData()
+    ipd.SetName('scalars')
+    spd.SetScalars(ipd.GetScalars())
+    """
+
+    # Save the ImageData dataset to a VTK XML file.
+    w = vtkXMLImageDataWriter()
+
+    if fast:
+        w.SetDataModeToAppend()
+        w.EncodeAppendedDataOff()
+    else:
+        w.SetDataModeToAscii()
+
+    w.SetFileName(filename)
+    w.SetInput(spts)
+    w.Write()
+
+# -------------------------------------------------------------------
+
+from vtk import vtkStructuredGrid, vtkPoints, vtkXMLStructuredGridWriter
+
+def write_vts(filename, atoms, data=None):
+    raise NotImplementedError
+
+# -------------------------------------------------------------------
+
+from vtk import vtkUnstructuredGrid, vtkPoints, vtkXMLUnstructuredGridWriter
+
+def write_vtu(filename, atoms, data=None):
+
+    #if isinstance(fileobj, str):
+    #    fileobj = paropen(fileobj, 'w')
+        
+    if isinstance(atoms, list):
+        if len(atoms) > 1:
+            raise ValueError('Can only write one configuration to a VTI file!')
+        atoms = atoms[0]
+
+    """
+    if data is None:
+        raise ValueError('VTK XML Unstructured Grid (VTI) format requires data!')
+
+    data = np.asarray(data)
+
+    if data.dtype == complex:
+        data = np.abs(data)
+    """
+
+    cell = atoms.get_cell()
+
+    assert np.all(cell==np.diag(cell.diagonal())), 'Unit cell must be orthogonal' #TODO bounding box with general unit cell?!
+
+    bbox = np.array(zip(np.zeros(3),cell.diagonal())).ravel()
+
+    # Create a VTK grid of structured points
+    ugd = vtkUnstructuredGrid()
+    ugd.SetWholeBoundingBox(bbox)
+
+    """
+    # Allocate a VTK array of type double and copy data
+    da = vtkDoubleArray()
+    da.SetName('scalars')
+    da.SetNumberOfComponents(3)
+    da.SetNumberOfTuples(len(atoms))
+
+    for i,pos in enumerate(atoms.get_positions()):
+        da.SetTuple3(i,pos[0],pos[1],pos[2])
+    """
+    p = vtkPoints()
+    p.SetNumberOfPoints(len(atoms))
+    p.SetDataTypeToDouble()
+    for i,pos in enumerate(atoms.get_positions()):
+        p.InsertPoint(i,pos[0],pos[1],pos[2])
+
+
+    ugd.SetPoints(p)
+
+    # Assign the VTK array as point data of the grid
+    #upd = ugd.GetPointData() # type(spd) is vtkPointData
+    #upd.SetScalars(da)
+
+
+    # Save the UnstructuredGrid dataset to a VTK XML file.
+    w = vtkXMLUnstructuredGridWriter()
+
+    if fast:
+        w.SetDataModeToAppend()
+        w.EncodeAppendedDataOff()
+    else:
+        w.GetCompressor().SetCompressionLevel(0)
+        w.SetDataModeToAscii()
+
+    w.SetFileName(filename)
+    w.SetInput(ugd)
+    w.Write()
+
+
+# -------------------------------------------------------------------
+
+def read_vti(filename):
+    raise NotImplementedError
+
+def read_vts(filename):
+    raise NotImplementedError
+
+def read_vtu(filename):
+    raise NotImplementedError
+
+# -------------------------------------------------------------------
+
+from vtk import vtkXMLFileReadTester
+
+def probe_vtkxml(filename):
+    """something..."""
+
+    r = vtkXMLFileReadTester()
+    r.SetFileName(filename)
+
+    if r.TestReadFile():
+        return r.GetFileDataType()
+    else:
+        return None
+
diff --git a/ase/io/wien2k.py b/ase/io/wien2k.py
new file mode 100644
index 0000000..40962b0
--- /dev/null
+++ b/ase/io/wien2k.py
@@ -0,0 +1,168 @@
+from math import sin, cos, pi, sqrt
+
+import numpy as np
+
+from ase.atoms import Atoms, Atom
+from ase.units import Bohr, Ry
+
+def read_scf(filename):
+    try:
+        f = open(filename + '.scf', 'r')
+        pip = f.readlines()
+        ene = []
+        for line in pip:
+            if line[0:4] == ':ENE':
+                ene.append(float(line[43:59]) * Ry)
+        f.close()
+        return ene
+    except:
+        return None
+
+def read_struct(filename, ase = True):
+    f = open(filename, 'r')
+    pip = f.readlines()
+    lattice = pip[1][0:3]
+    nat = int(pip[1][27:30])
+    cell = np.zeros(6)
+    for i in range(6):
+        cell[i] = float(pip[3][0 + i * 10:10 + i * 10])
+    cell[0:3] = cell[0:3] * Bohr
+    if lattice == 'P  ':
+        lattice = 'P'
+    elif lattice == 'H  ':
+        lattice = 'P'
+        cell[3:6] = [90.0, 90.0, 120.0]
+    elif lattice == 'R  ':
+        lattice = 'R'
+    elif lattice == 'F  ':
+        lattice = 'F'
+    elif lattice == 'B  ':
+        lattice = 'I'
+    elif lattice == 'CXY':
+        lattice = 'C'
+    elif lattice == 'CXZ':
+        lattice = 'B'
+    elif lattice == 'CYZ':
+        lattice = 'A'
+    else:
+        print 'TEST needed'
+    pos = np.array([])
+    atomtype = []
+    rmt = []
+    neq = np.zeros(nat)
+    iline = 4
+    indif = 0
+    for iat in range(nat):
+        indifini = indif
+        if len(pos) == 0:
+            pos = np.array([[float(pip[iline][12:22]),
+                             float(pip[iline][25:35]),
+                             float(pip[iline][38:48])]])
+        else:
+            pos = np.append(pos, np.array([[float(pip[iline][12:22]),
+                                            float(pip[iline][25:35]),
+                                            float(pip[iline][38:48])]]),
+                            axis = 0)
+        indif += 1
+        iline += 1
+        neq[iat] = int(pip[iline][15:17])
+        iline += 1
+        for ieq in range(1, int(neq[iat])):
+            pos = np.append(pos, np.array([[float(pip[iline][12:22]),
+                                            float(pip[iline][25:35]),
+                                            float(pip[iline][38:48])]]),
+                            axis = 0)
+            indif += 1
+            iline += 1
+        for i in range(indif - indifini):
+            atomtype.append(pip[iline][0:2].replace(' ', ''))
+            rmt.append(float(pip[iline][43:48]))
+        iline += 4
+    if ase:
+        cell2 = coorsys(cell)
+        atoms = Atoms(atomtype, pos, pbc = True)
+        atoms.set_cell(cell2, scale_atoms = True)
+        cell2 = np.dot(c2p(lattice), cell2)
+        if lattice == 'R':
+            atoms.set_cell(cell2, scale_atoms = True)
+        else:
+            atoms.set_cell(cell2)
+        return atoms
+    else:
+        return cell, lattice, pos, atomtype, rmt
+
+def write_struct(filename, atoms2 = None, rmt = None, lattice = 'P'):
+    atoms=atoms2.copy()
+    atoms.set_scaled_positions(atoms.get_scaled_positions())
+    f = file(filename, 'w')
+    f.write('ASE generated\n')
+    nat = len(atoms)
+    if rmt == None:
+        rmt = [2.0] * nat
+    f.write(lattice+'   LATTICE,NONEQUIV.ATOMS:%3i\nMODE OF CALC=RELA\n'%nat)
+    cell = atoms.get_cell()
+    metT = np.dot(cell, np.transpose(cell))
+    cell2 = cellconst(metT)
+    cell2[0:3] = cell2[0:3] / Bohr
+    f.write(('%10.6f' * 6) % tuple(cell2) + '\n')
+    #print atoms.get_positions()[0]
+    for ii in range(nat):
+        f.write('ATOM %3i: ' % (ii + 1))
+        pos = atoms.get_scaled_positions()[ii]
+        f.write('X=%10.8f Y=%10.8f Z=%10.8f\n' % tuple(pos))
+        f.write('          MULT= 1          ISPLIT= 1\n')
+        zz = atoms.get_atomic_numbers()[ii]
+        if zz > 71:
+            ro = 0.000005 
+        elif zz > 36:
+            ro = 0.00001
+        elif zz > 18:
+            ro = 0.00005
+        else:
+            ro = 0.0001
+        f.write('%-10s NPT=%5i  R0=%9.8f RMT=%10.4f   Z:%10.5f\n' %
+                (atoms.get_chemical_symbols()[ii], 781, ro, rmt[ii], zz))
+        f.write('LOCAL ROT MATRIX:    %9.7f %9.7f %9.7f\n' % (1.0, 0.0, 0.0))
+        f.write('                     %9.7f %9.7f %9.7f\n' % (0.0, 1.0, 0.0))
+        f.write('                     %9.7f %9.7f %9.7f\n' % (0.0, 0.0, 1.0))
+    f.write('   0\n')
+
+def cellconst(metT):
+    aa = np.sqrt(metT[0, 0])
+    bb = np.sqrt(metT[1, 1])
+    cc = np.sqrt(metT[2, 2])
+    gamma = np.arccos(metT[0, 1] / (aa * bb)) / np.pi * 180.0
+    beta  = np.arccos(metT[0, 2] / (aa * cc)) / np.pi * 180.0
+    alpha = np.arccos(metT[1, 2] / (bb * cc)) / np.pi * 180.0
+    return np.array([aa, bb, cc, alpha, beta, gamma])
+
+def coorsys(latconst):
+    a = latconst[0]
+    b = latconst[1]
+    c = latconst[2]
+    cal = np.cos(latconst[3] * np.pi / 180.0)
+    cbe = np.cos(latconst[4] * np.pi / 180.0)
+    cga = np.cos(latconst[5] * np.pi / 180.0)
+    sal = np.sin(latconst[3] * np.pi / 180.0)
+    sbe = np.sin(latconst[4] * np.pi / 180.0)
+    sga = np.sin(latconst[5] * np.pi / 180.0)
+    return np.array([[a, b * cga, c * cbe],
+                     [0, b * sga, c * (cal - cbe * cga) / sga],
+                     [0, 0, c * np.sqrt(1 - cal**2 - cbe**2 - cga**2 + 2 * cal * cbe * cga) / sga]]).transpose()
+
+def c2p(lattice):
+    # apply as eg. cell2 = np.dot(ct.c2p('F'), cell)
+    if lattice == 'P':
+        cell = np.eye(3)
+    elif lattice == 'F':
+        cell = np.array([[0.0, 0.5, 0.5], [0.5, 0.0, 0.5], [0.5, 0.5, 0.0]])
+    elif lattice == 'I':
+        cell = np.array([[-0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0.5, 0.5, -0.5]])
+    elif lattice == 'C':
+        cell = np.array([[0.5, 0.5, 0.0], [0.5, -0.5, 0.0], [0.0, 0.0, -1.0]])
+    elif lattice == 'R':
+        cell = np.array([[2.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0], [-1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0], [-1.0 / 3.0, -2.0/3.0, 1.0 / 3.0]])
+
+    else:
+        print 'lattice is ' + lattice + '!'
+    return cell
diff --git a/ase/io/xsf.py b/ase/io/xsf.py
new file mode 100644
index 0000000..86288ff
--- /dev/null
+++ b/ase/io/xsf.py
@@ -0,0 +1,176 @@
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.units import Hartree
+from ase.parallel import paropen
+from ase.calculators.singlepoint import SinglePointCalculator
+
+
+def write_xsf(fileobj, images, data=None):
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+        
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    fileobj.write('ANIMSTEPS %d\n' % len(images))
+
+    numbers = images[0].get_atomic_numbers()
+    
+    pbc = images[0].get_pbc()
+    if pbc[2]:
+        fileobj.write('CRYSTAL\n')
+    elif pbc[1]:
+        fileobj.write('SLAB\n')
+    elif pbc[0]:
+        fileobj.write('POLYMER\n')
+    else:
+        fileobj.write('MOLECULE\n')
+
+    for n, atoms in enumerate(images):
+        if pbc.any():
+            fileobj.write('PRIMVEC %d\n' % (n + 1))
+            cell = atoms.get_cell()
+            for i in range(3):
+                fileobj.write(' %.14f %.14f %.14f\n' % tuple(cell[i]))
+
+        fileobj.write('PRIMCOORD %d\n' % (n + 1))
+
+        # Get the forces if it's not too expensive:
+        calc = atoms.get_calculator()
+        if (calc is not None and
+            (hasattr(calc, 'calculation_required') and
+             not calc.calculation_required(atoms,
+                                           ['energy', 'forces', 'stress']))):
+            forces = atoms.get_forces()
+        else:
+            forces = None
+
+        pos = atoms.get_positions()
+
+        fileobj.write(' %d 1\n' % len(pos))
+        for a in range(len(pos)):
+            fileobj.write(' %2d' % numbers[a])
+            fileobj.write(' %20.14f %20.14f %20.14f' % tuple(pos[a]))
+            if forces is None:
+                fileobj.write('\n')
+            else:
+                fileobj.write(' %20.14f %20.14f %20.14f\n' % tuple(forces[a]))
+            
+    if data is None:
+        return
+
+    fileobj.write('BEGIN_BLOCK_DATAGRID_3D\n')
+    fileobj.write(' data\n')
+    fileobj.write(' BEGIN_DATAGRID_3Dgrid#1\n')
+
+    data = np.asarray(data)
+    if data.dtype == complex:
+        data = np.abs(data)
+
+    shape = data.shape
+    fileobj.write('  %d %d %d\n' % shape)
+
+    cell = atoms.get_cell()
+    origin = np.zeros(3)
+    for i in range(3):
+        if not pbc[i]:
+            origin += cell[i] / shape[i]
+    fileobj.write('  %f %f %f\n' % tuple(origin))
+
+    for i in range(3):
+        fileobj.write('  %f %f %f\n' %
+                      tuple(cell[i] * (shape[i] + 1) / shape[i]))
+
+    for x in range(shape[2]):
+        for y in range(shape[1]):
+            fileobj.write('   ')
+            fileobj.write(' '.join(['%f' % d for d in data[x, y]]))
+            fileobj.write('\n')
+        fileobj.write('\n')
+
+    fileobj.write(' END_DATAGRID_3D\n')
+    fileobj.write('END_BLOCK_DATAGRID_3D\n')
+
+
+def read_xsf(fileobj, index=-1, read_data=True):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    readline = fileobj.readline
+    while True:
+        line = readline()
+        if line[0] != '#':
+            line = line.strip()
+            break
+
+    if 'ANIMSTEPS' in line:
+        nimages = int(line.split()[1])
+        line = readline().strip()
+    else:
+        nimages = 1
+
+    if 'CRYSTAL' in line:
+        pbc = True
+    elif 'SLAB' in line:
+        pbc = (True, True, False)
+    elif 'POLYMER' in line:
+        pbc = (True, False, False)
+    else:
+        pbc = False
+
+    images = []
+    for n in range(nimages):
+        cell = None
+        if pbc:
+            line = readline().strip()
+            assert 'PRIMVEC' in line
+            cell = []
+            for i in range(3):
+                cell.append([float(x) for x in readline().split()])
+
+        line = readline().strip()
+        assert 'PRIMCOORD' in line
+
+        natoms = int(readline().split()[0])
+        numbers = []
+        positions = []
+        for a in range(natoms):
+            line = readline().split()
+            numbers.append(int(line[0]))
+            positions.append([float(x) for x in line[1:]])
+
+        positions = np.array(positions)
+        if len(positions[0]) == 3:
+            forces = None
+        else:
+            positions = positions[:, :3]
+            forces = positions[:, 3:] * Hartree
+
+        image = Atoms(numbers, positions, cell=cell, pbc=pbc)
+
+        if forces is not None:
+            image.set_calculator(SinglePointCalculator(None, forces, None,
+                                                       None, image))
+        images.append(image)
+
+    if read_data:
+        line = readline()
+        assert 'BEGIN_BLOCK_DATAGRID_3D' in line
+        line = readline()
+        assert 'BEGIN_DATAGRID_3D' in line
+
+        shape = [int(x) for x in readline().split()]
+        start = [float(x) for x in readline().split()]
+
+        for i in range(3):
+            readline()
+            
+        n_data = shape[0]*shape[1]*shape[2]
+        data = np.array([float(readline())
+                         for s in range(n_data)]).reshape(shape[::-1])
+        data = np.swapaxes(data, 0, 2)
+        
+        return data, images[index]
+
+    return images[index]
diff --git a/ase/io/xyz.py b/ase/io/xyz.py
new file mode 100644
index 0000000..038139d
--- /dev/null
+++ b/ase/io/xyz.py
@@ -0,0 +1,41 @@
+from ase.atoms import Atoms
+from ase.parallel import paropen
+
+
+def read_xyz(fileobj, index=-1):
+    if isinstance(fileobj, str):
+        fileobj = open(fileobj)
+
+    lines = fileobj.readlines()
+    L1 = lines[0].split()
+    if len(L1) == 1:
+        del lines[:2]
+        natoms = int(L1[0])
+    else:
+        natoms = len(lines)
+    images = []
+    while len(lines) >= natoms:
+        positions = []
+        symbols = []
+        for line in lines[:natoms]:
+            symbol, x, y, z = line.split()[:4]
+            symbol = symbol.lower().capitalize()
+            symbols.append(symbol)
+            positions.append([float(x), float(y), float(z)])
+        images.append(Atoms(symbols=symbols, positions=positions))
+        del lines[:natoms + 2]
+    return images[index]
+
+def write_xyz(fileobj, images):
+    if isinstance(fileobj, str):
+        fileobj = paropen(fileobj, 'w')
+
+    if not isinstance(images, (list, tuple)):
+        images = [images]
+
+    symbols = images[0].get_chemical_symbols()
+    natoms = len(symbols)
+    for atoms in images:
+        fileobj.write('%d\n\n' % natoms)
+        for s, (x, y, z) in zip(symbols, atoms.get_positions()):
+            fileobj.write('%-2s %22.15f %22.15f %22.15f\n' % (s, x, y, z))
diff --git a/ase/lattice/__init__.py b/ase/lattice/__init__.py
new file mode 100644
index 0000000..3ed4b15
--- /dev/null
+++ b/ase/lattice/__init__.py
@@ -0,0 +1 @@
+from ase.lattice.bulk import bulk
diff --git a/ase/lattice/bravais.py b/ase/lattice/bravais.py
new file mode 100644
index 0000000..1f0a8a2
--- /dev/null
+++ b/ase/lattice/bravais.py
@@ -0,0 +1,450 @@
+"""Bravais.py - class for generating Bravais lattices etc.
+
+This is a base class for numerous classes setting up pieces of crystal.
+"""
+
+import math
+
+import numpy as np
+
+from ase.atoms import Atoms
+from ase.utils import gcd
+import ase.data
+
+
+class Bravais:
+    """Bravais lattice factory.
+
+    This is a base class for the objects producing various lattices
+    (SC, FCC, ...).
+    """
+
+    # The following methods are NOT defined here, but must be defined
+    # in classes inhering from Bravais:
+    #   get_lattice_constant
+    #   make_crystal_basis
+    # The following class attributes are NOT defined here, but must be defined
+    # in classes inhering from Bravais:
+    #   int_basis
+    #   inverse_basis
+
+    other = {0:(1,2), 1:(2,0), 2:(0,1)}
+
+    # For Bravais lattices with a basis, set the basis here.  Leave as
+    # None if no basis is present.
+    bravais_basis = None
+
+    # If more than one type of element appear in the crystal, give the
+    # order here.  For example, if two elements appear in a 3:1 ratio,
+    # bravais_basis could contain four vectors, and element_basis
+    # could be (0,0,1,0) - the third atom in the basis is different
+    # from the other three.  Leave as None if all atoms are of the
+    # same type.
+    element_basis = None
+
+    # How small numbers should be considered zero in the unit cell?
+    chop_tolerance = 1e-10
+
+    def __call__(self, symbol,
+                 directions=(None,None,None), miller=(None,None,None),
+                 size=(1,1,1), latticeconstant=None,
+                 pbc=True, align=True, debug=0):
+        "Create a lattice."
+        self.size = size
+        self.pbc = pbc
+        self.debug = debug
+        self.process_element(symbol)
+        self.find_directions(directions, miller)
+        if self.debug:
+            self.print_directions_and_miller()
+        self.convert_to_natural_basis()
+        if self.debug >= 2:
+            self.print_directions_and_miller(" (natural basis)")
+        if latticeconstant is None:
+            if self.element_basis is None:
+                self.latticeconstant = self.get_lattice_constant()
+            else:
+                raise ValueError,\
+                      "A lattice constant must be specified for a compound"
+        else:
+            self.latticeconstant = latticeconstant
+        if self.debug:
+            print "Expected number of atoms in unit cell:", self.calc_num_atoms()
+        if self.debug >= 2:
+            print "Bravais lattice basis:", self.bravais_basis
+            if self.bravais_basis is not None:
+                print " ... in natural basis:", self.natural_bravais_basis
+        self.make_crystal_basis()
+        self.make_unit_cell()
+        if align:
+            self.align()
+        return self.make_list_of_atoms()
+
+    def align(self):
+        "Align the first axis along x-axis and the second in the x-y plane."
+        degree = 180/np.pi
+        if self.debug >= 2:
+            print "Basis before alignment:"
+            print self.basis
+        if self.basis[0][0]**2 + self.basis[0][2]**2 < 0.01 * self.basis[0][1]**2:
+            # First basis vector along y axis - rotate 90 deg along z
+            t = np.array([[0, -1, 0],
+                          [1, 0, 0],
+                          [0, 0, 1]], np.float)
+            self.basis = np.dot(self.basis, t)
+            transf = t
+            if self.debug >= 2:
+                print "Rotating -90 degrees around z axis for numerical stability."
+                print self.basis
+        else:
+            transf = np.identity(3, np.float)
+        assert abs(np.linalg.det(transf) - 1) < 1e-6
+        # Rotate first basis vector into xy plane
+        theta = math.atan2(self.basis[0,2], self.basis[0,0])
+        t = np.array([[np.cos(theta), 0, -np.sin(theta)],
+                      [         0, 1, 0          ],
+                      [np.sin(theta), 0, np.cos(theta) ]])
+        self.basis = np.dot(self.basis, t)
+        transf = np.dot(transf, t)
+        if self.debug >= 2:
+            print "Rotating %f degrees around y axis." % (-theta*degree,)
+            print self.basis
+        assert abs(np.linalg.det(transf) - 1) < 1e-6
+        # Rotate first basis vector to point along x axis
+        theta = math.atan2(self.basis[0,1], self.basis[0,0])
+        t = np.array([[np.cos(theta), -np.sin(theta), 0],
+                      [np.sin(theta),  np.cos(theta), 0],
+                      [         0,           0, 1]])
+        self.basis = np.dot(self.basis, t)
+        transf = np.dot(transf, t)
+        if self.debug >= 2:
+            print "Rotating %f degrees around z axis." % (-theta*degree,)
+            print self.basis
+        assert abs(np.linalg.det(transf) - 1) < 1e-6
+        # Rotate second basis vector into xy plane
+        theta = math.atan2(self.basis[1,2], self.basis[1,1])
+        t = np.array([[1, 0, 0],
+                      [0, np.cos(theta), -np.sin(theta)],
+                      [0, np.sin(theta),  np.cos(theta)]])
+        self.basis = np.dot(self.basis, t)
+        transf = np.dot(transf, t)
+        if self.debug >= 2:
+            print "Rotating %f degrees around x axis." % (-theta*degree,)
+            print self.basis
+        assert abs(np.linalg.det(transf) - 1) < 1e-6
+        # Now we better rotate the atoms as well
+        self.atoms = np.dot(self.atoms, transf)
+        # ... and rotate miller_basis
+        self.miller_basis = np.dot(self.miller_basis, transf)
+
+    def make_list_of_atoms(self):
+        "Repeat the unit cell."
+        nrep = self.size[0] * self.size[1] * self.size[2]
+        if nrep <= 0:
+            raise ValueError, "Cannot create a non-positive number of unit cells"
+        # Now the unit cells must be merged.
+        a2 = []
+        e2 = []
+        for i in xrange(self.size[0]):
+            offset = self.basis[0] * i
+            a2.append(self.atoms + offset[np.newaxis,:])
+            e2.append(self.elements)
+        atoms = np.concatenate(a2)
+        elements = np.concatenate(e2)
+        a2 = []
+        e2 = []
+        for j in xrange(self.size[1]):
+            offset = self.basis[1] * j
+            a2.append(atoms + offset[np.newaxis,:])
+            e2.append(elements)
+        atoms = np.concatenate(a2)
+        elements = np.concatenate(e2)
+        a2 = []
+        e2 = []
+        for k in xrange(self.size[2]):
+            offset = self.basis[2] * k
+            a2.append(atoms + offset[np.newaxis,:])
+            e2.append(elements)
+        atoms = np.concatenate(a2)
+        elements = np.concatenate(e2)
+        del a2, e2
+        assert len(atoms) == nrep * len(self.atoms)
+        basis = np.array([[self.size[0],0,0],
+                          [0,self.size[1],0],
+                          [0,0,self.size[2]]])
+        basis = np.dot(basis, self.basis)
+
+        # Tiny elements should be replaced by zero.  The cutoff is
+        # determined by chop_tolerance which is a class attribute.
+        basis = np.where(np.abs(basis) < self.chop_tolerance,
+                         0.0, basis)
+
+        # None should be replaced, and memory should be freed.
+        lattice = Lattice(positions=atoms, cell=basis, numbers=elements,
+                          pbc=self.pbc)
+        lattice.millerbasis = self.miller_basis
+        # Add info for lattice.surface.AddAdsorbate
+        lattice._addsorbate_info_size = np.array(self.size[:2])
+        return lattice
+
+    def process_element(self, element):
+        "Extract atomic number from element"
+        # The types that can be elements: integers and strings
+        if self.element_basis is None:
+            if isinstance(element, type("string")):
+                self.atomicnumber = ase.data.atomic_numbers[element]
+            elif isinstance(element, int):
+                self.atomicnumber = element
+            else:
+                raise TypeError("The symbol argument must be a string or an atomic number.")
+        else:
+            atomicnumber = []
+            try:
+                if len(element) != max(self.element_basis) + 1:
+                    oops = True
+                else:
+                    oops = False
+            except TypeError:
+                oops = True
+            if oops:
+                raise TypeError(
+                    ("The symbol argument must be a sequence of length %d"
+                         +" (one for each kind of lattice position")
+                        % (max(self.element_basis)+1,))
+            for e in element:
+                if isinstance(e, type("string")):
+                    atomicnumber.append(ase.data.atomic_numbers[e])
+                elif isinstance(e, int):
+                    atomicnumber.append(e)
+                else:
+                    raise TypeError("The symbols argument must be a sequence of strings or atomic numbers.")
+            self.atomicnumber = [atomicnumber[i] for i in self.element_basis]
+            assert len(self.atomicnumber) == len(self.bravais_basis)
+
+    def convert_to_natural_basis(self):
+        "Convert directions and miller indices to the natural basis."
+        self.directions = np.dot(self.directions, self.inverse_basis)
+        if self.bravais_basis is not None:
+            self.natural_bravais_basis = np.dot(self.bravais_basis,
+                                                        self.inverse_basis)
+        for i in (0,1,2):
+            self.directions[i] = reduceindex(self.directions[i])
+        for i in (0,1,2):
+            (j,k) = self.other[i]
+            self.miller[i] = reduceindex(self.handedness *
+                                         cross(self.directions[j],
+                                                self.directions[k]))
+
+    def calc_num_atoms(self):
+        v = int(round(abs(np.linalg.det(self.directions))))
+        if self.bravais_basis is None:
+            return v
+        else:
+            return v * len(self.bravais_basis)
+
+    def make_unit_cell(self):
+        "Make the unit cell."
+        # Make three loops, and find the positions in the integral
+        # lattice.  Each time a position is found, the atom is placed
+        # in the real unit cell by put_atom().
+        self.natoms = self.calc_num_atoms()
+        self.nput = 0
+        self.atoms = np.zeros((self.natoms,3), np.float)
+        self.elements = np.zeros(self.natoms, np.int)
+        self.farpoint = farpoint = sum(self.directions)
+        #printprogress = self.debug and (len(self.atoms) > 250)
+        percent = 0
+        # Find the radius of the sphere containing the whole system
+        sqrad = 0
+        for i in (0,1):
+            for j in (0,1):
+                for k in (0,1):
+                    vect = (i * self.directions[0] +
+                            j * self.directions[1] +
+                            k * self.directions[2])
+                    if np.dot(vect,vect) > sqrad:
+                        sqrad = np.dot(vect,vect)
+        del i,j,k
+        # Loop along first crystal axis (i)
+        for (istart, istep) in ((0,1), (-1,-1)):
+            i = istart
+            icont = True
+            while icont:
+                nj = 0
+                for (jstart, jstep) in ((0,1), (-1,-1)):
+                    j = jstart
+                    jcont = True
+                    while jcont:
+                        nk = 0
+                        for (kstart, kstep) in ((0,1), (-1,-1)):
+                            k = kstart
+                            #print "Starting line i=%d, j=%d, k=%d, step=(%d,%d,%d)" % (i,j,k,istep,jstep,kstep)
+                            kcont = True
+                            while kcont:
+                                # Now (i,j,k) loops over Z^3, except that
+                                # the loops can be cut off when we get outside
+                                # the unit cell.
+                                point = np.array((i,j,k))
+                                if self.inside(point):
+                                    self.put_atom(point)
+                                    nk += 1
+                                    nj += 1
+                                # Is k too high?
+                                if np.dot(point,point) > sqrad:
+                                    assert not self.inside(point)
+                                    kcont = False
+                                k += kstep
+                        # Is j too high?
+                        if i*i+j*j > sqrad:
+                            jcont = False
+                        j += jstep
+                # Is i too high?
+                if i*i > sqrad:
+                    icont = False
+                i += istep
+                #if printprogress:
+                #    perce = int(100*self.nput / len(self.atoms))
+                #    if perce > percent + 10:
+                #        print ("%d%%" % perce),
+                #        percent = perce
+        assert(self.nput == self.natoms)
+
+    def inside(self, point):
+        "Is a point inside the unit cell?"
+        return (np.dot(self.miller[0], point) >= 0 and
+                np.dot(self.miller[0], point - self.farpoint) < 0 and
+                np.dot(self.miller[1], point) >= 0 and
+                np.dot(self.miller[1], point - self.farpoint) < 0 and
+                np.dot(self.miller[2], point) >= 0 and
+                np.dot(self.miller[2], point - self.farpoint) < 0)
+
+    def put_atom(self, point):
+        "Place an atom given its integer coordinates."
+        if self.bravais_basis is None:
+            # No basis - just place a single atom
+            pos = np.dot(point, self.crystal_basis)
+            if self.debug >= 2:
+                print ("Placing an atom at (%d,%d,%d) ~ (%.3f, %.3f, %.3f)."
+                       % (tuple(point) + tuple(pos)))
+            self.atoms[self.nput] = pos
+            self.elements[self.nput] = self.atomicnumber
+            self.nput += 1
+        else:
+            for i, offset in enumerate(self.natural_bravais_basis):
+                pos = np.dot(point + offset, self.crystal_basis)
+                if self.debug >= 2:
+                    print ("Placing an atom at (%d+%f, %d+%f, %d+%f) ~ (%.3f, %.3f, %.3f)."
+                           % (point[0], offset[0], point[1], offset[1],
+                              point[2], offset[2], pos[0], pos[1], pos[2]))
+                self.atoms[self.nput] = pos
+                if self.element_basis is None:
+                    self.elements[self.nput] = self.atomicnumber
+                else:
+                    self.elements[self.nput] = self.atomicnumber[i]
+                self.nput += 1
+
+    def find_directions(self, directions, miller):
+        "Find missing directions and miller indices from the specified ones."
+        directions = list(directions)
+        miller = list(miller)
+        # If no directions etc are specified, use a sensible default.
+        if directions == [None, None, None] and miller == [None, None, None]:
+            directions = [[1,0,0], [0,1,0], [0,0,1]]
+        # Now fill in missing directions and miller indices.  This is an
+        # iterative process.
+        change = 1
+        while change:
+            change = False
+            missing = 0
+            for i in (0,1,2):
+                (j,k) = self.other[i]
+                if directions[i] is None:
+                    missing += 1
+                    if miller[j] is not None and miller[k] is not None:
+                        directions[i] = reduceindex(cross(miller[j],
+                                                          miller[k]))
+                        change = True
+                        if self.debug >= 2:
+                            print "Calculating directions[%d] from miller indices" % i
+                if miller[i] is None:
+                    missing += 1
+                    if directions[j] is not None and directions[k] is not None:
+                        miller[i] = reduceindex(cross(directions[j],
+                                                      directions[k]))
+                        change = True
+                        if self.debug >= 2:
+                            print "Calculating miller[%d] from directions" % i
+        if missing:
+            raise ValueError, "Specification of directions and miller indices is incomplete."
+        # Make sure that everything is Numeric arrays
+        self.directions = np.array(directions)
+        self.miller = np.array(miller)
+        # Check for left-handed coordinate system
+        if np.linalg.det(self.directions) < 0:
+            print "WARNING: Creating a left-handed coordinate system!"
+            self.miller = -self.miller
+            self.handedness = -1
+        else:
+            self.handedness = 1
+        # Now check for consistency
+        for i in (0,1,2):
+            (j,k) = self.other[i]
+            m = reduceindex(self.handedness *
+                            cross(self.directions[j], self.directions[k]))
+            if sum(np.not_equal(m, self.miller[i])):
+                print "ERROR: Miller index %s is inconsisten with directions %d and %d" % (i,j,k)
+                print "Miller indices:"
+                print str(self.miller)
+                print "Directions:"
+                print str(self.directions)
+                raise ValueError, "Inconsistent specification of miller indices and directions."
+
+    def print_directions_and_miller(self, txt=""):
+        "Print direction vectors and Miller indices."
+        print "Direction vectors of unit cell%s:" % (txt,)
+        for i in (0,1,2):
+            print "   ", self.directions[i]
+        print "Miller indices of surfaces%s:" % (txt,)
+        for i in (0,1,2):
+            print "   ", self.miller[i]
+
+
+class MillerInfo:
+    """Mixin class to provide information about Miller indices."""
+    def miller_to_direction(self, miller):
+        """Returns the direction corresponding to a given Miller index."""
+        return np.dot(miller, self.millerbasis)
+
+
+class Lattice(Atoms, MillerInfo):
+    """List of atoms initially containing a regular lattice of atoms.
+
+    A part from the usual list of atoms methods this list of atoms type
+    also has a method, `miller_to_direction`, used to convert from Miller
+    indices to directions in the coordinate system of the lattice.
+    """
+    pass
+
+
+# Helper functions
+def cross(a, b):
+    """The cross product of two vectors."""
+    return np.array((a[1]*b[2] - b[1]*a[2],
+                     a[2]*b[0] - b[2]*a[0],
+                     a[0]*b[1] - b[0]*a[1]))
+
+
+def reduceindex(M):
+    "Reduce Miller index to the lowest equivalent integers."
+    oldM = M
+    g = gcd(M[0], M[1])
+    h = gcd(g, M[2])
+    while h != 1:
+        M = M/h
+        g = gcd(M[0], M[1])
+        h = gcd(g, M[2])
+    if np.dot(oldM, M) > 0:
+        return M
+    else:
+        return -M
+
diff --git a/ase/lattice/bulk.py b/ase/lattice/bulk.py
new file mode 100644
index 0000000..d886045
--- /dev/null
+++ b/ase/lattice/bulk.py
@@ -0,0 +1,165 @@
+from math import sqrt
+
+from ase.atoms import Atoms, string2symbols
+from ase.data import reference_states, atomic_numbers, chemical_symbols
+
+
+def bulk(name, crystalstructure=None, a=None, c=None, covera=None,
+         orthorhombic=False, cubic=False):
+    """Creating bulk systems.
+
+    Crystal structure and lattice constant(s) will be guessed if not
+    provided.
+
+    name: str
+        Chemical symbol or symbols as in 'MgO' or 'NaCl'.
+    crystalstructure: str
+        Must be one of sc, fcc, bcc, hcp, diamond, zincblende or
+        rocksalt.
+    a: float
+        Lattice constant.
+    c: float
+        Lattice constant.
+    covera: float
+        c/a raitio used for hcp.  Use sqrt(8/3.0) for ideal ratio.
+    orthorhombic: bool
+        Construct orthorhombic unit cell instead of primitive cell
+        which is the default.
+    cubic: bool
+        Construct cubic unit cell if possible.
+    """
+
+    if a is not None:
+        a = float(a)
+    if c is not None:
+        c = float(c)
+        
+    if covera is not None and c is not None:
+        raise ValueError("Don't specify both c and c/a!")
+
+    if name in chemical_symbols:
+        Z = atomic_numbers[name]
+        ref = reference_states[Z]
+        if ref is not None:
+            xref = ref['symmetry']
+        else:
+            xref = None
+
+    if crystalstructure is None:
+        crystalstructure = xref
+
+    if a is None:
+        assert xref == crystalstructure
+        a = ref['a']
+
+    if crystalstructure == 'hcp':
+        cubic = False
+        if c is not None:
+            covera = c / a
+        elif covera is None:
+            if xref == 'hcp':
+                covera = ref['c/a']
+            else:
+                covera = sqrt(8.0 / 3.0)
+
+    if orthorhombic and crystalstructure != 'sc':
+        return _orthorhombic_bulk(name, crystalstructure, a, covera)
+
+    if cubic and crystalstructure == 'bcc':
+        return _orthorhombic_bulk(name, crystalstructure, a, covera)
+
+    if cubic and crystalstructure != 'sc':
+        return _cubic_bulk(name, crystalstructure, a)
+    
+    if crystalstructure == 'sc':
+        atoms = Atoms(name, cell=(a, a, a), pbc=True)
+    elif crystalstructure == 'fcc':
+        b = a / 2
+        atoms = Atoms(name, cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=True)
+    elif crystalstructure == 'bcc':
+        b = a / 2
+        atoms = Atoms(name, cell=[(-b, b, b), (b, -b, b), (b, b, -b)],
+                      pbc=True)
+    elif crystalstructure == 'hcp':
+        atoms = Atoms(2 * name,
+                      scaled_positions=[(0, 0, 0),
+                                        (1.0 / 3.0, 2.0 / 3.0, 0.5)],
+                      cell=[(a, 0, 0),
+                            (-a / 2, a * sqrt(3) / 2, 0),
+                            (0, 0, covera * a)],
+                      pbc=True)
+    elif crystalstructure == 'diamond':
+        atoms = bulk(2 * name, 'zincblende', a)
+    elif crystalstructure == 'zincblende':
+        s1, s2 = string2symbols(name)
+        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
+        atoms.positions[1] += a / 4
+    elif crystalstructure == 'rocksalt':
+        s1, s2 = string2symbols(name)
+        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
+        atoms.positions[1, 0] += a / 2
+    else:
+        raise ValueError('Unknown crystal structure: ' + crystalstructure)
+    
+    return atoms
+
+
+def _orthorhombic_bulk(name, crystalstructure, a, covera=None):
+    if crystalstructure == 'fcc':
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
+    elif crystalstructure == 'bcc':
+        atoms = Atoms(2 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
+    elif crystalstructure == 'hcp':
+        atoms = Atoms(4 * name,
+                      cell=(a, a * sqrt(3), covera * a),
+                      scaled_positions=[(0, 0, 0),
+                                        (0.5, 0.5, 0),
+                                        (0.5, 1.0 / 6.0, 0.5),
+                                        (0, 2.0 / 3.0, 0.5)],
+                      pbc=True)
+    elif crystalstructure == 'diamond':
+        atoms = _orthorhombic_bulk(2 * name, 'zincblende', a)
+    elif crystalstructure == 'zincblende':
+        s1, s2 = string2symbols(name)
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0, 0.25),
+                                        (0.5, 0.5, 0.5), (0, 0.5, 0.75)])
+    elif crystalstructure == 'rocksalt':
+        s1, s2 = string2symbols(name)
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0),
+                                        (0.5, 0.5, 0.5), (0, 0, 0.5)])
+    else:
+        raise RuntimeError
+    
+    return atoms
+
+
+def _cubic_bulk(name, crystalstructure, a):
+    if crystalstructure == 'fcc':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0, 0.5, 0.5),
+                                        (0.5, 0, 0.5), (0.5, 0.5, 0)])
+    elif crystalstructure == 'diamond':
+        atoms = _cubic_bulk(2 * name, 'zincblende', a)
+    elif crystalstructure == 'zincblende':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.25, 0.25, 0.25),
+                                        (0, 0.5, 0.5), (0.25, 0.75, 0.75),
+                                        (0.5, 0, 0.5), (0.75, 0.25, 0.75),
+                                        (0.5, 0.5, 0), (0.75, 0.75, 0.25)])
+    elif crystalstructure == 'rocksalt':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0, 0),
+                                        (0, 0.5, 0.5), (0.5, 0.5, 0.5),
+                                        (0.5, 0, 0.5), (0, 0, 0.5),
+                                        (0.5, 0.5, 0), (0, 0.5, 0)])
+    else:
+        raise RuntimeError
+    
+    return atoms
diff --git a/ase/lattice/compounds.py b/ase/lattice/compounds.py
new file mode 100644
index 0000000..45de651
--- /dev/null
+++ b/ase/lattice/compounds.py
@@ -0,0 +1,195 @@
+"""Function-like objects creating lattices with more than one element.
+
+These lattice creators are mainly intended as examples for how to build you
+own.  The following crystal structures are defined:
+
+    B1 = NaCl = Rocksalt
+    B2 = CsCl
+    B3 = ZnS = Zincblende
+    L1_2 = AuCu3
+    L1_0 = AuCu
+    TRI_Fe2O3
+    HEX_Fe2O3
+    
+"""
+from ase.lattice.cubic import FaceCenteredCubicFactory, \
+    BodyCenteredCubicFactory, DiamondFactory, SimpleCubicFactory
+from ase.lattice.tetragonal import SimpleTetragonalFactory
+from ase.lattice.triclinic import TriclinicFactory
+from ase.lattice.hexagonal import HexagonalFactory
+
+import numpy as np
+from ase.data import reference_states as _refstate
+
+
+# To prevent a layer of element one on one side, and a layer of
+# element two on the other side, NaCl is based on SimpleCubic instead
+# of on FaceCenteredCubic
+class NaClFactory(SimpleCubicFactory):
+    "A factory for creating NaCl (B1, Rocksalt) lattices."
+
+    bravais_basis = [[0, 0, 0], [0, 0, 0.5], [0, 0.5, 0], [0, 0.5, 0.5],
+                     [0.5, 0, 0], [0.5, 0, 0.5], [0.5, 0.5, 0],
+                     [0.5, 0.5, 0.5]]
+    element_basis = (0, 1, 1, 0, 1, 0, 0, 1)
+    
+
+B1 = NaCl = Rocksalt = NaClFactory()
+
+class CsClFactory(SimpleCubicFactory):
+    "A factory for creating CsCl (B2) lattices."
+    bravais_basis = [[0, 0, 0], [0.5, 0.5, 0.5]]
+    element_basis = (0, 1)
+
+B2 = CsCl = CsClFactory()
+
+
+#The zincblende structure is easily derived from Diamond, which
+#already has the right basis.
+class ZnSFactory(DiamondFactory):
+    "A factory for creating ZnS (B3, Zincblende) lattices."
+    element_basis = (0, 1)
+
+B3 = ZnS = Zincblende = ZnSFactory()
+
+
+# The L1_0 structure is "based on FCC", but is a tetragonal distortion
+# of fcc.  It must therefore be derived from the base-centered
+# tetragonal structure.  That structure, however, does not exist,
+# since it is equivalent to a simple tetragonal structure rotated 45
+# degrees along the z-axis.  Basing L1_2 on that would however give
+# unexpected miller indices.  L1_2 will therefore be based on a simple
+# tetragonal structure, but with a basis corresponding to a
+# base-centered tetragonal.
+class AuCuFactory(SimpleTetragonalFactory):
+    "A factory for creating AuCu (L1_0) lattices (tetragonal symmetry)."
+    bravais_basis = [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
+    element_basis = (0, 1, 1, 0)
+
+AuCu = L1_0 = AuCuFactory()
+
+# The L1_2 structure is "based on FCC", but is really simple cubic
+# with a basis.
+class AuCu3Factory(SimpleCubicFactory):
+    "A factory for creating AuCu3 (L1_2) lattices."
+    bravais_basis = [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
+    element_basis = (0, 1, 1, 1)
+
+AuCu3 = L1_2 = AuCu3Factory()
+
+class TriclinicFe2O3Factory(TriclinicFactory):
+    """A factory for creating hematite (Fe2O3) lattices.
+
+     Rhombohedral unit cell.
+     Pauling L, Hendricks S B
+     Journal of the American Chemical Society 47 (1925) 781-790
+
+     Example::
+
+         #!/usr/bin/env python
+    
+         from ase.lattice.hexagonal import *
+         from ase.lattice.compounds import *
+         import ase.io as io
+         from ase import Atoms, Atom
+    
+         index1=3
+         index2=3
+         index3=3
+         mya = 5.42
+         myb = 5.42
+         myc = 5.42
+         myalpha = 55.28
+         mybeta = 55.28
+         mygamma = 55.28
+         gra = TRI_Fe2O3(symbol = ('Fe', 'O'),
+                         latticeconstant={'a':mya,'b':myb, 'c':myc, 
+                                          'alpha':myalpha, 
+                                          'beta':mybeta,
+                                          'gamma':mygamma},
+                         size=(index1,index2,index3))
+         io.write('rhombohedralUC_Fe2O3.xyz', gra, format='xyz')
+
+     """
+
+    bravais_basis = [[0.10534, 0.10534, 0.10534], [0.39466, 0.39466, 0.39466],
+                     [0.60534, 0.60534, 0.60534], [0.89466, 0.89466, 0.89466],
+                     [0.30569, 0.69431, 0.00000], [0.69431, 0.00000, 0.30569],
+                     [0.00000, 0.30569, 0.69431], [0.19431, 0.80569, 0.50000],
+                     [0.80569, 0.50000, 0.19431], [0.50000, 0.19431, 0.80569]]
+    element_basis = (0, 0, 0, 0, 1, 1, 1, 1, 1, 1)
+
+TRI_Fe2O3 = TriclinicFe2O3Factory()
+
+class HexagonalFe2O3Factory(HexagonalFactory):
+    """A factory for creating hematite (Fe2O3) lattices.
+     With hexagonal unit cell.
+     Blake R L, Hessevick R E, Zoltai T, Finger L W
+     American Mineralogist 51 (1966) 123-129
+     5.038 5.038 13.772 90 90 120 R-3c
+     Fe       0 0 .3553  .0080  .0080 .00029  .0040      0      0
+     O    .3059 0   1/4  .0068  .0083 .00046  .0042 .00058  .0012
+
+     Example:
+     #!/usr/bin/env python
+     from ase.lattice.hexagonal import *
+     from ase.lattice.compounds import *
+     import ase.io as io
+     from ase import Atoms, Atom
+
+     index1=1
+     index2=1
+     index3=1
+     mya = 5.038
+     myb = 5.038
+     myc = 13.772
+     myalpha = 90
+     mybeta = 90
+     mygamma = 120
+     gra = HEX_Fe2O3(symbol = ('Fe', 'O'),
+     latticeconstant={'a':mya,'b':myb, 'c':myc, 
+     'alpha':myalpha, 
+     'beta':mybeta,
+     'gamma':mygamma},
+     size=(index1,index2,index3))
+     io.write('hexaFe2O3.xyz', gra, format='xyz')
+     
+     """
+
+    bravais_basis =  [[0.000000, 0.000000, 0.355300],     
+                      [0.000000, 0.000000, 0.144700], 
+                      [0.000000, 0.000000, 0.644700], 
+                      [0.000000, 0.000000, 0.855300],     
+                      [0.666667, 0.333333, 0.688633], 
+                      [0.666667, 0.333333, 0.478033], 
+                      [0.666667, 0.333333, 0.978033],     
+                      [0.666667, 0.333333, 0.188633], 
+                      [0.333333, 0.666667, 0.021967],  
+                      [0.333333, 0.666667, 0.811367],     
+                      [0.333333, 0.666667, 0.311367], 
+                      [0.333333, 0.666667, 0.521967],
+                      #Fe to O here
+                      [0.305900, 0.000000, 0.250000],
+                      [0.000000, 0.305900, 0.250000],
+                      [0.694100, 0.694100, 0.250000],
+                      [0.694100, 0.000000, 0.750000],
+                      [0.000000, 0.694100, 0.750000],
+                      [0.305900, 0.305900, 0.750000],
+                      [0.972567, 0.333333, 0.583333],
+                      [0.666667, 0.639233, 0.583333],
+                      [0.360767, 0.027433, 0.583333],
+                      [0.360767, 0.333333, 0.083333],
+                      [0.666667, 0.027433, 0.083333],
+                      [0.972567, 0.639233, 0.083333],
+                      [0.639233, 0.666667, 0.916667],
+                      [0.333333, 0.972567, 0.916667],
+                      [0.027433, 0.360767, 0.916667],
+                      [0.027433, 0.666667, 0.416667],
+                      [0.333333, 0.360767, 0.416667],
+                      [0.639233, 0.972567, 0.416667]]
+    element_basis = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                     0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+                     1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
+
+HEX_Fe2O3 = HexagonalFe2O3Factory()
+
diff --git a/ase/lattice/cubic.py b/ase/lattice/cubic.py
new file mode 100644
index 0000000..87fec90
--- /dev/null
+++ b/ase/lattice/cubic.py
@@ -0,0 +1,125 @@
+"""Function-like objects creating cubic lattices (SC, FCC, BCC and Diamond).
+
+The following lattice creators are defined:
+    SimpleCubic
+    FaceCenteredCubic
+    BodyCenteredCubic
+    Diamond
+"""
+
+from ase.lattice.bravais import Bravais
+import numpy as np
+from ase.data import reference_states as _refstate
+
+class SimpleCubicFactory(Bravais):
+    "A factory for creating simple cubic lattices."
+
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "sc"
+
+    # The natural basis vectors of the crystal structure
+    int_basis = np.array([[1, 0, 0],
+                          [0, 1, 0],
+                          [0, 0, 1]])
+    basis_factor = 1.0
+
+    # Converts the natural basis back to the crystallographic basis
+    inverse_basis = np.array([[1, 0, 0],
+                              [0, 1, 0],
+                              [0, 0, 1]])
+    inverse_basis_factor = 1.0
+
+    # For checking the basis volume
+    atoms_in_unit_cell = 1
+    
+    def get_lattice_constant(self):
+        "Get the lattice constant of an element with cubic crystal structure."
+        if _refstate[self.atomicnumber]['symmetry'] != self.xtal_name:
+            raise ValueError, (("Cannot guess the %s lattice constant of"
+                                + " an element with crystal structure %s.")
+                               % (self.xtal_name,
+                                  _refstate[self.atomicnumber]['symmetry']))
+        return _refstate[self.atomicnumber]['a']
+
+    def make_crystal_basis(self):
+        "Make the basis matrix for the crystal unit cell and the system unit cell."
+        self.crystal_basis = (self.latticeconstant * self.basis_factor
+                              * self.int_basis)
+        self.miller_basis = self.latticeconstant * np.identity(3)
+        self.basis = np.dot(self.directions, self.crystal_basis)
+        self.check_basis_volume()
+
+    def check_basis_volume(self):
+        "Check the volume of the unit cell."
+        vol1 = abs(np.linalg.det(self.basis))
+        cellsize = self.atoms_in_unit_cell
+        if self.bravais_basis is not None:
+            cellsize *= len(self.bravais_basis)
+        vol2 = (self.calc_num_atoms() * self.latticeconstant**3 / cellsize)
+        assert abs(vol1-vol2) < 1e-5
+
+    def find_directions(self, directions, miller):
+        "Find missing directions and miller indices from the specified ones."
+        directions = list(directions)
+        miller = list(miller)
+        # Process keyword "orthogonal"
+        self.find_ortho(directions)
+        self.find_ortho(miller)
+        Bravais.find_directions(self, directions, miller)
+        
+    def find_ortho(self, idx):
+        "Replace keyword 'ortho' or 'orthogonal' with a direction."
+        for i in range(3):
+            if isinstance(idx[i], str) and (idx[i].lower() == "ortho" or
+                                            idx[i].lower() == "orthogonal"):
+                if self.debug:
+                    print "Calculating orthogonal direction", i
+                    print  idx[i-2], "X", idx[i-1],  
+                idx[i] = reduceindex(cross(idx[i-2], idx[i-1]))
+                if self.debug:
+                    print "=", idx[i]
+                
+
+SimpleCubic = SimpleCubicFactory()
+
+class FaceCenteredCubicFactory(SimpleCubicFactory):
+    "A factory for creating face-centered cubic lattices."
+
+    xtal_name = "fcc"
+    int_basis = np.array([[0, 1, 1],
+                          [1, 0, 1],
+                          [1, 1, 0]])
+    basis_factor = 0.5
+    inverse_basis = np.array([[-1, 1, 1],
+                              [1, -1, 1],
+                              [1, 1, -1]])
+    inverse_basis_factor = 1.0
+
+    atoms_in_unit_cell = 4
+    
+FaceCenteredCubic = FaceCenteredCubicFactory()
+
+class BodyCenteredCubicFactory(SimpleCubicFactory):
+    "A factory for creating body-centered cubic lattices."
+
+    xtal_name = "bcc"
+    int_basis = np.array([[-1, 1, 1],
+                          [1, -1, 1],
+                          [1, 1, -1]])
+    basis_factor = 0.5
+    inverse_basis = np.array([[0, 1, 1],
+                              [1, 0, 1],
+                              [1, 1, 0]])
+    inverse_basis_factor = 1.0
+
+    atoms_in_unit_cell = 2
+    
+BodyCenteredCubic = BodyCenteredCubicFactory()
+
+class DiamondFactory(FaceCenteredCubicFactory):
+    "A factory for creating diamond lattices."
+    xtal_name = "diamond"
+    bravais_basis = [[0,0,0], [0.25, 0.25, 0.25]]
+    
+Diamond = DiamondFactory()
+
diff --git a/ase/lattice/general_surface.py b/ase/lattice/general_surface.py
new file mode 100644
index 0000000..8af4125
--- /dev/null
+++ b/ase/lattice/general_surface.py
@@ -0,0 +1,105 @@
+import numpy as np
+from numpy.linalg import norm, solve
+
+from ase.utils import gcd
+from ase.lattice.bulk import bulk
+
+
+def surface(lattice, indices, layers, vacuum=0.0, tol=1e-10):
+    """Create surface from a given lattice and Miller indices.
+    
+    lattice: Atoms object or str
+        Bulk lattice structure of alloy or pure metal.  One can also
+        give the chemical symbol as a string, in which case the
+        correct bulk lattice will be generated automatically.
+    indices: sequence of three int
+        Surface normal in Miller indices (h,k,l).
+    layers: int
+        Number of equivalent layers of the slab.
+    vacuum: float
+        Amount of vacuum added on both sides of the slab.
+    """
+
+    indices = np.asarray(indices)
+
+    if indices.shape != (3,) or not indices.any() or indices.dtype != int:
+        raise ValueError('%s is an invalid surface type' % indices)
+
+    if isinstance(lattice, str):
+        lattice = bulk(lattice, cubic=True)
+
+    h, k, l = indices
+    h0, k0, l0 = (indices == 0)
+    if h0 and k0 or h0 and l0 or k0 and l0:  # if two indices are zero
+        if not h0:
+            c1, c2, c3 = [(0, 1, 0), (0, 0, 1), (1, 0, 0)]
+        if not k0:
+            c1, c2, c3 = [(1, 0, 0), (0, 0, 1), (0, 1, 0)]
+        if not l0:
+            c1, c2, c3 = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
+    else:
+        p, q = ext_gcd(k, l)
+        a1, a2, a3 = lattice.cell
+        
+        # constants describing the dot product of basis c1 and c2:
+        # dot(c1,c2) = k1+i*k2, i in Z
+        k1 = np.dot(p * (k * a1 - h * a2) + q * (l * a1 - h * a3),
+                    l * a2 - k * a3)
+        k2 = np.dot(l * (k * a1 - h * a2) - k * (l * a1 - h * a3),
+                    l * a2 - k * a3)
+
+        if abs(k2) > tol: 
+            i = -int(round(k1 / k2))  # i corresponding to the optimal basis
+            p, q = p + i * l, q - i * k
+            
+        a, b = ext_gcd(p * k + q * l, h)
+
+        c1 = (p * k + q * l, -p * h, -q * h)
+        c2 = np.array((0, l, -k)) // abs(gcd(l, k))
+        c3 = (b, a * p, a * q)
+
+    surf = build(lattice, np.array([c1, c2, c3]), layers, tol)
+    surf.center(vacuum=vacuum, axis=2)
+    return surf
+
+
+def build(lattice, basis, layers, tol):
+    surf = lattice.copy()
+    scaled = solve(basis.T, surf.get_scaled_positions().T).T
+    scaled -= np.floor(scaled + tol)
+    surf.set_scaled_positions(scaled)
+    surf.set_cell(np.dot(basis, surf.cell), scale_atoms=True)
+    surf *= (1, 1, layers)
+
+    a1, a2, a3 = surf.cell
+    surf.set_cell([a1, a2,
+                   np.cross(a1, a2) * np.dot(a3, np.cross(a1, a2)) /
+                   norm(np.cross(a1, a2))**2])
+    
+    # Change unit cell to have the x-axis parallel with a surface vector
+    # and z perpendicular to the surface:
+    a1, a2, a3 = surf.cell
+    surf.set_cell([(norm(a1), 0, 0),
+                   (np.dot(a1, a2) / norm(a1),
+                    np.sqrt(norm(a2)**2 - (np.dot(a1, a2) / norm(a1))**2), 0),
+                   (0, 0, -norm(a3))],
+                  scale_atoms=True)
+
+    surf.pbc = (True, True, False)
+
+    # Move atoms into the unit cell:
+    scaled = surf.get_scaled_positions()
+    scaled[:, :2] %= 1
+    surf.set_scaled_positions(scaled)
+
+    return surf
+
+
+def ext_gcd(a, b):
+    if b == 0:
+        return 1, 0
+    elif a % b == 0:
+        return 0, 1
+    else:
+        x, y = ext_gcd(b, a % b)
+        return y, x - y * (a / b)
diff --git a/ase/lattice/hexagonal.py b/ase/lattice/hexagonal.py
new file mode 100644
index 0000000..2b1e34a
--- /dev/null
+++ b/ase/lattice/hexagonal.py
@@ -0,0 +1,117 @@
+"""Function-like object creating hexagonal lattices.
+
+The following lattice creators are defined:
+
+* Hexagonal
+* HexagonalClosedPacked
+* Graphite
+* Graphene
+
+Example for using Graphene to create atoms object gra::
+
+    from ase.lattice.hexagonal import *
+    import ase.io as io
+    from ase import Atoms, Atom
+    
+    index1=6
+    index2=7
+    mya = 2.45
+    myc = 20.0
+    
+    gra = Graphene(symbol = 'C',latticeconstant={'a':mya,'c':myc},
+                   size=(index1,index2,1))
+    io.write('test.xyz', gra, format='xyz')
+"""
+
+from ase.lattice.triclinic import TriclinicFactory
+import numpy as np
+from ase.data import reference_states as _refstate
+
+
+class HexagonalFactory(TriclinicFactory):
+    "A factory for creating simple hexagonal lattices."
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "hexagonal"
+
+    def make_crystal_basis(self):
+        "Make the basis matrix for the crystal unit cell and the system unit cell."
+        # First convert the basis specification to a triclinic one
+        if type(self.latticeconstant) == type({}):
+            self.latticeconstant['alpha'] = 90
+            self.latticeconstant['beta'] = 90
+            self.latticeconstant['gamma'] = 120
+            self.latticeconstant['b/a'] = 1.0
+        else:
+            if len(self.latticeconstant) == 2:
+                a,c = self.latticeconstant
+                self.latticeconstant = (a,a,c,90,90,120)
+            else:
+                raise ValueError, "Improper lattice constants for hexagonal crystal."
+        TriclinicFactory.make_crystal_basis(self)
+
+    def find_directions(self, directions, miller):
+        """Find missing directions and miller indices from the specified ones.
+
+        Also handles the conversion of hexagonal-style 4-index notation to
+        the normal 3-index notation.
+        """
+        directions = list(directions)
+        miller = list(miller)
+        for obj in (directions,miller):
+            for i in range(3):
+                if obj[i] is not None:
+                    (a,b,c,d) = obj[i]
+                    if a + b + c != 0:
+                        raise ValueError(
+                            ("(%d,%d,%d,%d) is not a valid hexagonal Miller " +
+                             "index, as the sum of the first three numbers " +
+                             "should be zero.") % (a,b,c,d))
+                    x = 4*a + 2*b
+                    y = 2*a + 4*b
+                    z = 3*d
+                    obj[i] = (x,y,z)
+        TriclinicFactory.find_directions(self, directions, miller)
+        
+    def print_directions_and_miller(self, txt=""):
+        "Print direction vectors and Miller indices."
+        print "Direction vectors of unit cell%s:" % (txt,)
+        for i in (0,1,2):
+            self.print_four_vector("[]", self.directions[i])
+        print "Miller indices of surfaces%s:" % (txt,)
+        for i in (0,1,2):
+            self.print_four_vector("()", self.miller[i])
+
+    def print_four_vector(self, bracket, numbers):
+        bra, ket = bracket
+        (x,y,z) = numbers
+        a = 2*x - y
+        b = -x + 2*y
+        c = -x -y
+        d = 2*z
+        print "   %s%d, %d, %d%s  ~  %s%d, %d, %d, %d%s" % \
+              (bra,x,y,z,ket, bra,a,b,c,d,ket)
+        
+        
+Hexagonal = HexagonalFactory()
+
+class HexagonalClosedPackedFactory(HexagonalFactory):
+    "A factory for creating HCP lattices."
+    xtal_name = "hcp"
+    bravais_basis = [[0,0,0], [1.0/3.0, 2.0/3.0, 0.5]]
+
+HexagonalClosedPacked = HexagonalClosedPackedFactory()
+
+class GraphiteFactory(HexagonalFactory):
+    "A factory for creating graphite lattices."
+    xtal_name = "graphite"
+    bravais_basis = [[0,0,0], [1.0/3.0, 2.0/3.0, 0], [1.0/3.0,2.0/3.0,0.5], [2.0/3.0,1.0/3.0,0.5]]
+
+Graphite = GraphiteFactory()
+
+class GrapheneFactory(HexagonalFactory):
+    "A factory for creating graphene lattices."
+    xtal_name = "graphene"
+    bravais_basis = [[0,0,0], [1.0/3.0, 2.0/3.0, 0]]
+
+Graphene = GrapheneFactory()
+
diff --git a/ase/lattice/monoclinic.py b/ase/lattice/monoclinic.py
new file mode 100644
index 0000000..d7f7272
--- /dev/null
+++ b/ase/lattice/monoclinic.py
@@ -0,0 +1,47 @@
+"""Function-like object creating monoclinic lattices.
+
+The following lattice creator is defined:
+    SimpleMonoclinic
+    BaseCenteredMonoclinic
+"""
+
+from ase.lattice.triclinic import TriclinicFactory
+import numpy as np
+from ase.data import reference_states as _refstate
+
+
+class SimpleMonoclinicFactory(TriclinicFactory):
+    "A factory for creating simple monoclinic lattices."
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "monoclinic"
+
+    def make_crystal_basis(self):
+        "Make the basis matrix for the crystal unit cell and the system unit cell."
+        # First convert the basis specification to a triclinic one
+        if type(self.latticeconstant) == type({}):
+            self.latticeconstant['beta'] = 90
+            self.latticeconstant['gamma'] = 90
+        else:
+            if len(self.latticeconstant) == 4:
+                self.latticeconstant = self.latticeconstant + (90,90)
+            else:
+                raise ValueError, "Improper lattice constants for monoclinic crystal."
+
+        TriclinicFactory.make_crystal_basis(self)
+        
+SimpleMonoclinic = SimpleMonoclinicFactory()
+
+class BaseCenteredMonoclinicFactory(SimpleMonoclinicFactory):
+    # The natural basis vectors of the crystal structure
+    int_basis = np.array([[1, -1, 0],
+                          [1, 1, 0],
+                          [0, 0, 2]])
+    basis_factor = 0.5
+
+    # Converts the natural basis back to the crystallographic basis
+    inverse_basis = np.array([[1, 1, 0],
+                              [-1, 1, 0],
+                              [0, 0, 1]])
+    inverse_basis_factor = 1.0
+
+BaseCenteredMonoclinic = BaseCenteredMonoclinicFactory()
diff --git a/ase/lattice/orthorhombic.py b/ase/lattice/orthorhombic.py
new file mode 100644
index 0000000..8abce18
--- /dev/null
+++ b/ase/lattice/orthorhombic.py
@@ -0,0 +1,144 @@
+"""Function-like objects creating orthorhombic lattices.
+
+The following lattice creators are defined:
+    SimleOrthorhombic
+    BaseCenteredOrthorhombic
+    BodyCenteredOrthorhombic
+    FaceCenteredOrthorhombic
+"""
+
+from ase.lattice.bravais import Bravais
+import numpy as np
+from ase.data import reference_states as _refstate
+
+
+class SimpleOrthorhombicFactory(Bravais):
+    "A factory for creating simple orthorhombic lattices."
+
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "orthorhombic"
+
+    # The natural basis vectors of the crystal structure
+    int_basis = np.array([[1, 0, 0],
+                          [0, 1, 0],
+                          [0, 0, 1]])
+    basis_factor = 1.0
+
+    # Converts the natural basis back to the crystallographic basis
+    inverse_basis = np.array([[1, 0, 0],
+                              [0, 1, 0],
+                              [0, 0, 1]])
+    inverse_basis_factor = 1.0
+
+    def get_lattice_constant(self):
+        "Get the lattice constant of an element with orhtorhombic crystal structure."
+        if _refstate[self.atomicnumber]['symmetry'] != self.xtal_name:
+            raise ValueError, (("Cannot guess the %s lattice constant of"
+                                + " an element with crystal structure %s.")
+                               % (self.xtal_name,
+                                  _refstate[self.atomicnumber]['symmetry']))
+        return _refstate[self.atomicnumber].copy()
+
+    def make_crystal_basis(self):
+        "Make the basis matrix for the crystal unit cell and the system unit cell."
+        lattice = self.latticeconstant
+        if type(lattice) == type({}):
+            a = lattice['a']
+            try:
+                b = lattice['b']
+            except KeyError:
+                b = a * lattice['b/a']
+            try:
+                c = lattice['c']
+            except KeyError:
+                c = a * lattice['c/a']
+        else:
+            if len(lattice) == 3:
+                (a,b,c) = lattice
+            else:
+                raise ValueError, "Improper lattice constants for orthorhombic crystal."
+
+        lattice = np.array([[a,0,0],[0,b,0],[0,0,c]])
+        self.latticeconstant = lattice
+        self.miller_basis = lattice
+        self.crystal_basis = (self.basis_factor *
+                              np.dot(self.int_basis, lattice))
+        self.basis = np.dot(self.directions, self.crystal_basis)
+        self.check_basis_volume()
+
+    def check_basis_volume(self):
+        "Check the volume of the unit cell."
+        vol1 = abs(np.linalg.det(self.basis))
+        vol2 = self.calc_num_atoms() * np.linalg.det(self.latticeconstant)
+        if self.bravais_basis is not None:
+            vol2 /= len(self.bravais_basis)
+        if abs(vol1-vol2) > 1e-5:
+            print "WARNING: Got volume %f, expected %f" % (vol1, vol2)
+
+SimpleOrthorhombic = SimpleOrthorhombicFactory()
+
+class BaseCenteredOrthorhombicFactory(SimpleOrthorhombicFactory):
+    "A factory for creating base-centered orthorhombic lattices."
+
+    # The natural basis vectors of the crystal structure
+    int_basis = np.array([[1, -1, 0],
+                          [1, 1, 0],
+                          [0, 0, 2]])
+    basis_factor = 0.5
+
+    # Converts the natural basis back to the crystallographic basis
+    inverse_basis = np.array([[1, 1, 0],
+                              [-1, 1, 0],
+                              [0, 0, 1]])
+    inverse_basis_factor = 1.0
+
+    def check_basis_volume(self):
+        "Check the volume of the unit cell."
+        vol1 = abs(np.linalg.det(self.basis))
+        vol2 = self.calc_num_atoms() * np.linalg.det(self.latticeconstant) / 2.0
+        if abs(vol1-vol2) > 1e-5:
+            print "WARNING: Got volume %f, expected %f" % (vol1, vol2)
+
+BaseCenteredOrthorhombic = BaseCenteredOrthorhombicFactory()
+
+class BodyCenteredOrthorhombicFactory(SimpleOrthorhombicFactory):
+    "A factory for creating body-centered orthorhombic lattices."
+
+    int_basis = np.array([[-1, 1, 1],
+                          [1, -1, 1],
+                          [1, 1, -1]])
+    basis_factor = 0.5
+    inverse_basis = np.array([[0, 1, 1],
+                              [1, 0, 1],
+                              [1, 1, 0]])
+    inverse_basis_factor = 1.0
+
+    def check_basis_volume(self):
+        "Check the volume of the unit cell."
+        vol1 = abs(np.linalg.det(self.basis))
+        vol2 = self.calc_num_atoms() * np.linalg.det(self.latticeconstant) / 2.0
+        if abs(vol1-vol2) > 1e-5:
+            print "WARNING: Got volume %f, expected %f" % (vol1, vol2)
+
+BodyCenteredOrthorhombic = BodyCenteredOrthorhombicFactory()
+class FaceCenteredOrthorhombicFactory(SimpleOrthorhombicFactory):
+    "A factory for creating face-centered orthorhombic lattices."
+
+    int_basis = np.array([[0, 1, 1],
+                          [1, 0, 1],
+                          [1, 1, 0]])
+    basis_factor = 0.5
+    inverse_basis = np.array([[-1, 1, 1],
+                              [1, -1, 1],
+                              [1, 1, -1]])
+    inverse_basis_factor = 1.0
+
+    def check_basis_volume(self):
+        "Check the volume of the unit cell."
+        vol1 = abs(np.linalg.det(self.basis))
+        vol2 = self.calc_num_atoms() * np.linalg.det(self.latticeconstant) / 4.0
+        if abs(vol1-vol2) > 1e-5:
+            print "WARNING: Got volume %f, expected %f" % (vol1, vol2)
+
+FaceCenteredOrthorhombic = FaceCenteredOrthorhombicFactory()
+
diff --git a/ase/lattice/spacegroup/__init__.py b/ase/lattice/spacegroup/__init__.py
new file mode 100644
index 0000000..c6f99a7
--- /dev/null
+++ b/ase/lattice/spacegroup/__init__.py
@@ -0,0 +1,4 @@
+from spacegroup import Spacegroup
+from crystal import crystal
+
+__all__ = ['Spacegroup', 'crystal']
diff --git a/ase/lattice/spacegroup/cell.py b/ase/lattice/spacegroup/cell.py
new file mode 100644
index 0000000..9ab2a3c
--- /dev/null
+++ b/ase/lattice/spacegroup/cell.py
@@ -0,0 +1,115 @@
+# Copyright (C) 2010, Jesper Friis
+# (see accompanying license files for details).
+
+import numpy as np
+from numpy import pi, sin, cos, tan, arcsin, arccos, arctan, sqrt
+from numpy import dot
+from numpy.linalg import norm
+
+import ase
+
+
+__all__ = ['cell_to_cellpar', 'cellpar_to_cell', 'metric_from_cell']
+
+
+def unit_vector(x):
+    """Return a unit vector in the same direction as x."""
+    y = np.array(x, dtype='float')
+    return y/norm(y)
+
+
+def angle(x, y):
+    """Return the angle between vectors a and b in degrees."""
+    return arccos(dot(x, y)/(norm(x)*norm(y)))*180./pi
+
+
+def cell_to_cellpar(cell):
+    """Returns the cell parameters [a, b, c, alpha, beta, gamma] as a
+    numpy array."""
+    va, vb, vc = cell
+    a = np.linalg.norm(va)
+    b = np.linalg.norm(vb)
+    c = np.linalg.norm(vc)
+    alpha = 180.0/pi*arccos(dot(vb, vc)/(b*c))
+    beta  = 180.0/pi*arccos(dot(vc, va)/(c*a))
+    gamma = 180.0/pi*arccos(dot(va, vb)/(a*b))
+    return np.array([a, b, c, alpha, beta, gamma])
+        
+
+def cellpar_to_cell(cellpar, ab_normal=(0,0,1), a_direction=None):
+    """Return a 3x3 cell matrix from `cellpar` = [a, b, c, alpha,
+    beta, gamma].  The returned cell is orientated such that a and b
+    are normal to `ab_normal` and a is parallel to the projection of
+    `a_direction` in the a-b plane.
+
+    Default `a_direction` is (1,0,0), unless this is parallel to
+    `ab_normal`, in which case default `a_direction` is (0,0,1).
+
+    The returned cell has the vectors va, vb and vc along the rows. The
+    cell will be oriented such that va and vb are normal to `ab_normal`
+    and va will be along the projection of `a_direction` onto the a-b
+    plane.
+
+    Example:
+
+    >>> cell = cellpar_to_cell([1, 2, 4,  10,  20, 30], (0,1,1), (1,2,3))
+    >>> np.round(cell, 3)
+    array([[ 0.816, -0.408,  0.408],
+           [ 1.992, -0.13 ,  0.13 ],
+           [ 3.859, -0.745,  0.745]])
+
+    """
+    if a_direction is None:
+        if np.linalg.norm(np.cross(ab_normal, (1,0,0))) < 1e-5:
+            a_direction = (0,0,1)
+        else:
+            a_direction = (1,0,0)
+
+    # Define rotated X,Y,Z-system, with Z along ab_normal and X along
+    # the projection of a_direction onto the normal plane of Z.
+    ad = np.array(a_direction)
+    Z = unit_vector(ab_normal)
+    X = unit_vector(ad - dot(ad, Z)*Z)
+    Y = np.cross(Z, X)
+
+    # Express va, vb and vc in the X,Y,Z-system
+    alpha, beta, gamma = 90., 90., 90.
+    if isinstance(cellpar, (int, long, float)):
+        a = b = c = cellpar
+    elif len(cellpar) == 1:
+        a = b = c = cellpar[0]
+    elif len(cellpar) == 3:
+        a, b, c = cellpar
+        alpha, beta, gamma = 90., 90., 90.
+    else:
+        a, b, c, alpha, beta, gamma = cellpar
+    alpha *= pi/180.0
+    beta *= pi/180.0
+    gamma *= pi/180.0
+    va = a * np.array([1, 0, 0])
+    vb = b * np.array([cos(gamma), sin(gamma), 0])
+    cx = cos(beta)
+    cy = (cos(alpha) - cos(beta)*cos(gamma))/sin(gamma)
+    cz = sqrt(1. - cx*cx - cy*cy)
+    vc = c * np.array([cx, cy, cz])
+
+    # Convert to the Cartesian x,y,z-system
+    abc = np.vstack((va, vb, vc))
+    T = np.vstack((X, Y, Z))
+    cell = dot(abc, T)
+    return cell
+
+
+def metric_from_cell(cell):
+    """Calculates the metric matrix from cell, which is given in the
+    Cartesian system."""
+    cell = np.asarray(cell, dtype=float)
+    return np.dot(cell, cell.T)
+
+
+
+
+if __name__ == '__main__':
+    import doctest
+    print 'doctest: ', doctest.testmod()
+
diff --git a/ase/lattice/spacegroup/crystal.py b/ase/lattice/spacegroup/crystal.py
new file mode 100644
index 0000000..4715692
--- /dev/null
+++ b/ase/lattice/spacegroup/crystal.py
@@ -0,0 +1,173 @@
+# Copyright (C) 2010, Jesper Friis
+# (see accompanying license files for details).
+
+"""
+A module for ASE for simple creation of crystalline structures from
+knowledge of the space group.
+
+"""
+
+import numpy as np
+
+import ase
+from ase.atoms import string2symbols
+from spacegroup import Spacegroup
+from cell import cellpar_to_cell
+
+__all__ = ['crystal']
+
+
+
+def crystal(symbols=None, basis=None, spacegroup=1, setting=1, 
+            cell=None, cellpar=None, 
+            ab_normal=(0,0,1), a_direction=None, size=(1,1,1),
+            ondublicates='warn', symprec=0.001, 
+            pbc=True, primitive_cell=False, **kwargs):
+    """Create an Atoms instance for a conventional unit cell of a
+    space group.
+
+    Parameters:
+
+    symbols : str | sequence of str | sequence of Atom | Atoms
+        Element symbols of the unique sites.  Can either be a string
+        formula or a sequence of element symbols. E.g. ('Na', 'Cl')
+        and 'NaCl' are equivalent.  Can also be given as a sequence of
+        Atom objects or an Atoms object.
+    basis : list of scaled coordinates 
+        Positions of the unique sites corresponding to symbols given
+        either as scaled positions or through an atoms instance.  Not
+        needed if *symbols* is a sequence of Atom objects or an Atoms
+        object.
+    spacegroup : int | string | Spacegroup instance
+        Space group given either as its number in International Tables
+        or as its Hermann-Mauguin symbol.
+    setting : 1 | 2
+        Space group setting.
+    cell : 3x3 matrix
+        Unit cell vectors.
+    cellpar : [a, b, c, alpha, beta, gamma]
+        Cell parameters with angles in degree. Is not used when `cell`
+        is given. 
+    ab_normal : vector
+        Is used to define the orientation of the unit cell relative
+        to the Cartesian system when `cell` is not given. It is the
+        normal vector of the plane spanned by a and b.
+    a_direction : vector
+        Defines the orientation of the unit cell a vector. a will be 
+        parallel to the projection of `a_direction` onto the a-b plane.
+    size : 3 positive integers
+        How many times the conventional unit cell should be repeated
+        in each direction.
+    ondublicates : 'keep' | 'replace' | 'warn' | 'error'
+        Action if `basis` contain symmetry-equivalent positions:
+            'keep'    - ignore additional symmetry-equivalent positions
+            'replace' - replace
+            'warn'    - like 'keep', but issue an UserWarning
+            'error'   - raises a SpacegroupValueError
+    symprec : float
+        Minimum "distance" betweed two sites in scaled coordinates
+        before they are counted as the same site.
+    pbc : one or three bools
+        Periodic boundary conditions flags.  Examples: True,
+        False, 0, 1, (1, 1, 0), (True, False, False).  Default
+        is True.
+    primitive_cell : bool
+        Wheter to return the primitive instead of the conventional
+        unit cell.
+
+    Keyword arguments:
+
+    All additional keyword arguments are passed on to the Atoms
+    constructor.  Currently, probably the most useful additional
+    keyword arguments are `info`, `constraint` and `calculator`.
+
+    Examples:
+
+    Two diamond unit cells (space group number 227)
+
+    >>> diamond = crystal('C', [(0,0,0)], spacegroup=227, 
+    ...     cellpar=[3.57, 3.57, 3.57, 90, 90, 90], size=(2,1,1))
+    >>> ase.view(diamond)  # doctest: +SKIP
+
+    A CoSb3 skutterudite unit cell containing 32 atoms
+
+    >>> skutterudite = crystal(('Co', 'Sb'), 
+    ...     basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)], 
+    ...     spacegroup=204, cellpar=[9.04, 9.04, 9.04, 90, 90, 90])
+    >>> len(skutterudite)
+    32
+    """
+    sg = Spacegroup(spacegroup, setting)
+    if (not isinstance(symbols, str) and 
+        hasattr(symbols, '__getitem__') and
+        len(symbols) > 0 and 
+        isinstance(symbols[0], ase.Atom)):
+        symbols = ase.Atoms(symbols)
+    if isinstance(symbols, ase.Atoms):
+        basis = symbols
+        symbols = basis.get_chemical_symbols()
+    if isinstance(basis, ase.Atoms):
+        basis_coords = basis.get_scaled_positions()
+        if cell is None and cellpar is None:
+            cell = basis.cell
+        if symbols is None:
+            symbols = basis.get_chemical_symbols()
+    else:
+        basis_coords = np.array(basis, dtype=float, copy=False, ndmin=2)
+    sites, kinds = sg.equivalent_sites(basis_coords, 
+                                       ondublicates=ondublicates, 
+                                       symprec=symprec)
+    symbols = parse_symbols(symbols)
+    symbols = [symbols[i] for i in kinds]
+    if cell is None:
+        cell = cellpar_to_cell(cellpar, ab_normal, a_direction)
+
+    info = dict(spacegroup=sg)
+    if primitive_cell:
+        info['unit_cell'] = 'primitive'
+    else:
+        info['unit_cell'] = 'conventional'
+
+    if 'info' in kwargs:
+        info.update(kwargs['info'])
+    kwargs['info'] = info
+
+    atoms = ase.Atoms(symbols, 
+                      scaled_positions=sites, 
+                      cell=cell,
+                      pbc=pbc,
+                      **kwargs)
+
+    if isinstance(basis, ase.Atoms):
+        for name in basis.arrays:
+            if not atoms.has(name):
+                array = basis.get_array(name)
+                atoms.new_array(name, [array[i] for i in kinds], 
+                                dtype=array.dtype, shape=array.shape[1:])
+
+    if primitive_cell:
+        from ase.utils.geometry  import cut
+        prim_cell = sg.scaled_primitive_cell
+        atoms = cut(atoms, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2])
+
+    if size != (1, 1, 1):
+        atoms = atoms.repeat(size)
+    return atoms
+
+def parse_symbols(symbols):
+    """Return `sumbols` as a sequence of element symbols."""
+    if isinstance(symbols, basestring):
+        symbols = string2symbols(symbols)
+    return symbols
+
+
+
+
+
+
+#-----------------------------------------------------------------
+# Self test
+if __name__ == '__main__':
+    import doctest
+    print 'doctest: ', doctest.testmod()
+    
diff --git a/ase/lattice/spacegroup/spacegroup.dat b/ase/lattice/spacegroup/spacegroup.dat
new file mode 100644
index 0000000..163e2c1
--- /dev/null
+++ b/ase/lattice/spacegroup/spacegroup.dat
@@ -0,0 +1,6665 @@
+1     P 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  1 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+2     P -1
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  1 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+3     P 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+3     P 2
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+4     P 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+
+4     P 21
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+5     C 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+5     A 2
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+6     P m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+6     P m
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+7     P c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+7     P n
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+
+8     C m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+8     A m
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+9     C c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+9     A n
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+
+10    P 2/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+10    P 2/m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+11    P 21/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+
+11    P 21/m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+12    C 2/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+12    A 2/m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+13    P 2/c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+13    P 2/n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+
+14    P 21/c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+
+14    P 21/n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+15    C 2/c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+15    A 2/n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  2 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+
+16    P 2 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+17    P 2 2 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+18    P 21 21 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+19    P 21 21 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+20    C 2 2 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+21    C 2 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+22    F 2 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+23    I 2 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+24    I 21 21 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+25    P m m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+26    P m c 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+27    P c c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+28    P m a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+
+29    P c a 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+
+30    P n c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+
+31    P m n 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+32    P b a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+33    P n a 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+34    P n n 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+35    C m m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+36    C m c 21
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+37    C c c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+38    A m m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+39    A b m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+
+40    A m a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+
+41    A b a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000 -0.5000000000
+     0.0000000000  0.5000000000  0.5000000000
+     1.0000000000  0.0000000000  0.0000000000
+  reciprocal primitive cell
+      0   1  -1
+      0   1   1
+      1   0   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+42    F m m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+43    F d d 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0  1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+
+44    I m m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+45    I b a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+46    I m a 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+
+47    P m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+48    P n n n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+48    P n n n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+
+49    P c c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+50    P b a n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+50    P b a n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+
+51    P m m a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+
+52    P n n a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+
+53    P m n a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+54    P c c A
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+
+55    P b a m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+56    P c c n
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+
+57    P b c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+
+58    P n n m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+59    P m m n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+59    P m m n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+
+60    P b c n
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+61    P b c a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+62    P n m a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+63    C m c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+64    C m c a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+65    C m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+66    C c c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+67    C m m a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+68    C c c a
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+
+68    C c c a
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.5000000000 -0.5000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1  -1   0
+      1   1   0
+      0   0   1
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+
+69    F m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+70    F d d d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0 -1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0  1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+
+70    F d d d
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.7500000000  0.7500000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.7500000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.7500000000  0.7500000000
+
+71    I m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+72    I b a m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+73    I b c a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+74    I m m a
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+75    P 4
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+76    P 41
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.7500000000
+
+77    P 42
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+78    P 43
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.2500000000
+
+79    I 4
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+80    I 41
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+
+81    P -4
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+82    I -4
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+83    P 4/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+84    P 42/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+85    P 4/n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+85    P 4/n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+
+86    P 42/n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+86    P 42/n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+
+87    I 4/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+88    I 41/a
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+   -1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+88    I 41/a
+  setting 2
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  4 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.7500000000  0.2500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.7500000000  0.7500000000  0.7500000000
+
+89    P 4 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+90    P 4 21 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+91    P 41 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.2500000000
+
+92    P 41 21 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.2500000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.7500000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+93    P 42 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+94    P 42 21 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+95    P 43 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.2500000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.2500000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.7500000000
+
+96    P 43 21 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.2500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.2500000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+97    I 4 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+98    I 41 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+99    P 4 m m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+100   P 4 b n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+101   P 42 c m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+102   P 42 n m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+103   P 4 c c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+104   P 4 n c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+105   P 42 m c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+106   P 42 b c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+107   I 4 m m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+108   I 4 c m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+109   I 41 m d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+
+110   I 41 c d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.7500000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.2500000000
+
+111   P -4 2 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+112   P -4 2 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+113   P -4 21 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+114   P -4 21 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+115   P -4 m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+116   P -4 c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+117   P -4 b 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+118   P -4 n 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+119   I -4 m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+120   I -4 c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+121   I -4 2 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+122   I -4 2 d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+
+123   P 4/m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+124   P 4/m c c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+125   P 4/n b m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+125   P 4/n b m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+126   P 4/n n c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+126   P 4/n n c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+127   P 4/m b m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+128   P 4/m n c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+129   P 4/n m m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+129   P 4/n m m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+130   P 4/n c c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+130   P 4/n c c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+131   P 42/m m c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+132   P 42/m c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+133   P 42/n b c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+133   P 42/n b c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+134   P 42/n n m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+134   P 42/n n m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+
+135   P 42/m b c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+
+136   P 42/m n m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+137   P 42/n m c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+
+137   P 42/n m c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+138   P 42/n c m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+
+138   P 42/n c m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+139   I 4/m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+140   I 4/m c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+141   I 41/a m d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+
+141   I 41/a m d
+  setting 2
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.2500000000  0.7500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.7500000000
+
+142   I 41/a c d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  16 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.2500000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.7500000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.7500000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.2500000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.7500000000
+
+142   I 41/a c d
+  setting 2
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  8 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+
+143   P 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+144   P 31
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+
+145   P 32
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+
+146   R 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+146   R 3
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+147   P -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+148   R -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+148   R -3
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  3 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+149   P 3 1 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+150   P 3 2 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+151   P 31 1 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+152   P 31 2 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+
+153   P 32 1 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+154   P 32 2 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+
+155   R 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+155   R 3 2
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+156   P 3 m 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+157   P 3 1 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+158   P 3 c 1
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+159   P 3 1 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+160   R 3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+160   R 3 m
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+161   R 3 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+161   R 3 c
+  setting 2
+  centrosymmetric 0
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+162   P -3 1 m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+163   P -3 1 c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+164   P -3 m 1
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+165   P -3 c 1
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+166   R -3 m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+166   R -3 m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+167   R -3 c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.6666666667  0.3333333333  0.3333333333
+    -0.3333333333  0.3333333333  0.3333333333
+    -0.3333333333 -0.6666666667  0.3333333333
+  reciprocal primitive cell
+      1   1   0
+     -1   1   1
+      0  -1   1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.6666666667  0.3333333333  0.3333333333
+     0.3333333333  0.6666666667  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+167   R -3 c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.3333333333  0.6666666667 -0.3333333333
+     0.3333333333 -0.3333333333 -0.3333333333
+    -0.6666666667 -0.3333333333 -0.3333333333
+  reciprocal primitive cell
+      1   1   0
+      1  -1  -1
+     -1   0  -1
+  3 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.3333333333  0.6666666667  0.3333333333
+     0.6666666667  0.3333333333  0.6666666667
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+168   P 6
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+169   P 61
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.8333333333
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.1666666667
+
+170   P 65
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.1666666667
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.8333333333
+
+171   P 62
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+
+172   P 64
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+
+173   P 63
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+174   P -6
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+175   P 6/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+176   P 63/m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  6 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+177   P 6 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+178   P 61 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.8333333333
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.1666666667
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.8333333333
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.1666666667
+
+179   P 65 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.1666666667
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.8333333333
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.1666666667
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.8333333333
+
+180   P 62 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+
+181   P 64 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.3333333333
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.6666666667
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.3333333333
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.6666666667
+
+182   P 63 2 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+183   P 6 m m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+184   P 6 c c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+185   P 63 c m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+186   P 63 m c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+187   P -6 m 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+188   P -6 c 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+189   P -6 2 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+
+190   P -6 2 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+
+191   P 6/m m m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+192   P 6/m c c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+193   P 63/m c m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+
+194   P 63/m m c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0    -1  1  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    1 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1 -1  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0    -1  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  1  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     1 -1  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+
+195   P 2 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+196   F 2 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+197   I 2 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+198   P 21 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+
+199   I 21 3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+
+200   P m -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+201   P n -3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0  1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+201   P n -3
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.0000000000
+
+202   F m -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+203   F d -3
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0 -1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0  1  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+    0  0 -1    -1  0  0     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    0  0 -1     1  0  0     0  1  0    0.2500000000  0.2500000000  0.2500000000
+    0  0  1     1  0  0     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    0  0  1    -1  0  0     0  1  0    0.2500000000  0.2500000000  0.2500000000
+    0 -1  0     0  0 -1    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+    0  1  0     0  0 -1     1  0  0    0.2500000000  0.2500000000  0.2500000000
+    0 -1  0     0  0  1     1  0  0    0.2500000000  0.2500000000  0.2500000000
+    0  1  0     0  0  1    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+
+203   F d -3
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.2500000000  0.2500000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.2500000000  0.0000000000  0.2500000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.2500000000  0.2500000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.2500000000  0.2500000000
+    0  0 -1    -1  0  0     0  1  0    0.2500000000  0.2500000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.2500000000  0.0000000000  0.2500000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.2500000000  0.0000000000  0.2500000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.2500000000  0.2500000000
+    0 -1  0     0  0 -1     1  0  0    0.2500000000  0.2500000000  0.0000000000
+
+204   I m -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+205   P a -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+
+206   I a -3
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  12 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+
+207   P 4 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+208   P 42 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+209   F 4 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+210   F 41 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.7500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.7500000000
+   -1  0  0     0  0  1     0  1  0    0.7500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.7500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.7500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+
+211   I 4 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+212   P 43 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.7500000000  0.7500000000  0.2500000000
+    0 -1  0     1  0  0     0  0  1    0.7500000000  0.2500000000  0.7500000000
+    1  0  0     0  0  1     0 -1  0    0.2500000000  0.7500000000  0.7500000000
+   -1  0  0     0  0  1     0  1  0    0.7500000000  0.2500000000  0.7500000000
+   -1  0  0     0  0 -1     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  0 -1     0  1  0    0.7500000000  0.7500000000  0.2500000000
+    0  0  1     0  1  0    -1  0  0    0.2500000000  0.7500000000  0.7500000000
+    0  0  1     0 -1  0     1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0  0 -1     0  1  0     1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  0 -1     0 -1  0    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+
+213   P 41 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0 -1    0.7500000000  0.7500000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.2500000000
+   -1  0  0     0  0  1     0  1  0    0.2500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.7500000000  0.7500000000  0.7500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.2500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.2500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.2500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.2500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.7500000000  0.7500000000  0.7500000000
+
+214   I 41 3 2
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0 -1    0.7500000000  0.7500000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.2500000000
+   -1  0  0     0  0  1     0  1  0    0.2500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.7500000000  0.7500000000  0.7500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.2500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.2500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.2500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.2500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.7500000000  0.7500000000  0.7500000000
+
+215   P -4 3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+216   F -4 3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+217   I -4 3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+218   P -4 3 n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+219   F -4 3 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+220   I -4 3 d
+  setting 1
+  centrosymmetric 0
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.2500000000  0.2500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0  1    0.2500000000  0.7500000000  0.7500000000
+    0  1  0    -1  0  0     0  0 -1    0.7500000000  0.2500000000  0.7500000000
+    0 -1  0     1  0  0     0  0 -1    0.7500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0  1  0    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0  0  1     0 -1  0    0.7500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0  1  0    0.2500000000  0.7500000000  0.7500000000
+    1  0  0     0  0 -1     0 -1  0    0.7500000000  0.2500000000  0.7500000000
+    0  0  1     0  1  0     1  0  0    0.2500000000  0.2500000000  0.2500000000
+    0  0  1     0 -1  0    -1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  0 -1     0  1  0    -1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0     1  0  0    0.2500000000  0.7500000000  0.7500000000
+
+221   P m -3 m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+222   P n -3 n
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  48 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0  1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+222   P n -3 n
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.5000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.5000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+223   P m -3 n
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+224   P n -3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  48 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0  1     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+224   P n -3 m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     1.0000000000  0.0000000000  0.0000000000
+     0.0000000000  1.0000000000  0.0000000000
+     0.0000000000  0.0000000000  1.0000000000
+  reciprocal primitive cell
+      1   0   0
+      0   1   0
+      0   0   1
+  1 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0  1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+225   F m -3 m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+226   F m -3 c
+  setting 1
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0  1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0  1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+227   F d -3 m
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  48 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.7500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.7500000000
+   -1  0  0     0  0  1     0  1  0    0.7500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.7500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.7500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0 -1  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  1  0     0  0 -1    0.2500000000  0.7500000000  0.7500000000
+    1  0  0     0 -1  0     0  0  1    0.7500000000  0.7500000000  0.2500000000
+   -1  0  0     0  1  0     0  0  1    0.7500000000  0.2500000000  0.7500000000
+    0  0 -1    -1  0  0     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    0  0 -1     1  0  0     0  1  0    0.7500000000  0.2500000000  0.7500000000
+    0  0  1     1  0  0     0 -1  0    0.2500000000  0.7500000000  0.7500000000
+    0  0  1    -1  0  0     0  1  0    0.7500000000  0.7500000000  0.2500000000
+    0 -1  0     0  0 -1    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+    0  1  0     0  0 -1     1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0 -1  0     0  0  1     1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  1  0     0  0  1    -1  0  0    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+   -1  0  0     0  0 -1     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1     0 -1  0     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     0  1  0    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  0  1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+227   F d -3 m
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.7500000000  0.2500000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.2500000000  0.5000000000  0.7500000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.7500000000  0.2500000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.7500000000  0.2500000000
+    0  0 -1    -1  0  0     0  1  0    0.7500000000  0.2500000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.2500000000  0.5000000000  0.7500000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.2500000000  0.5000000000  0.7500000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.7500000000  0.2500000000
+    0 -1  0     0  0 -1     1  0  0    0.7500000000  0.2500000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.5000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.5000000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.5000000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.5000000000
+   -1  0  0     0  0  1     0  1  0    0.5000000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.5000000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.5000000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.5000000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.5000000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+228   F d -3 c
+  setting 1
+  centrosymmetric 0
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  48 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.5000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.0000000000  0.5000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.5000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.7500000000
+    0 -1  0    -1  0  0     0  0 -1    0.2500000000  0.2500000000  0.2500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.7500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.7500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.7500000000
+   -1  0  0     0  0  1     0  1  0    0.7500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.2500000000  0.2500000000  0.2500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.7500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.7500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.7500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.7500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.2500000000  0.2500000000  0.2500000000
+   -1  0  0     0 -1  0     0  0 -1    0.7500000000  0.7500000000  0.7500000000
+    1  0  0     0  1  0     0  0 -1    0.7500000000  0.2500000000  0.2500000000
+    1  0  0     0 -1  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+   -1  0  0     0  1  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    0  0 -1    -1  0  0     0 -1  0    0.7500000000  0.7500000000  0.7500000000
+    0  0 -1     1  0  0     0  1  0    0.2500000000  0.7500000000  0.2500000000
+    0  0  1     1  0  0     0 -1  0    0.7500000000  0.2500000000  0.2500000000
+    0  0  1    -1  0  0     0  1  0    0.2500000000  0.2500000000  0.7500000000
+    0 -1  0     0  0 -1    -1  0  0    0.7500000000  0.7500000000  0.7500000000
+    0  1  0     0  0 -1     1  0  0    0.2500000000  0.2500000000  0.7500000000
+    0 -1  0     0  0  1     1  0  0    0.2500000000  0.7500000000  0.2500000000
+    0  1  0     0  0  1    -1  0  0    0.7500000000  0.2500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0  1    0.0000000000  0.5000000000  0.0000000000
+    0  1  0     1  0  0     0  0  1    0.5000000000  0.5000000000  0.5000000000
+    0 -1  0     1  0  0     0  0 -1    0.5000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.5000000000
+   -1  0  0     0  0 -1     0  1  0    0.0000000000  0.5000000000  0.0000000000
+    1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.5000000000
+    1  0  0     0  0  1     0  1  0    0.5000000000  0.5000000000  0.5000000000
+   -1  0  0     0  0  1     0 -1  0    0.5000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0     1  0  0    0.0000000000  0.5000000000  0.0000000000
+    0  0 -1     0  1  0    -1  0  0    0.5000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.5000000000
+    0  0  1     0  1  0     1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+228   F d -3 c
+  setting 2
+  centrosymmetric 1
+  primitive cell
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  reciprocal primitive cell
+     -1   1   1
+      1  -1   1
+      1   1  -1
+  4 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.0000000000  0.5000000000  0.5000000000
+     0.5000000000  0.0000000000  0.5000000000
+     0.5000000000  0.5000000000  0.0000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.2500000000  0.7500000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.7500000000  0.5000000000  0.2500000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.2500000000  0.7500000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.2500000000  0.7500000000
+    0  0 -1    -1  0  0     0  1  0    0.2500000000  0.7500000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.7500000000  0.5000000000  0.2500000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.7500000000  0.5000000000  0.2500000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.2500000000  0.7500000000
+    0 -1  0     0  0 -1     1  0  0    0.2500000000  0.7500000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.5000000000  0.5000000000  0.5000000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.0000000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.5000000000  0.5000000000  0.5000000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.0000000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.0000000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.5000000000  0.5000000000  0.5000000000
+
+229   I m -3 m
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0 -1  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     0  0 -1    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  1  0     1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0    -1  0  0     0  0 -1    0.0000000000  0.0000000000  0.0000000000
+    0  1  0    -1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     1  0  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0  1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0  1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0  0 -1     0 -1  0    0.0000000000  0.0000000000  0.0000000000
+    1  0  0     0  0 -1     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0  1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1     0 -1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0  1  0     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0  0 -1     0 -1  0    -1  0  0    0.0000000000  0.0000000000  0.0000000000
+
+230   I a -3 d
+  setting 1
+  centrosymmetric 1
+  primitive cell
+    -0.5000000000  0.5000000000  0.5000000000
+     0.5000000000 -0.5000000000  0.5000000000
+     0.5000000000  0.5000000000 -0.5000000000
+  reciprocal primitive cell
+      0   1   1
+      1   0   1
+      1   1   0
+  2 subtranslations
+     0.0000000000  0.0000000000  0.0000000000
+     0.5000000000  0.5000000000  0.5000000000
+  24 symmetry operations (rot+trans)
+    1  0  0     0  1  0     0  0  1    0.0000000000  0.0000000000  0.0000000000
+   -1  0  0     0 -1  0     0  0  1    0.5000000000  0.0000000000  0.5000000000
+   -1  0  0     0  1  0     0  0 -1    0.0000000000  0.5000000000  0.5000000000
+    1  0  0     0 -1  0     0  0 -1    0.5000000000  0.5000000000  0.0000000000
+    0  0  1     1  0  0     0  1  0    0.0000000000  0.0000000000  0.0000000000
+    0  0  1    -1  0  0     0 -1  0    0.5000000000  0.5000000000  0.0000000000
+    0  0 -1    -1  0  0     0  1  0    0.5000000000  0.0000000000  0.5000000000
+    0  0 -1     1  0  0     0 -1  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0  1     1  0  0    0.0000000000  0.0000000000  0.0000000000
+    0 -1  0     0  0  1    -1  0  0    0.0000000000  0.5000000000  0.5000000000
+    0  1  0     0  0 -1    -1  0  0    0.5000000000  0.5000000000  0.0000000000
+    0 -1  0     0  0 -1     1  0  0    0.5000000000  0.0000000000  0.5000000000
+    0  1  0     1  0  0     0  0 -1    0.7500000000  0.2500000000  0.2500000000
+    0 -1  0    -1  0  0     0  0 -1    0.7500000000  0.7500000000  0.7500000000
+    0  1  0    -1  0  0     0  0  1    0.2500000000  0.2500000000  0.7500000000
+    0 -1  0     1  0  0     0  0  1    0.2500000000  0.7500000000  0.2500000000
+    1  0  0     0  0  1     0 -1  0    0.7500000000  0.2500000000  0.2500000000
+   -1  0  0     0  0  1     0  1  0    0.2500000000  0.7500000000  0.2500000000
+   -1  0  0     0  0 -1     0 -1  0    0.7500000000  0.7500000000  0.7500000000
+    1  0  0     0  0 -1     0  1  0    0.2500000000  0.2500000000  0.7500000000
+    0  0  1     0  1  0    -1  0  0    0.7500000000  0.2500000000  0.2500000000
+    0  0  1     0 -1  0     1  0  0    0.2500000000  0.2500000000  0.7500000000
+    0  0 -1     0  1  0     1  0  0    0.2500000000  0.7500000000  0.2500000000
+    0  0 -1     0 -1  0    -1  0  0    0.7500000000  0.7500000000  0.7500000000
+
diff --git a/ase/lattice/spacegroup/spacegroup.py b/ase/lattice/spacegroup/spacegroup.py
new file mode 100644
index 0000000..45982ef
--- /dev/null
+++ b/ase/lattice/spacegroup/spacegroup.py
@@ -0,0 +1,710 @@
+# Copyright (C) 2010, Jesper Friis
+# (see accompanying license files for details).
+
+"""Definition of the Spacegroup class.
+
+This module only depends on NumPy and the space group database.
+"""
+
+import os
+import warnings
+
+import numpy as np
+
+__all__ = ['Spacegroup']
+
+
+class SpacegroupError(Exception):
+    """Base exception for the spacegroup module."""
+    pass
+
+class SpacegroupNotFoundError(SpacegroupError):
+    """Raised when given space group cannot be found in data base."""
+    pass
+
+class SpacegroupValueError(SpacegroupError):
+    """Raised when arguments have invalid value."""
+    pass
+
+
+class Spacegroup(object):
+    """A space group class. 
+    
+    The instances of Spacegroup describes the symmetry operations for
+    the given space group. 
+
+    Example:
+    
+    >>> from ase.lattice.spacegroup import Spacegroup
+    >>> 
+    >>> sg = Spacegroup(225)
+    >>> print 'Space group', sg.no, sg.symbol
+    Space group 225 F m -3 m
+    >>> sg.scaled_primitive_cell
+    array([[ 0. ,  0.5,  0.5],
+           [ 0.5,  0. ,  0.5],
+           [ 0.5,  0.5,  0. ]])
+    >>> sites, kinds = sg.equivalent_sites([[0,0,0]])
+    >>> sites
+    array([[ 0. ,  0. ,  0. ],
+           [ 0. ,  0.5,  0.5],
+           [ 0.5,  0. ,  0.5],
+           [ 0.5,  0.5,  0. ]])
+    """
+    no = property(
+        lambda self: self._no,
+        doc='Space group number in International Tables of Crystallography.')
+    symbol = property(
+        lambda self: self._symbol, 
+        doc='Hermann-Mauguin (or international) symbol for the space group.')
+    setting = property(
+        lambda self: self._setting, 
+        doc='Space group setting. Either one or two.')
+    lattice = property(
+        lambda self: self._symbol[0], 
+        doc="""Lattice type: 
+            P      primitive
+            I      body centering, h+k+l=2n
+            F      face centering, h,k,l all odd or even
+            A,B,C  single face centering, k+l=2n, h+l=2n, h+k=2n
+            R      rhombohedral centering, -h+k+l=3n (obverse); h-k+l=3n (reverse)
+            """)
+    centrosymmetric = property(
+        lambda self: self._centrosymmetric, 
+        doc='Whether a center of symmetry exists.')
+    scaled_primitive_cell = property(
+        lambda self: self._scaled_primitive_cell, 
+        doc='Primitive cell in scaled coordinates as a matrix with the '
+        'primitive vectors along the rows.')
+    reciprocal_cell = property(
+        lambda self: self._reciprocal_cell, 
+        doc='Tree Miller indices that span all kinematically non-forbidden '
+        'reflections as a matrix with the Miller indices along the rows.')
+    nsubtrans = property(
+        lambda self: len(self._subtrans), 
+        doc='Number of cell-subtranslation vectors.')
+    def _get_nsymop(self):
+        """Returns total number of symmetry operations."""
+        if self.centrosymmetric:
+            return 2 * len(self._rotations) * len(self._subtrans)
+        else:
+            return len(self._rotations) * len(self._subtrans)
+    nsymop = property(_get_nsymop, doc='Total number of symmetry operations.')
+    subtrans = property(
+        lambda self: self._subtrans, 
+        doc='Translations vectors belonging to cell-sub-translations.')
+    rotations = property(
+        lambda self: self._rotations, 
+        doc='Symmetry rotation matrices. The invertions are not included '
+        'for centrosymmetrical crystals.')
+    translations = property(
+        lambda self: self._translations, 
+        doc='Symmetry translations. The invertions are not included '
+        'for centrosymmetrical crystals.')
+
+    def __init__(self, spacegroup, setting=1, datafile=None):
+        """Returns a new Spacegroup instance. 
+
+        Parameters:
+
+        spacegroup : int | string | Spacegroup instance
+            The space group number in International Tables of
+            Crystallography or its Hermann-Mauguin symbol. E.g. 
+            spacegroup=225 and spacegroup='F m -3 m' are equivalent.
+        setting : 1 | 2
+            Some space groups have more than one setting. `setting`
+            determines Which of these should be used.
+        datafile : None | string
+            Path to database file. If `None`, the the default database 
+            will be used.
+        """
+        if isinstance(spacegroup, Spacegroup):
+            for k, v in spacegroup.__dict__.iteritems():
+                setattr(self, k, v)
+            return 
+        if not datafile:
+            datafile = get_datafile()
+        f = open(datafile, 'r')
+        try:
+            _read_datafile(self, spacegroup, setting, f)
+        finally:
+            f.close()
+
+    def __repr__(self):
+        return 'Spacegroup(%d, setting=%d)' % (self.no, self.setting)
+    
+    def __str__(self):
+        """Return a string representation of the space group data in
+        the same format as found the database."""
+        retval = []
+        # no, symbol
+        retval.append('%-3d   %s\n' % (self.no, self.symbol))
+        # setting
+        retval.append('  setting %d\n' % (self.setting))
+        # centrosymmetric
+        retval.append('  centrosymmetric %d\n' % (self.centrosymmetric))
+        # primitive vectors
+        retval.append('  primitive vectors\n')
+        for i in range(3):
+            retval.append('   ')
+            for j in range(3):
+                retval.append(' %13.10f' % (self.scaled_primitive_cell[i, j]))
+            retval.append('\n')
+        # primitive reciprocal vectors
+        retval.append('  reciprocal vectors\n')
+        for i in range(3):
+            retval.append('   ')
+            for j in range(3):
+                retval.append(' %3d' % (self.reciprocal_cell[i, j]))
+            retval.append('\n')
+        # sublattice
+        retval.append('  %d subtranslations\n' % self.nsubtrans)
+        for i in range(self.nsubtrans):
+            retval.append('   ')
+            for j in range(3):
+                retval.append(' %13.10f' % (self.subtrans[i, j]))
+            retval.append('\n')
+        # symmetry operations
+        nrot = len(self.rotations)
+        retval.append('  %d symmetry operations (rot+trans)\n' % nrot)
+        for i in range(nrot):
+            retval.append(' ')
+            for j in range(3):
+                retval.append(' ')
+                for k in range(3):
+                    retval.append(' %2d' % (self.rotations[i, j, k]))
+                retval.append('  ')
+            for j in range(3):
+                retval.append(' %13.10f' % self.translations[i, j])
+            retval.append('\n')
+        retval.append('\n')
+        return ''.join(retval)
+
+    def __eq__(self, other):
+        """Chech whether *self* and *other* refer to the same
+        spacegroup number and setting."""
+        if not isinstance(other, Spacegroup):
+            other = Spacegroup(other)
+        return self.no == other.no and self.setting == other.setting
+
+    def __index__(self):
+        return self.no
+
+    def get_symop(self):
+        """Returns all symmetry operations (including inversions and
+        subtranslations) as a sequence of (rotation, translation)
+        tuples."""
+        symop = []
+        parities = [1]
+        if self.centrosymmetric:
+            parities.append(-1)
+        for parity in parities:
+            for subtrans in self.subtrans:
+                for rot, trans in zip(self.rotations, self.translations):
+                    newtrans = np.mod(trans + subtrans, 1)
+                    symop.append((parity*rot, newtrans))
+        return symop
+
+    def get_op(self):
+        """Returns all symmetry operations (including inversions and
+        subtranslations), but unlike get_symop(), they are returned as
+        two ndarrays."""
+        if self.centrosymmetric:
+            rot = np.tile(np.vstack((self.rotations, -self.rotations)), 
+                          (self.nsubtrans, 1, 1))
+            trans = np.repeat(self.subtrans, 2*len(self.rotations), axis=0)
+        else:
+            rot = np.tile(self.rotations, (self.nsubtrans, 1, 1))
+            trans = np.repeat(self.subtrans, len(self.rotations), axis=0)
+        return rot, trans
+
+    def get_rotations(self):
+        """Return all rotations, including inversions for
+        centrosymmetric crystals."""
+        if self.centrosymmetric:
+            return np.vstack((self.rotations, -self.rotations))
+        else:
+            return self.rotations
+
+    def equivalent_reflections(self, hkl):
+        """Return all equivalent reflections to the list of Miller indices
+        in hkl.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.equivalent_reflections([[0, 0, 2]])
+        array([[ 0,  0, -2],
+               [ 0, -2,  0],
+               [-2,  0,  0],
+               [ 2,  0,  0],
+               [ 0,  2,  0],
+               [ 0,  0,  2]])
+        """
+        hkl = np.array(hkl, dtype='int', ndmin=2)
+        rot = self.get_rotations()
+        n, nrot = len(hkl), len(rot)
+        R = rot.transpose(0, 2, 1).reshape((3*nrot, 3)).T
+        refl = np.dot(hkl, R).reshape((n*nrot, 3))
+        ind = np.lexsort(refl.T)
+        refl = refl[ind]
+        diff = np.diff(refl, axis=0)
+        mask = np.any(diff, axis=1)
+        return np.vstack((refl[mask], refl[-1,:]))
+
+    def symmetry_normalised_reflections(self, hkl):
+        """Returns an array of same size as *hkl*, containing the
+        corresponding symmetry-equivalent reflections of lowest
+        indices.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.symmetry_normalised_reflections([[2, 0, 0], [0, 2, 0]])
+        array([[ 0,  0, -2],
+               [ 0,  0, -2]])
+        """
+        hkl = np.array(hkl, dtype=int, ndmin=2)
+        normalised = np.empty(hkl.shape, int)
+        R = self.get_rotations().transpose(0, 2, 1)
+        for i, g in enumerate(hkl):
+            gsym = np.dot(R, g)
+            j = np.lexsort(gsym.T)[0]
+            normalised[i,:] = gsym[j]
+        return normalised
+
+    def unique_reflections(self, hkl):
+        """Returns a subset *hkl* containing only the symmetry-unique
+        reflections.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.unique_reflections([[ 2,  0,  0], 
+        ...                        [ 0, -2,  0], 
+        ...                        [ 2,  2,  0], 
+        ...                        [ 0, -2, -2]])
+        array([[2, 0, 0],
+               [2, 2, 0]])
+        """
+        hkl = np.array(hkl, dtype=int, ndmin=2)
+        hklnorm = self.symmetry_normalised_reflections(hkl)
+        perm = np.lexsort(hklnorm.T)
+        iperm = perm.argsort()
+        xmask = np.abs(np.diff(hklnorm[perm], axis=0)).any(axis=1)
+        mask = np.concatenate(([True], xmask))
+        imask = mask[iperm]
+        return hkl[imask]
+            
+    def equivalent_sites(self, scaled_positions, ondublicates='error', 
+                         symprec=1e-3):
+        """Returns the scaled positions and all their equivalent sites.
+
+        Parameters:
+
+        scaled_positions: list | array
+            List of non-equivalent sites given in unit cell coordinates.
+        ondublicates : 'keep' | 'replace' | 'warn' | 'error'
+            Action if `scaled_positions` contain symmetry-equivalent
+            positions:
+            
+            'keep'
+               ignore additional symmetry-equivalent positions
+            'replace'
+                replace
+            'warn'
+                like 'keep', but issue an UserWarning
+            'error'
+                raises a SpacegroupValueError
+                    
+        symprec: float
+            Minimum "distance" betweed two sites in scaled coordinates
+            before they are counted as the same site.
+
+        Returns:
+
+        sites: array
+            A NumPy array of equivalent sites.
+        kinds: list
+            A list of integer indices specifying which input site is 
+            equivalent to the corresponding returned site.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sites, kinds = sg.equivalent_sites([[0, 0, 0], [0.5, 0.0, 0.0]])
+        >>> sites
+        array([[ 0. ,  0. ,  0. ],
+               [ 0. ,  0.5,  0.5],
+               [ 0.5,  0. ,  0.5],
+               [ 0.5,  0.5,  0. ],
+               [ 0.5,  0. ,  0. ],
+               [ 0. ,  0.5,  0. ],
+               [ 0. ,  0. ,  0.5],
+               [ 0.5,  0.5,  0.5]])
+        >>> kinds
+        [0, 0, 0, 0, 1, 1, 1, 1]
+        """
+        kinds = []
+        sites = []
+        symprec2 = symprec**2
+        scaled = np.array(scaled_positions, ndmin=2)
+        for kind, pos in enumerate(scaled):
+            for rot, trans in self.get_symop():
+                site = np.mod(np.dot(rot, pos) + trans, 1.)
+                if not sites:
+                    sites.append(site)
+                    kinds.append(kind)
+                    continue
+                t = site - sites
+                mask = np.sum(t*t, 1) < symprec2
+                if np.any(mask):
+                    ind = np.argwhere(mask)[0][0]
+                    if kinds[ind] == kind:
+                        pass
+                    elif ondublicates == 'keep':
+                        pass
+                    elif ondublicates == 'replace':
+                        kinds[ind] = kind
+                    elif ondublicates == 'warn':
+                        warnings.warn('scaled_positions %d and %d '
+                                      'are equivalent'%(kinds[ind], kind))
+                    elif ondublicates == 'error':
+                        raise SpacegroupValueError(
+                            'scaled_positions %d and %d are equivalent'%(
+                                kinds[ind], kind))
+                    else:
+                        raise SpacegroupValueError(
+                            'Argument "ondublicates" must be one of: '
+                            '"keep", "replace", "warn" or "error".')
+                else:
+                    sites.append(site)
+                    kinds.append(kind)
+        return np.array(sites), kinds
+
+    def symmetry_normalised_sites(self, scaled_positions):
+        """Returns an array of same size as *scaled_positions*,
+        containing the corresponding symmetry-equivalent sites within
+        the unit cell of lowest indices.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.symmetry_normalised_sites([[0.0, 0.5, 0.5], [1.0, 1.0, 0.0]])
+        array([[ 0.,  0.,  0.],
+               [ 0.,  0.,  0.]])
+        """
+        scaled = np.array(scaled_positions, ndmin=2)
+        normalised = np.empty(scaled.shape, np.float)
+        rot, trans = self.get_op()
+        for i, pos in enumerate(scaled):
+            sympos = np.dot(rot, pos) + trans
+            # Must be done twice, see the scaled_positions.py test
+            sympos %= 1.0
+            sympos %= 1.0
+            j = np.lexsort(sympos.T)[0]
+            normalised[i,:] = sympos[j]
+        return normalised
+
+    def unique_sites(self, scaled_positions, symprec=1e-3, output_mask=False):
+        """Returns a subset of *scaled_positions* containing only the
+        symmetry-unique positions.  If *output_mask* is True, a boolean
+        array masking the subset is also returned.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.unique_sites([[0.0, 0.0, 0.0], 
+        ...                  [0.5, 0.5, 0.0], 
+        ...                  [1.0, 0.0, 0.0], 
+        ...                  [0.5, 0.0, 0.0]])
+        array([[ 0. ,  0. ,  0. ],
+               [ 0.5,  0. ,  0. ]])
+        """
+        scaled = np.array(scaled_positions, ndmin=2)
+        symnorm = self.symmetry_normalised_sites(scaled)
+        perm = np.lexsort(symnorm.T)
+        iperm = perm.argsort()
+        xmask = np.abs(np.diff(symnorm[perm], axis=0)).max(axis=1) > symprec
+        mask = np.concatenate(([True], xmask))
+        imask = mask[iperm]
+        if output_mask:
+            return scaled[imask], imask
+        else:
+            return scaled[imask]
+
+    def tag_sites(self, scaled_positions, symprec=1e-3):
+        """Returns an integer array of the same length as *scaled_positions*, 
+        tagging all equivalent atoms with the same index.
+
+        Example:
+
+        >>> from ase.lattice.spacegroup import Spacegroup
+        >>> sg = Spacegroup(225)  # fcc
+        >>> sg.tag_sites([[0.0, 0.0, 0.0], 
+        ...               [0.5, 0.5, 0.0], 
+        ...               [1.0, 0.0, 0.0], 
+        ...               [0.5, 0.0, 0.0]])
+        array([0, 0, 0, 1])
+        """
+        scaled = np.array(scaled_positions, ndmin=2)
+        scaled %= 1.0
+        scaled %= 1.0
+        tags = -np.ones((len(scaled), ), dtype=int)
+        mask = np.ones((len(scaled), ), dtype=np.bool)
+        rot, trans = self.get_op()
+        i = 0
+        while mask.any():
+            pos = scaled[mask][0]
+            sympos = np.dot(rot, pos) + trans
+            # Must be done twice, see the scaled_positions.py test
+            sympos %= 1.0
+            sympos %= 1.0
+            m = ~np.all(np.any(np.abs(scaled[np.newaxis,:,:] - 
+                                      sympos[:,np.newaxis,:]) > symprec, 
+                               axis=2), axis=0)
+            assert not np.any((~mask) & m)
+            tags[m] = i
+            mask &= ~m
+            i += 1
+        return tags
+    
+
+def get_datafile():
+    """Return default path to datafile."""
+    return os.path.join(os.path.dirname(__file__), 'spacegroup.dat')
+
+
+def format_symbol(symbol):
+    """Returns well formatted Hermann-Mauguin symbol as extected by
+    the database, by correcting the case and adding missing or
+    removing dublicated spaces."""
+    fixed = []
+    s = symbol.strip()
+    s = s[0].upper() + s[1:].lower()
+    for c in s:
+        if c.isalpha():
+            fixed.append(' ' + c + ' ')
+        elif c.isspace():
+            fixed.append(' ')
+        elif c.isdigit():
+            fixed.append(c)
+        elif c == '-':
+            fixed.append(' ' + c)
+        elif c == '/':
+            fixed.append(' ' + c)
+    s = ''.join(fixed).strip()
+    return ' '.join(s.split())
+
+
+#-----------------------------------------------------------------
+# Functions for parsing the database. They are moved outside the
+# Spacegroup class in order to make it easier to later implement
+# caching to avoid reading the database each time a new Spacegroup
+# instance is created.
+#-----------------------------------------------------------------
+
+def _skip_to_blank(f, spacegroup, setting):
+    """Read lines from f until a blank line is encountered."""
+    while True:
+        line = f.readline()
+        if not line:
+            raise SpacegroupNotFoundError(
+                'invalid spacegroup %s, setting %i not found in data base' % 
+                ( spacegroup, setting ) )
+        if not line.strip():
+            break
+
+
+def _skip_to_nonblank(f, spacegroup, setting):
+    """Read lines from f until a nonblank line not starting with a
+    hash (#) is encountered and returns this and the next line."""
+    while True:
+        line1 = f.readline()
+        if not line1:
+            raise SpacegroupNotFoundError(
+                'invalid spacegroup %s, setting %i not found in data base' %
+                ( spacegroup, setting ) )
+        line1.strip()
+        if line1 and not line1.startswith('#'):
+            line2 = f.readline()
+            break
+    return line1, line2
+
+
+def _read_datafile_entry(spg, no, symbol, setting, f):
+    """Read space group data from f to spg."""
+    spg._no = no
+    spg._symbol = symbol.strip()
+    spg._setting = setting
+    spg._centrosymmetric = bool(int(f.readline().split()[1]))
+    # primitive vectors
+    f.readline()
+    spg._scaled_primitive_cell = np.array([map(float, f.readline().split()) 
+                                           for i in range(3)], 
+                                          dtype=np.float)
+    # primitive reciprocal vectors
+    f.readline()
+    spg._reciprocal_cell = np.array([map(int, f.readline().split()) 
+                                        for i in range(3)],
+                                       dtype=np.int)
+    # subtranslations
+    spg._nsubtrans = int(f.readline().split()[0])
+    spg._subtrans = np.array([map(float, f.readline().split()) 
+                              for i in range(spg._nsubtrans)],
+                             dtype=np.float)
+    # symmetry operations
+    nsym = int(f.readline().split()[0])
+    symop = np.array([map(float, f.readline().split()) for i in range(nsym)],
+                     dtype=np.float)
+    spg._nsymop = nsym
+    spg._rotations = np.array(symop[:,:9].reshape((nsym,3,3)), dtype=np.int)
+    spg._translations = symop[:,9:]
+
+
+def _read_datafile(spg, spacegroup, setting, f):
+    if isinstance(spacegroup, int):
+        pass
+    elif isinstance(spacegroup, basestring):
+        #spacegroup = ' '.join(spacegroup.strip().split())
+        spacegroup = format_symbol(spacegroup)
+    else:
+        raise SpacegroupValueError('`spacegroup` must be of type int or str')
+    while True:
+        line1, line2 = _skip_to_nonblank(f, spacegroup, setting)
+        _no,_symbol = line1.strip().split(None, 1)
+        _symbol = format_symbol(_symbol)
+        _setting = int(line2.strip().split()[1])
+        _no = int(_no)
+        if ((isinstance(spacegroup, int) and _no == spacegroup) or
+            (isinstance(spacegroup, basestring) and 
+             _symbol == spacegroup)) and _setting == setting:
+            _read_datafile_entry(spg, _no, _symbol, _setting, f)
+            break
+        else:
+            _skip_to_blank(f, spacegroup, setting)
+                
+
+def parse_sitesym(symlist, sep=','):
+    """Parses a sequence of site symmetries in the form used by
+    International Tables and returns corresponding rotation and
+    translation arrays.
+
+    Example:
+
+    >>> symlist = [
+    ...     'x,y,z',
+    ...     '-y+1/2,x+1/2,z',
+    ...     '-y,-x,-z',
+    ... ]
+    >>> rot, trans = parse_sitesym(symlist)
+    >>> rot
+    array([[[ 1,  0,  0],
+            [ 0,  1,  0],
+            [ 0,  0,  1]],
+    <BLANKLINE>
+           [[ 0, -1,  0],
+            [ 1,  0,  0],
+            [ 0,  0,  1]],
+    <BLANKLINE>
+           [[ 0, -1,  0],
+            [-1,  0,  0],
+            [ 0,  0, -1]]])
+    >>> trans
+    array([[ 0. ,  0. ,  0. ],
+           [ 0.5,  0.5,  0. ],
+           [ 0. ,  0. ,  0. ]])
+    """
+    nsym = len(symlist)
+    rot = np.zeros((nsym, 3, 3), dtype='int')
+    trans = np.zeros((nsym, 3))
+    for i, sym in enumerate(symlist):
+        for j, s in enumerate (sym.split(sep)):
+            s = s.lower().strip()
+            while s:
+                sign = 1
+                if s[0] in '+-':
+                    if s[0] == '-':
+                        sign = -1
+                    s = s[1:]
+                if s[0] in 'xyz':
+                    k = ord(s[0]) - ord('x')
+                    rot[i, j, k] = sign
+                    s = s[1:]
+                elif s[0].isdigit() or s[0] == '.':
+                    n = 0
+                    while n < len(s) and (s[n].isdigit() or s[n] in '/.'):
+                        n += 1
+                    t = s[:n]
+                    s = s[n:]
+                    if '/' in t:
+                        q, r = t.split('/')
+                        trans[i,j] = float(q)/float(r)
+                    else:
+                        trans[i,j] = float(t)
+                else:
+                    raise SpacegroupValueError(
+                        'Error parsing %r. Invalid site symmetry: %s' % 
+                        (s, sym))
+    return rot, trans
+                
+
+def spacegroup_from_data(no=None, symbol=None, setting=1, 
+                         centrosymmetric=None, scaled_primitive_cell=None, 
+                         reciprocal_cell=None, subtrans=None, sitesym=None, 
+                         rotations=None, translations=None, datafile=None):
+    """Manually create a new space group instance.  This might be
+    usefull when reading crystal data with its own spacegroup
+    definitions."""
+    if no is not None:
+        spg = Spacegroup(no, setting, datafile)
+    elif symbol is not None:
+        spg = Spacegroup(symbol, setting, datafile)
+    else:
+        raise SpacegroupValueError('either *no* or *symbol* must be given')
+
+    have_sym = False
+    if centrosymmetric is not None:
+        spg._centrosymmetric = bool(centrosymmetric)
+    if scaled_primitive_cell is not None:
+        spg._scaled_primitive_cell = np.array(scaled_primitive_cell)
+    if reciprocal_cell is not None:
+        spg._reciprocal_cell = np.array(reciprocal_cell)
+    if subtrans is not None:
+        spg._subtrans = np.atleast_2d(subtrans)
+        spg._nsubtrans = spg._subtrans.shape[0]
+    if sitesym is not None:
+        spg._rotations, spg._translations = parse_sitesym(sitesym)
+        have_sym = True
+    if rotations is not None:
+        spg._rotations = np.atleast_3d(rotations)
+        have_sym = True
+    if translations is not None:
+        spg._translations = np.atleast_2d(translations)
+        have_sym = True
+    if have_sym:
+        if spg._rotations.shape[0] != spg._translations.shape[0]:
+            raise SpacegroupValueError('inconsistent number of rotations and '
+                                       'translations')
+        spg._nsymop = spg._rotations.shape[0]
+    return spg
+
+
+ 
+
+#-----------------------------------------------------------------
+# Self test
+if __name__ == '__main__':
+
+    # Import spacegroup in order to ensure that __file__ is defined
+    # such that the data base can be found.
+    import spacegroup
+
+    import doctest
+    print 'doctest: ', doctest.testmod()
diff --git a/ase/lattice/surface.py b/ase/lattice/surface.py
new file mode 100644
index 0000000..ddb3a1d
--- /dev/null
+++ b/ase/lattice/surface.py
@@ -0,0 +1,342 @@
+"""Helper functions for creating the most common surfaces and related tasks.
+
+The helper functions can create the most common low-index surfaces,
+add vacuum layers and add adsorbates.
+
+"""
+
+from math import sqrt
+
+import numpy as np
+
+from ase.atom import Atom
+from ase.atoms import Atoms
+from ase.data import reference_states, atomic_numbers
+from ase.lattice.general_surface import surface
+
+
+def fcc100(symbol, size, a=None, vacuum=None):
+    """FCC(100) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'bridge', 'hollow'."""
+    return _surface(symbol, 'fcc', '100', size, a, None, vacuum)
+
+def fcc110(symbol, size, a=None, vacuum=None):
+    """FCC(110) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'longbridge',
+    'shortbridge','hollow'."""
+    return _surface(symbol, 'fcc', '110', size, a, None, vacuum)
+
+def bcc100(symbol, size, a=None, vacuum=None):
+    """BCC(100) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'bridge', 'hollow'."""
+    return _surface(symbol, 'bcc', '100', size, a, None, vacuum)
+
+def bcc110(symbol, size, a=None, vacuum=None, orthogonal=False):
+    """BCC(110) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'longbridge',
+    'shortbridge', 'hollow'.
+ 
+    Use *orthogonal=True* to get an orthogonal unit cell - works only
+    for size=(i,j,k) with j even."""
+    return _surface(symbol, 'bcc', '110', size, a, None, vacuum, orthogonal)
+
+def bcc111(symbol, size, a=None, vacuum=None, orthogonal=False):
+    """BCC(111) surface.
+ 
+    Supported special adsorption sites: 'ontop'.
+ 
+    Use *orthogonal=True* to get an orthogonal unit cell - works only
+    for size=(i,j,k) with j even."""
+    return _surface(symbol, 'bcc', '111', size, a, None, vacuum, orthogonal)
+
+def fcc111(symbol, size, a=None, vacuum=None, orthogonal=False):
+    """FCC(111) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'bridge', 'fcc' and 'hcp'.
+ 
+    Use *orthogonal=True* to get an orthogonal unit cell - works only
+    for size=(i,j,k) with j even."""
+    return _surface(symbol, 'fcc', '111', size, a, None, vacuum, orthogonal)
+
+def hcp0001(symbol, size, a=None, c=None, vacuum=None, orthogonal=False):
+    """HCP(0001) surface.
+ 
+    Supported special adsorption sites: 'ontop', 'bridge', 'fcc' and 'hcp'.
+ 
+    Use *orthogonal=True* to get an orthogonal unit cell - works only
+    for size=(i,j,k) with j even."""
+    return _surface(symbol, 'hcp', '0001', size, a, c, vacuum, orthogonal)
+
+    
+def hcp10m10(symbol, size, a=None, c=None, vacuum=None):
+    """HCP(10m10) surface.
+    
+    Supported special adsorption sites: 'ontop'.
+    
+    Works only for size=(i,j,k) with j even."""
+    return _surface(symbol, 'hcp', '10m10', size, a, c, vacuum)
+
+def diamond100(symbol, size, a=None, vacuum=None):
+    """DIAMOND(100) surface.
+
+    Supported special adsorption sites: 'ontop'."""
+    return _surface(symbol, 'diamond', '100', size, a, None, vacuum)
+
+def diamond111(symbol, size, a=None, vacuum=None, orthogonal=False):
+    """DIAMOND(111) surface.
+ 
+    Supported special adsorption sites: 'ontop'."""
+
+    if orthogonal:
+        raise NotImplementedError("Can't do orthogonal cell yet!")
+    return _surface(symbol, 'diamond', '111', size, a, None, vacuum, orthogonal)
+
+    
+def add_adsorbate(slab, adsorbate, height, position=(0, 0), offset=None,
+                  mol_index=0):
+    """Add an adsorbate to a surface.
+
+    This function adds an adsorbate to a slab.  If the slab is
+    produced by one of the utility functions in ase.lattice.surface, it
+    is possible to specify the position of the adsorbate by a keyword
+    (the supported keywords depend on which function was used to
+    create the slab).
+
+    If the adsorbate is a molecule, the atom indexed by the mol_index
+    optional argument is positioned on top of the adsorption position
+    on the surface, and it is the responsibility of the user to orient
+    the adsorbate in a sensible way.
+
+    This function can be called multiple times to add more than one
+    adsorbate.
+
+    Parameters:
+
+    slab: The surface onto which the adsorbate should be added.
+
+    adsorbate:  The adsorbate. Must be one of the following three types:
+        A string containing the chemical symbol for a single atom.
+        An atom object.
+        An atoms object (for a molecular adsorbate).
+
+    height: Height above the surface.
+
+    position: The x-y position of the adsorbate, either as a tuple of
+        two numbers or as a keyword (if the surface is produced by one
+        of the functions in ase.lattice.surfaces).
+
+    offset (default: None): Offsets the adsorbate by a number of unit
+        cells. Mostly useful when adding more than one adsorbate.
+
+    mol_index (default: 0): If the adsorbate is a molecule, index of
+        the atom to be positioned above the location specified by the
+        position argument.
+
+    Note *position* is given in absolute xy coordinates (or as
+    a keyword), whereas offset is specified in unit cells.  This
+    can be used to give the positions in units of the unit cell by
+    using *offset* instead.
+    
+    """
+    info = slab.adsorbate_info
+    if 'cell' not in info:
+        info['cell'] = slab.get_cell()[:2, :2]
+
+    
+    pos = np.array([0.0, 0.0])  # (x, y) part
+    spos = np.array([0.0, 0.0]) # part relative to unit cell
+    if offset is not None:
+        spos += np.asarray(offset, float)
+
+    if isinstance(position, str):
+        # A site-name:
+        if 'sites' not in info:
+            raise TypeError('If the atoms are not made by an ' +
+                            'ase.lattice.surface function, ' +
+                            'position cannot be a name.')
+        if position not in info['sites']:
+            raise TypeError('Adsorption site %s not supported.' % position)
+        spos += info['sites'][position]
+    else:
+        pos += position
+
+    pos += np.dot(spos, info['cell'])
+
+    # Convert the adsorbate to an Atoms object
+    if isinstance(adsorbate, Atoms):
+        ads = adsorbate
+    elif isinstance(adsorbate, Atom):
+        ads = Atoms([adsorbate])
+    else:
+        # Hope it is a useful string or something like that
+        ads = Atoms(adsorbate)
+
+    # Get the z-coordinate:
+    try:
+        a = info['top layer atom index']
+    except KeyError:
+        a = slab.positions[:, 2].argmax()
+        info['top layer atom index'] = a
+    z = slab.positions[a, 2] + height
+
+    # Move adsorbate into position
+    ads.translate([pos[0], pos[1], z] - ads.positions[mol_index])
+
+    # Attach the adsorbate
+    slab.extend(ads)
+
+
+def _surface(symbol, structure, face, size, a, c, vacuum, orthogonal=True):
+    """Function to build often used surfaces.
+
+    Don't call this function directly - use fcc100, fcc110, bcc111, ..."""
+    
+    Z = atomic_numbers[symbol]
+
+    if a is None:
+        sym = reference_states[Z]['symmetry']
+        if sym != structure:
+            raise ValueError("Can't guess lattice constant for %s-%s!" %
+                             (structure, symbol))
+        a = reference_states[Z]['a']
+
+    if structure == 'hcp' and c is None:
+        if reference_states[Z]['symmetry'] == 'hcp':
+            c = reference_states[Z]['c/a'] * a
+        else:
+            c = sqrt(8 / 3.0) * a
+
+    positions = np.empty((size[2], size[1], size[0], 3))
+    positions[..., 0] = np.arange(size[0]).reshape((1, 1, -1))
+    positions[..., 1] = np.arange(size[1]).reshape((1, -1, 1))
+    positions[..., 2] = np.arange(size[2]).reshape((-1, 1, 1))
+
+    numbers = np.ones(size[0] * size[1] * size[2], int) * Z
+
+    tags = np.empty((size[2], size[1], size[0]), int)
+    tags[:] = np.arange(size[2], 0, -1).reshape((-1, 1, 1))
+
+    slab = Atoms(numbers,
+                 tags=tags.ravel(),
+                 pbc=(True, True, False),
+                 cell=size)
+
+    surface_cell = None
+    sites = {'ontop': (0, 0)}
+    surf = structure + face
+    if surf == 'fcc100':
+        cell = (sqrt(0.5), sqrt(0.5), 0.5)
+        positions[-2::-2, ..., :2] += 0.5
+        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
+    elif surf == 'diamond100':
+        cell = (sqrt(0.5), sqrt(0.5), 0.5 / 2)
+        positions[-4::-4, ..., :2] += (0.5, 0.5)
+        positions[-3::-4, ..., :2] += (0.0, 0.5)
+        positions[-2::-4, ..., :2] += (0.0, 0.0)
+        positions[-1::-4, ..., :2] += (0.5, 0.0)
+    elif surf == 'fcc110':
+        cell = (1.0, sqrt(0.5), sqrt(0.125))
+        positions[-2::-2, ..., :2] += 0.5
+        sites.update({'hollow': (0.5, 0.5), 'longbridge': (0.5, 0),
+                      'shortbridge': (0, 0.5)})
+    elif surf == 'bcc100':
+        cell = (1.0, 1.0, 0.5)
+        positions[-2::-2, ..., :2] += 0.5
+        sites.update({'hollow': (0.5, 0.5), 'bridge': (0.5, 0)})
+    else:
+        if orthogonal and size[1] % 2 == 1:
+            raise ValueError(("Can't make orthorhombic cell with size=%r.  " %
+                              (tuple(size),)) +
+                             'Second number in size must be even.')
+        if surf == 'fcc111':
+            cell = (sqrt(0.5), sqrt(0.375), 1 / sqrt(3))
+            if orthogonal:
+                positions[-1::-3, 1::2, :, 0] += 0.5
+                positions[-2::-3, 1::2, :, 0] += 0.5
+                positions[-3::-3, 1::2, :, 0] -= 0.5
+                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
+                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
+            else:
+                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
+                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
+            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
+                          'hcp': (2.0 / 3, 2.0 / 3)})
+        elif surf == 'diamond111':
+            cell = (sqrt(0.5), sqrt(0.375), 1 / sqrt(3) / 2)
+            assert not orthogonal
+            positions[-1::-6, ..., :3] += (0.0, 0.0, 0.5)
+            positions[-2::-6, ..., :2] += (0.0, 0.0)
+            positions[-3::-6, ..., :3] += (-1.0 / 3, 2.0 / 3, 0.5)
+            positions[-4::-6, ..., :2] += (-1.0 / 3, 2.0 / 3)
+            positions[-5::-6, ..., :3] += (1.0 / 3, 1.0 / 3, 0.5)
+            positions[-6::-6, ..., :2] += (1.0 / 3, 1.0 / 3)
+        elif surf == 'hcp0001':
+            cell = (1.0, sqrt(0.75), 0.5 * c / a)
+            if orthogonal:
+                positions[:, 1::2, :, 0] += 0.5
+                positions[-2::-2, ..., :2] += (0.0, 2.0 / 3)
+            else:
+                positions[-2::-2, ..., :2] += (-1.0 / 3, 2.0 / 3)
+            sites.update({'bridge': (0.5, 0), 'fcc': (1.0 / 3, 1.0 / 3),
+                          'hcp': (2.0 / 3, 2.0 / 3)})
+        elif surf == 'hcp10m10':
+            cell = (1.0, 0.5 * c / a, sqrt(0.75))
+            assert orthogonal
+            positions[-2::-2, ..., 0] += 0.5
+            positions[:, ::2, :, 2] += 2.0 / 3
+        elif surf == 'bcc110':
+            cell = (1.0, sqrt(0.5), sqrt(0.5))
+            if orthogonal:
+                positions[:, 1::2, :, 0] += 0.5
+                positions[-2::-2, ..., :2] += (0.0, 1.0)
+            else:
+                positions[-2::-2, ..., :2] += (-0.5, 1.0)
+            sites.update({'shortbridge': (0, 0.5),
+                          'longbridge': (0.5, 0),
+                          'hollow': (0.375, 0.25)})
+        elif surf == 'bcc111':
+            cell = (sqrt(2), sqrt(1.5), sqrt(3) / 6)
+            if orthogonal:
+                positions[-1::-3, 1::2, :, 0] += 0.5
+                positions[-2::-3, 1::2, :, 0] += 0.5
+                positions[-3::-3, 1::2, :, 0] -= 0.5
+                positions[-2::-3, ..., :2] += (0.0, 2.0 / 3)
+                positions[-3::-3, ..., :2] += (0.5, 1.0 / 3)
+            else:
+                positions[-2::-3, ..., :2] += (-1.0 / 3, 2.0 / 3)
+                positions[-3::-3, ..., :2] += (1.0 / 3, 1.0 / 3)
+            sites.update({'hollow': (1.0 / 3, 1.0 / 3)})
+        else:
+            2 / 0
+            
+        surface_cell = a * np.array([(cell[0], 0),
+                                     (cell[0] / 2, cell[1])])
+        if not orthogonal:
+            cell = np.array([(cell[0], 0, 0),
+                             (cell[0] / 2, cell[1], 0),
+                             (0, 0, cell[2])])
+
+    if surface_cell is None:
+        surface_cell = a * np.diag(cell[:2])
+
+    if isinstance(cell, tuple):
+        cell = np.diag(cell)
+        
+    slab.set_positions(positions.reshape((-1, 3)))
+
+    slab.set_cell([a * v * n for v, n in zip(cell, size)], scale_atoms=True)
+
+    if vacuum is not None:
+        slab.center(vacuum=vacuum, axis=2)
+    
+    slab.adsorbate_info['cell'] = surface_cell
+    slab.adsorbate_info['sites'] = sites
+    
+    return slab
+
+    
+        
diff --git a/ase/lattice/tetragonal.py b/ase/lattice/tetragonal.py
new file mode 100644
index 0000000..f9b4c19
--- /dev/null
+++ b/ase/lattice/tetragonal.py
@@ -0,0 +1,44 @@
+"""Function-like objects creating tetragonal lattices.
+
+The following lattice creators are defined:
+    SimleTetragonal
+    CenteredTetragonal
+"""
+
+from ase.lattice.orthorhombic import SimpleOrthorhombicFactory,\
+     BodyCenteredOrthorhombicFactory
+import numpy as np
+from ase.data import reference_states as _refstate
+
+
+class _Tetragonalize:
+    "A mixin class for implementing tetragonal crystals as orthorhombic ones."
+
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "tetragonal"
+
+    def make_crystal_basis(self):
+        lattice = self.latticeconstant
+        if type(lattice) == type({}):
+            lattice['b/a'] = 1.0
+        else:
+            if len(lattice) == 2:
+                lattice = (lattice[0], lattice[0], lattice[1])
+            else:
+                raise ValueError, "Improper lattice constants for tetragonal crystal."
+        self.latticeconstant = lattice
+        self.orthobase.make_crystal_basis(self)
+
+class SimpleTetragonalFactory(_Tetragonalize, SimpleOrthorhombicFactory):
+    "A factory for creating simple tetragonal lattices."
+    orthobase = SimpleOrthorhombicFactory
+
+SimpleTetragonal = SimpleTetragonalFactory()
+
+
+class CenteredTetragonalFactory(_Tetragonalize,
+                                BodyCenteredOrthorhombicFactory):
+    "A factory for creating centered tetragonal lattices."
+    orthobase = BodyCenteredOrthorhombicFactory
+
+CenteredTetragonal = CenteredTetragonalFactory()
diff --git a/ase/lattice/triclinic.py b/ase/lattice/triclinic.py
new file mode 100644
index 0000000..7ba25a2
--- /dev/null
+++ b/ase/lattice/triclinic.py
@@ -0,0 +1,83 @@
+"""Function-like object creating triclinic lattices.
+
+The following lattice creator is defined:
+    Triclinic
+"""
+
+from ase.lattice.bravais import Bravais
+import numpy as np
+from ase.data import reference_states as _refstate
+
+class TriclinicFactory(Bravais):
+    "A factory for creating triclinic lattices."
+
+    # The name of the crystal structure in ChemicalElements
+    xtal_name = "triclinic"
+
+    # The natural basis vectors of the crystal structure
+    int_basis = np.array([[1, 0, 0],
+                          [0, 1, 0],
+                          [0, 0, 1]])
+    basis_factor = 1.0
+
+    # Converts the natural basis back to the crystallographic basis
+    inverse_basis = np.array([[1, 0, 0],
+                              [0, 1, 0],
+                              [0, 0, 1]])
+    inverse_basis_factor = 1.0
+
+    def get_lattice_constant(self):
+        "Get the lattice constant of an element with triclinic crystal structure."
+        if _refstate[self.atomicnumber]['symmetry'] != self.xtal_name:
+            raise ValueError(('Cannot guess the %s lattice constant of'
+                              + ' an element with crystal structure %s.')
+                             % (self.xtal_name,
+                                _refstate[self.atomicnumber]['symmetry']))
+        return _refstate[self.atomicnumber].copy()
+
+
+    def make_crystal_basis(self):
+        "Make the basis matrix for the crystal unit cell and the system unit cell."
+        lattice = self.latticeconstant
+        if type(lattice) == type({}):
+            a = lattice['a']
+            try:
+                b = lattice['b']
+            except KeyError:
+                b = a * lattice['b/a']
+            try:
+                c = lattice['c']
+            except KeyError:
+                c = a * lattice['c/a']
+            alpha = lattice['alpha']
+            beta = lattice['beta']
+            gamma = lattice['gamma']
+        else:
+            if len(lattice) == 6:
+                (a,b,c,alpha,beta,gamma) = lattice
+            else:
+                raise ValueError, "Improper lattice constants for triclinic crystal."
+
+        degree = np.pi / 180.0
+        cosa = np.cos(alpha*degree)
+        cosb = np.cos(beta*degree)
+        sinb = np.sin(beta*degree)
+        cosg = np.cos(gamma*degree)
+        sing = np.sin(gamma*degree)
+        lattice = np.array([[a,0,0],
+                            [b*cosg, b*sing,0],
+                            [c*cosb, c*(cosa-cosb*cosg)/sing,
+                             c*np.sqrt(sinb**2 - ((cosa-cosb*cosg)/sing)**2)]])
+        self.latticeconstant = lattice
+        self.miller_basis = lattice
+        self.crystal_basis = (self.basis_factor *
+                              np.dot(self.int_basis, lattice))
+        self.basis = np.dot(self.directions, self.crystal_basis)
+        assert abs(np.dot(lattice[0],lattice[1]) - a*b*cosg) < 1e-5
+        assert abs(np.dot(lattice[0],lattice[2]) - a*c*cosb) < 1e-5
+        assert abs(np.dot(lattice[1],lattice[2]) - b*c*cosa) < 1e-5
+        assert abs(np.dot(lattice[0],lattice[0]) - a*a) < 1e-5
+        assert abs(np.dot(lattice[1],lattice[1]) - b*b) < 1e-5
+        assert abs(np.dot(lattice[2],lattice[2]) - c*c) < 1e-5
+
+Triclinic = TriclinicFactory()
diff --git a/ase/md/__init__.py b/ase/md/__init__.py
new file mode 100644
index 0000000..8071e92
--- /dev/null
+++ b/ase/md/__init__.py
@@ -0,0 +1,5 @@
+"""Molecular Dynamics."""
+
+from ase.md.logger import MDLogger
+from ase.md.verlet import VelocityVerlet
+from ase.md.langevin import Langevin
diff --git a/ase/md/langevin.py b/ase/md/langevin.py
new file mode 100644
index 0000000..532f937
--- /dev/null
+++ b/ase/md/langevin.py
@@ -0,0 +1,130 @@
+"""Langevin dynamics class."""
+
+
+import sys
+import numpy as np
+from numpy.random import standard_normal
+from ase.md.md import MolecularDynamics
+from ase.parallel import world
+
+
+class Langevin(MolecularDynamics):
+    """Langevin (constant N, V, T) molecular dynamics.
+
+    Usage: Langevin(atoms, dt, temperature, friction)
+
+    atoms
+        The list of atoms.
+        
+    dt
+        The time step.
+
+    temperature
+        The desired temperature, in energy units.
+
+    friction
+        A friction coefficient, typically 1e-4 to 1e-2.
+
+    fixcm
+        If True, the position and momentum of the center of mass is
+        kept unperturbed.  Default: True.
+
+    The temperature and friction are normally scalars, but in principle one
+    quantity per atom could be specified by giving an array.
+
+    This dynamics accesses the atoms using Cartesian coordinates."""
+    
+    def __init__(self, atoms, timestep, temperature, friction, fixcm=True,
+                 trajectory=None, logfile=None, loginterval=1,
+                 communicator=world):
+        MolecularDynamics.__init__(self, atoms, timestep, trajectory,
+                                   logfile, loginterval)
+        self.temp = temperature
+        self.frict = friction
+        self.fixcm = fixcm  # will the center of mass be held fixed?
+        self.communicator = communicator
+        self.updatevars()
+        
+    def set_temperature(self, temperature):
+        self.temp = temperature
+        self.updatevars()
+
+    def set_friction(self, friction):
+        self.frict = friction
+        self.updatevars()
+
+    def set_timestep(self, timestep):
+        self.dt = timestep
+        self.updatevars()
+
+    def updatevars(self):
+        dt = self.dt
+        # If the friction is an array some other constants must be arrays too.
+        self._localfrict = hasattr(self.frict, 'shape')
+        lt = self.frict * dt
+        masses = self.masses
+        sdpos = dt * np.sqrt(self.temp / masses * (2.0/3.0 - 0.5 * lt) * lt)
+        sdpos.shape = (-1, 1)
+        sdmom = np.sqrt(self.temp * masses * 2.0 * (1.0 - lt) * lt)
+        sdmom.shape = (-1, 1)
+        pmcor = np.sqrt(3.0)/2.0 * (1.0 - 0.125 * lt)
+        cnst = np.sqrt((1.0 - pmcor) * (1.0 + pmcor))
+
+        act0 = 1.0 - lt + 0.5 * lt * lt
+        act1 = (1.0 - 0.5 * lt + (1.0/6.0) * lt * lt)
+        act2 = 0.5 - (1.0/6.0) * lt + (1.0/24.0) * lt * lt
+        c1 = act1 * dt / masses
+        c1.shape = (-1, 1)
+        c2 = act2 * dt * dt / masses
+        c2.shape = (-1, 1)
+        c3 = (act1 - act2) * dt
+        c4 = act2 * dt
+        del act1, act2
+        if self._localfrict:
+            # If the friction is an array, so are these
+            act0.shape = (-1, 1)
+            c3.shape = (-1, 1)
+            c4.shape = (-1, 1)
+            pmcor.shape = (-1, 1)
+            cnst.shape = (-1, 1)
+        self.sdpos = sdpos
+        self.sdmom = sdmom
+        self.c1 = c1
+        self.c2 = c2
+        self.act0 = act0
+        self.c3 = c3
+        self.c4 = c4
+        self.pmcor = pmcor
+        self.cnst = cnst
+
+    def step(self, f):
+        atoms = self.atoms
+        p = self.atoms.get_momenta()
+
+        random1 = standard_normal(size=(len(atoms), 3))
+        random2 = standard_normal(size=(len(atoms), 3))
+
+        self.communicator.broadcast(random1, 0)
+        self.communicator.broadcast(random2, 0)
+        
+        rrnd = self.sdpos * random1
+        prnd = (self.sdmom * self.pmcor * random1 +
+                self.sdmom * self.cnst * random2)
+
+        if self.fixcm:
+            rrnd = rrnd - np.sum(rrnd, 0) / len(atoms)
+            prnd = prnd - np.sum(prnd, 0) / len(atoms)
+            n = len(atoms)
+            rrnd *= np.sqrt(n / (n - 1.0))
+            prnd *= np.sqrt(n / (n - 1.0))
+
+        atoms.set_positions(atoms.get_positions() +
+                            self.c1 * p +
+                            self.c2 * f + rrnd)
+        p *= self.act0
+        p += self.c3 * f + prnd
+        atoms.set_momenta(p)
+                      
+        f = atoms.get_forces()
+        atoms.set_momenta(p + self.c4 * f)
+        return f
diff --git a/ase/md/logger.py b/ase/md/logger.py
new file mode 100644
index 0000000..f51c25d
--- /dev/null
+++ b/ase/md/logger.py
@@ -0,0 +1,100 @@
+"""Logging for molecular dynamics."""
+
+import weakref
+import sys
+import ase.units as units
+# ase.parallel imported in __init__
+
+class MDLogger:
+    """Class for logging molecular dynamics simulations.
+
+    Parameters:
+    dyn:           The dynamics.  Only a weak reference is kept.
+
+    atoms:         The atoms.
+
+    logfile:       File name or open file, "-" meaning standart output.
+
+    stress=False:  Include stress in log.
+
+    peratom=False: Write energies per atom.
+
+    mode="a":      How the file is opened if logfile is a filename.
+    """
+    def __init__(self, dyn, atoms, logfile, header=True, stress=False,
+                 peratom=False, mode="a"):
+        import ase.parallel
+        if ase.parallel.rank > 0:
+            logfile="/dev/null"  # Only log on master
+        if hasattr(dyn, "get_time"):
+            self.dyn = weakref.proxy(dyn)
+        else:
+            self.dyn = None
+        self.atoms = atoms
+        self.natoms = atoms.get_number_of_atoms()
+        if logfile == "-":
+            self.logfile = sys.stdout
+            self.ownlogfile = False
+        elif hasattr(logfile, "write"):
+            self.logfile = logfile
+            self.ownlogfile = False
+        else:
+            self.logfile = open(logfile, mode)
+            self.ownlogfile = True
+        self.stress = stress
+        self.peratom = peratom
+        if self.dyn is not None:
+            self.hdr = "%-8s " % ("Time[ps]",)
+            self.fmt = "%-8.2f "
+        else:
+            self.hdr = ""
+            self.fmt = ""
+        if self.peratom:
+            self.hdr += "%12s %12s %12s  %6s" % ("Etot/N[eV]", "Epot/N[eV]",
+                                                 "Ekin/N[eV]", "T[K]")
+            self.fmt += "%12.4f %12.4f %12.4f  %6.1f"
+        else:
+            self.hdr += "%12s %12s %12s  %6s" % ("Etot[eV]", "Epot[eV]",
+                                                 "Ekin[eV]", "T[K]")
+            # Choose a sensible number of decimals
+            if self.natoms <= 10:
+                digits = 4
+            elif self.natoms <= 100:
+                digits = 3
+            elif self.natoms <= 1000:
+                digits = 2
+            else:
+                digits = 1
+            self.fmt += 3*("%%12.%df " % (digits,)) + " %6.1f"
+        if self.stress:
+            self.hdr += "      ---------------- stress [GPa] -----------------"
+            self.fmt += 6*" %10.3f"
+        self.fmt += "\n"
+        if header:
+            self.logfile.write(self.hdr+"\n")
+            
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        if self.ownlogfile:
+            self.logfile.close()
+
+    def __call__(self):
+        epot = self.atoms.get_potential_energy()
+        ekin = self.atoms.get_kinetic_energy()
+        temp = ekin / (1.5 * units.kB * self.natoms)
+        if self.peratom:
+            epot /= self.natoms
+            ekin /= self.natoms
+        if self.dyn is not None:
+            t = self.dyn.get_time() / (1000*units.fs)
+            dat = (t,)
+        else:
+            dat = ()
+        dat += (epot+ekin, epot, ekin, temp)
+        if self.stress:
+            dat += tuple(self.atoms.get_stress() / units.GPa)
+        self.logfile.write(self.fmt % dat)
+        self.logfile.flush()
+        
diff --git a/ase/md/md.py b/ase/md/md.py
new file mode 100644
index 0000000..96c0e02
--- /dev/null
+++ b/ase/md/md.py
@@ -0,0 +1,41 @@
+"""Molecular Dynamics."""
+
+import warnings
+import numpy as np
+
+from ase.optimize.optimize import Dynamics
+from ase.data import atomic_masses
+from ase.md.logger import MDLogger
+
+
+class MolecularDynamics(Dynamics):
+    """Base-class for all MD classes."""
+    def __init__(self, atoms, timestep, trajectory, logfile=None,
+                 loginterval=1):
+        Dynamics.__init__(self, atoms, logfile=None, trajectory=trajectory)
+        self.dt = timestep
+        self.masses = self.atoms.get_masses()
+        if 0 in self.masses:
+            warnings.warn('Zero mass encountered in atoms; this will '
+                          'likely lead to errors if the massless atoms '
+                          'are unconstrained.')
+        self.masses.shape = (-1, 1)
+        if logfile:
+            self.attach(MDLogger(dyn=self, atoms=atoms, logfile=logfile),
+                        interval=loginterval)
+
+    def run(self, steps=50):
+        """Integrate equation of motion."""
+        f = self.atoms.get_forces()
+
+        if not self.atoms.has('momenta'):
+            self.atoms.set_momenta(np.zeros_like(f))
+
+        for step in xrange(steps):
+            f = self.step(f)
+            self.nsteps += 1
+            self.call_observers()
+
+    def get_time(self):
+        return self.nsteps * self.dt
+    
diff --git a/ase/md/npt.py b/ase/md/npt.py
new file mode 100644
index 0000000..12a2c11
--- /dev/null
+++ b/ase/md/npt.py
@@ -0,0 +1,797 @@
+'''Constant pressure/stress and temperature dynamics.
+
+Combined Nose-Hoover and Parrinello-Rahman dynamics, creating an NPT
+(or N,stress,T) ensemble.
+
+The method is the one proposed by Melchionna et al. [1] and later
+modified by Melchionna [2].  The differential equations are integrated
+using a centered difference method [3].
+
+ 1. S. Melchionna, G. Ciccotti and B. L. Holian, "Hoover NPT dynamics
+    for systems varying in shape and size", Molecular Physics 78, p. 533
+    (1993).
+
+ 2. S. Melchionna, "Constrained systems and statistical distribution",
+    Physical Review E, 61, p. 6165 (2000).
+
+ 3. B. L. Holian, A. J. De Groot, W. G. Hoover, and C. G. Hoover,
+    "Time-reversible equilibrium and nonequilibrium isothermal-isobaric
+    simulations with centered-difference Stoermer algorithms.", Physical
+    Review A, 41, p. 4552 (1990).
+'''
+
+__docformat__ = 'reStructuredText'
+
+from numpy import *
+import sys
+import weakref
+from ase.md.md import MolecularDynamics
+#from ASE.Trajectories.NetCDFTrajectory import NetCDFTrajectory
+
+# Delayed imports:  If the trajectory object is reading a special ASAP version
+# of HooverNPT, that class is imported from Asap.Dynamics.NPTDynamics.
+
+class NPT(MolecularDynamics):
+    '''Constant pressure/stress and temperature dynamics.
+
+    Combined Nose-Hoover and Parrinello-Rahman dynamics, creating an
+    NPT (or N,stress,T) ensemble.
+
+    The method is the one proposed by Melchionna et al. [1] and later
+    modified by Melchionna [2].  The differential equations are integrated
+    using a centered difference method [3].  See also NPTdynamics.tex
+
+    The dynamics object is called with the following parameters:
+
+    atoms
+        The list of atoms.
+
+    dt
+        The timestep in units matching eV, A, u.
+
+    temperature
+        The desired temperature in eV.
+
+    externalstress
+        The external stress in eV/A^3.  Either a symmetric
+        3x3 tensor, a 6-vector representing the same, or a
+        scalar representing the pressure.  Note that the
+        stress is positive in tension whereas the pressure is
+        positive in compression: giving a scalar p is
+        equivalent to giving the tensor (-p, -p, -p, 0, 0, 0).
+
+    ttime
+        Characteristic timescale of the thermostat.
+        Set to None to disable the thermostat.
+
+    pfactor
+        A constant in the barostat differential equation.  If
+        a characteristic barostat timescale of ptime is
+        desired, set pfactor to ptime^2 * B (where B is the
+        Bulk Modulus).  Set to None to disable the barostat.
+        Typical metallic bulk moduli are of the order of
+        100 GPa or 0.6 eV/A^3.
+
+    mask=None
+        Optional argument.  A tuple of three integers (0 or 1),
+        indicating if the system can change size along the
+        three Cartesian axes.  Set to (1,1,1) or None to allow
+        a fully flexible computational box.  Set to (1,1,0)
+        to disallow elongations along the z-axis etc.
+
+    Useful parameter values:
+
+    * The same timestep can be used as in Verlet dynamics, i.e. 5 fs is fine
+      for bulk copper.
+
+    * The ttime and pfactor are quite critical[4], too small values may
+      cause instabilites and/or wrong fluctuations in T / p.  Too
+      large values cause an oscillation which is slow to die.  Good
+      values for the characteristic times seem to be 25 fs for ttime,
+      and 75 fs for ptime (used to calculate pfactor), at least for
+      bulk copper with 15000-200000 atoms.  But this is not well
+      tested, it is IMPORTANT to monitor the temperature and
+      stress/pressure fluctuations.
+    
+    It has the following methods:
+
+    __call__(n)
+        Perform n timesteps.
+    initialize()
+        Estimates the dynamic variables for time=-1 to start
+        the algorithm.   This is automatically called before
+        the first timestep.
+    set_stress()
+        Set the external stress.  Use with care.  It is
+        preferable to set the right value when creating the
+        object.
+    set_mask()
+        Change the mask.  Use with care, as you may "freeze"
+        a fluctuation in the strain rate.
+    get_gibbs_free_energy()
+        Gibbs free energy is supposed to be preserved by this
+        dynamics.  This is mainly intended as a diagnostic
+        tool.
+    
+    References:
+    
+    1) S. Melchionna, G. Ciccotti and B. L. Holian, Molecular
+       Physics 78, p. 533 (1993).
+
+    2) S. Melchionna, Physical
+       Review E 61, p. 6165 (2000).
+
+    3) B. L. Holian, A. J. De Groot, W. G. Hoover, and C. G. Hoover,
+       Physical Review A 41, p. 4552 (1990).
+
+    4) F. D. Di Tolla and M. Ronchetti, Physical
+       Review E 48, p. 1726 (1993).
+    
+    '''
+
+    classname = "NPT"  # Used by the trajectory.
+    def __init__(self, atoms, 
+                 timestep, temperature, externalstress, ttime, pfactor,
+                 mask=None, trajectory=None, logfile=None, loginterval=1):
+        MolecularDynamics.__init__(self, atoms, timestep, trajectory,
+                                   logfile, loginterval)
+        #self.atoms = atoms
+        #self.timestep = timestep
+        self.zero_center_of_mass_momentum(verbose=1)
+        self.temperature = temperature
+        self.set_stress(externalstress)
+        self.set_mask(mask)
+        self.eta = zeros((3,3), float)
+        self.zeta = 0.0
+        self.zeta_integrated = 0.0
+        self.initialized = 0
+        self.ttime = ttime
+        self.pfactor_given = pfactor
+        self._calculateconstants()
+        self.timeelapsed = 0.0
+        self.frac_traceless = 1
+
+    def set_temperature(self, temperature):
+        self.temperature = temperature
+        self._calculateconstants()
+        
+    def set_stress(self, stress):
+        """Set the applied stress.
+
+        Must be a symmetric 3x3 tensor, a 6-vector representing a symmetric
+        3x3 tensor, or a number representing the pressure.
+        """
+        if type(stress) == type(1.0) or type(stress) == type(1):
+            stress = array((-stress, -stress, -stress, 0.0, 0.0, 0.0))
+        elif stress.shape == (3,3):
+            if not self._issymmetric(stress):
+                raise ValueError, "The external stress must be a symmetric tensor."
+            stress = array((stress[0,0], stress[1,1], stress[2,2], stress[1,2],
+                            stress[0,2], stress[0,1]))
+        elif stress.shape != (6,):
+            raise ValueError, "The external stress has the wrong shape."
+        self.externalstress = stress
+
+    def set_mask(self, mask):
+        """Set the mask indicating dynamic elements of the computational box.
+
+        If set to None, all elements may change.  If set to a 3-vector
+        of ones and zeros, elements which are zero specify directions
+        along which the size of the computational box cannot change.
+        For example, if mask = {1,1,0} the length of the system along
+        the z-axis cannot change, although xz and yz shear is still
+        possible.  To disable shear globally, set the mode to diagonal
+        (not yet implemented).
+        """
+        if mask is None:
+            mask = ones((3,))
+        if not hasattr(mask, "shape"):
+            mask = array(mask)        
+        if mask.shape != (3,) and mask.shape != (3,3):
+            raise "The mask has the wrong shape (must be a 3-vector or 3x3 matrix)"
+        else:
+            mask = not_equal(mask, 0)  # Make sure it is 0/1
+
+        if mask.shape == (3,):
+            self.mask = outer(mask, mask)
+        else:
+            self.mask = mask
+        
+    def set_fraction_traceless(self, fracTraceless):
+        """set what fraction of the traceless part of the force
+        on eta is kept.
+
+        By setting this to zero, the volume may change but the shape may not.
+        """
+        self.frac_traceless = fracTraceless
+
+    def get_strain_rate(self):
+        "Get the strain rate as an upper-triangular 3x3 matrix"
+        return array(self.eta, copy=1)
+
+    def set_strain_rate(self, rate):
+        "Set the strain rate.  Must be an upper triangular 3x3 matrix."
+        if not (rate.shape == (3,3) and self._isuppertriangular(rate)):
+            raise ValueError, "Strain rate must be an upper triangular matrix."
+        self.eta = rate
+        if self.initialized:
+            # Recalculate h_past and eta_past so they match the current value.
+            self._initialize_eta_h()
+
+    def get_time(self):
+        "Get the elapsed time."
+        return self.timeelapsed
+    
+    def run(self, steps):
+        """Perform a number of time steps."""
+        if not self.initialized:
+            self.initialize()
+        else:
+            if self.have_the_atoms_been_changed():
+                raise NotImplementedError, "You have modified the atoms since the last timestep."
+
+        for i in xrange(steps):
+            self.step()
+            self.nsteps += 1
+            self.call_observers()
+
+    def have_the_atoms_been_changed(self):
+        "Checks if the user has modified the positions or momenta of the atoms"
+        limit = 1e-10
+        h = self._getbox()
+        if max(abs((h - self.h).ravel())) > limit:
+            self._warning("The computational box has been modified.")
+            return 1
+        expected_r = dot(self.q + 0.5, h)
+        err = max(abs((expected_r - self.atoms.get_positions()).ravel())) 
+        if err > limit:
+            self._warning("The atomic positions have been modified: "+ str(err))
+            return 1
+        return 0
+    
+    def step(self):
+        """Perform a single time step.
+        
+        Assumes that the forces and stresses are up to date, and that
+        the positions and momenta have not been changed since last
+        timestep.
+        """
+        
+        ## Assumes the following variables are OK
+        # q_past, q, q_future, p, eta, eta_past, zeta, zeta_past, h, h_past
+        #
+        # q corresponds to the current positions
+        # p must be equal to self.atoms.GetCartesianMomenta()
+        # h must be equal to self.atoms.GetUnitCell()
+        #
+        #print "Making a timestep"
+        dt = self.dt
+        h_future = self.h_past + 2*dt * dot(self.h, self.eta)
+        if self.pfactor_given is None:
+            deltaeta = zeros(6, float)
+        else:
+            stress = self.stresscalculator()
+            deltaeta = -2*dt * (self.pfact * linalg.det(self.h)
+                                * (stress - self.externalstress))
+        
+        if self.frac_traceless == 1:
+            eta_future = self.eta_past + self.mask * self._makeuppertriangular(deltaeta)
+        else:
+            trace_part, traceless_part = self._separatetrace(self._makeuppertriangular(deltaeta))
+            eta_future = self.eta_past + trace_part + self.frac_traceless * traceless_part
+
+        deltazeta = 2*dt*self.tfact * (self.atoms.get_kinetic_energy()
+                                       - self.desiredEkin)
+        zeta_future = self.zeta_past + deltazeta
+        # Advance time
+        #print "Max change in scaled positions:", max(abs(self.q_future.flat - self.q.flat))
+        #print "Max change in basis set", max(abs((h_future - self.h).flat))
+        self.timeelapsed += dt
+        self.h_past = self.h
+        self.h = h_future
+        self.inv_h = linalg.inv(self.h)
+        # Do not throw away the q arrays, they are "magical" on parallel
+        # simulations (the contents migrate along with the atoms).
+        (self.q_past, self.q, self.q_future) = (self.q, self.q_future,
+                                                self.q_past)
+        self._setbox_and_positions(self.h,self.q)
+        self.eta_past = self.eta
+        self.eta = eta_future
+        self.zeta_past = self.zeta
+        self.zeta = zeta_future
+        self._synchronize()  # for parallel simulations.
+        self.zeta_integrated += dt * self.zeta
+        force = self.forcecalculator()
+        # The periodic boundary conditions may have moved the atoms.
+        self.post_pbc_fix(fixfuture=0)  
+        self._calculate_q_future(force)
+        self.atoms.set_momenta(dot(self.q_future-self.q_past, self.h/(2*dt)) *
+                               self._getmasses())
+        #self.stresscalculator()
+        
+    def forcecalculator(self):
+        return self.atoms.get_forces()
+    
+    def stresscalculator(self):
+        return self.atoms.get_stress()
+
+    def initialize(self):
+        """Initialize the dynamics.
+
+        The dynamics requires positions etc for the two last times to
+        do a timestep, so the algorithm is not self-starting.  This
+        method performs a 'backwards' timestep to generate a
+        configuration before the current.
+        """
+        #print "Initializing the NPT dynamics."
+        dt = self.dt
+        atoms = self.atoms
+        self.h = self._getbox()
+        if not self._isuppertriangular(self.h):
+            print "I am", self
+            print "self.h:"
+            print self.h
+            print "Min:", min((self.h[1,0], self.h[2,0], self.h[2,1]))
+            print "Max:", max((self.h[1,0], self.h[2,0], self.h[2,1]))
+            raise NotImplementedError, "Can (so far) only operate on lists of atoms where the computational box is an upper triangular matrix."
+        self.inv_h = linalg.inv(self.h)
+        # The contents of the q arrays should migrate in parallel simulations.
+        self._make_special_q_arrays()
+        self.q[:] = dot(self.atoms.get_positions(),
+                                self.inv_h) - 0.5
+        # zeta and eta were set in __init__
+        self._initialize_eta_h()
+        deltazeta = dt * self.tfact * (atoms.get_kinetic_energy() -
+                                       self.desiredEkin)
+        self.zeta_past = self.zeta - deltazeta
+        self._calculate_q_past_and_future()
+        self.initialized = 1
+
+    def get_gibbs_free_energy(self):
+        """Return the Gibb's free energy, which is supposed to be conserved.
+
+        Requires that the energies of the atoms are up to date.
+
+        This is mainly intended as a diagnostic tool.  If called before the
+        first timestep, Initialize will be called.
+        """
+        if not self.initialized:
+            self.initialize()
+        n = self._getnatoms()
+        #tretaTeta = sum(diagonal(matrixmultiply(transpose(self.eta),
+        #                                        self.eta)))
+        contractedeta = sum((self.eta*self.eta).ravel())
+        gibbs = (self.atoms.get_potential_energy() +
+                 self.atoms.get_kinetic_energy()
+                 - sum(self.externalstress[0:3]) * linalg.det(self.h) / 3.0)
+        if self.ttime is not None:
+            gibbs += (1.5 * n * self.temperature * (self.ttime * self.zeta)**2
+                      + 3 * self.temperature * (n-1) * self.zeta_integrated)
+        else:
+            assert self.zeta == 0.0
+        if self.pfactor_given is not None:
+            gibbs += 0.5 / self.pfact * contractedeta
+        else:
+            assert contractedeta == 0.0
+        return gibbs
+
+    def get_center_of_mass_momentum(self):
+        "Get the center of mass momentum."
+        return self.atoms.get_momenta().sum(0)
+
+    def zero_center_of_mass_momentum(self, verbose=0):
+        "Set the center of mass momentum to zero."
+        cm = self.get_center_of_mass_momentum()
+        abscm = sqrt(sum(cm*cm))
+        if verbose and abscm > 1e-4:
+            self._warning(self.classname+": Setting the center-of-mass momentum to zero (was %.6g %.6g %.6g)" % tuple(cm))
+        self.atoms.set_momenta(self.atoms.get_momenta()
+                               - cm / self._getnatoms())
+    
+    def post_pbc_fix(self, fixfuture=1):
+        """Correct for atoms moved by the boundary conditions.
+
+        If the fixfuture argument is 1 (the default), q_future is also
+        corrected.  This is not necessary when post_pbc_fix() is called from
+        within Timestep(), but must be done when the user calls post_pbc_fix
+        (for example if a CNA calculation may have triggered a migration).
+        """
+        q = dot(self.atoms.get_positions(),
+                           self.inv_h) - 0.5
+        delta_q = floor(0.5 + (q - self.q))
+        self.q += delta_q
+        self.q_past += delta_q
+        if fixfuture:
+            self.q_future += delta_q
+        
+    def attach_atoms(self, atoms):
+        """Assign atoms to a restored dynamics object.
+
+        This function must be called to set the atoms immediately after the
+        dynamics object has been read from a trajectory.
+        """
+        try:
+            self.atoms
+        except AttributeError:
+            pass
+        else:
+            raise RuntimeError, "Cannot call attach_atoms on a dynamics which already has atoms."
+        MolecularDynamics.__init__(self, atoms, self.dt)
+        ####self.atoms = atoms
+        limit = 1e-6
+        h = self._getbox()
+        if max(abs((h - self.h).ravel())) > limit:
+            raise RuntimeError, "The unit cell of the atoms does not match the unit cell stored in the file."
+        self.inv_h = linalg.inv(self.h)
+        self._make_special_q_arrays()
+        self.q[:] = dot(self.atoms.get_positions(),
+                                           self.inv_h) - 0.5
+        self._calculate_q_past_and_future()
+        self.initialized = 1
+        
+    def attach(self, function, interval=1, *args, **kwargs):
+        """Attach callback function or trajectory.
+
+        At every *interval* steps, call *function* with arguments
+        *args* and keyword arguments *kwargs*.
+        
+        If *function* is a trajectory object, its write() method is
+        attached, but if *function* is a BundleTrajectory (or another
+        trajectory supporting set_extra_data(), said method is first
+        used to instruct the trajectory to also save internal
+        data from the NPT dynamics object.
+        """
+        if hasattr(function, "set_extra_data"):
+            # We are attaching a BundleTrajectory or similar
+            function.set_extra_data("npt_init",
+                                    WeakMethodWrapper(self, "get_init_data"),
+                                    once=True)
+            function.set_extra_data("npt_dynamics",
+                                    WeakMethodWrapper(self, "get_data"))
+        MolecularDynamics.attach(self, function, interval, *args, **kwargs)
+
+    def get_init_data(self):
+        "Return the data needed to initialize a new NPT dynamics."
+        return {'dt': self.dt,
+                'temperature': self.temperature,
+                'desiredEkin': self.desiredEkin,
+                'externalstress': self.externalstress,
+                'mask': self.mask,
+                'ttime': self.ttime,
+                'tfact': self.tfact,
+                'pfactor_given': self.pfactor_given,
+                'pfact': self.pfact,
+                'frac_traceless': self.frac_traceless}
+        
+    def get_data(self):
+        "Return data needed to restore the state."
+        return {'eta': self.eta,
+                'eta_past': self.eta_past,
+                'zeta': self.zeta,
+                'zeta_past': self.zeta_past,
+                'zeta_integrated': self.zeta_integrated,
+                'h': self.h,
+                'h_past': self.h_past,
+                'timeelapsed': self.timeelapsed}
+        
+    @classmethod
+    def read_from_trajectory(cls, trajectory, frame=-1, atoms=None):
+        """Read dynamics and atoms from trajectory (Class method).
+        
+        Simultaneously reads the atoms and the dynamics from a BundleTrajectory,
+        including the internal data of the NPT dynamics object (automatically
+        saved when attaching a BundleTrajectory to an NPT object).
+        
+        Arguments::
+        
+        trajectory 
+            The filename or an open BundleTrajectory object.
+        
+        frame (optional)
+            Which frame to read.  Default: the last.
+            
+        atoms (optional, internal use only)
+            Pre-read atoms.  Do not use. 
+        """
+        if isinstance(trajectory, str):
+            if trajectory.endswith('/'):
+                trajectory = trajectory[:-1]
+            if trajectory.endswith('.bundle'):
+                from ase.io.bundletrajectory import BundleTrajectory
+                trajectory = BundleTrajectory(trajectory)
+            else:
+                raise ValueError("Cannot open '%': unsupported file format" % trajectory)
+        # trajectory is now a BundleTrajectory object (or compatible)
+        if atoms is None:
+            atoms = trajectory[frame]
+        init_data = trajectory.read_extra_data('npt_init', 0)
+        frame_data = trajectory.read_extra_data('npt_dynamics', frame)
+        dyn = cls(atoms, timestep=init_data['dt'], 
+                  temperature=init_data['temperature'],
+                  externalstress=init_data['externalstress'],
+                  ttime=init_data['ttime'],
+                  pfactor=init_data['pfactor_given'],
+                  mask=init_data['mask'])
+        dyn.desiredEkin = init_data['desiredEkin']
+        dyn.tfact = init_data['tfact']
+        dyn.pfact = init_data['pfact']
+        dyn.frac_traceless = init_data['frac_traceless']
+        for k, v in frame_data.items():
+            setattr(dyn, k, v)
+        return (dyn, atoms)
+        
+    def _getbox(self):
+        "Get the computational box."
+        return self.atoms.get_cell()
+
+    def _getmasses(self):
+        "Get the masses as an Nx1 array."
+        return reshape(self.atoms.get_masses(), (-1,1))
+    
+#    def _getcartesianpositions(self):
+#        "Get the cartesian positions of the atoms"
+#        return self.atoms.get_positions()
+    
+#    def _getmomenta(self):
+#        "Get the (cartesian) momenta of the atoms"
+#        return self.atoms.GetCartesianMomenta()
+
+#    def _getforces(self):
+#        "Get the (cartesian) forces of the atoms"
+#        return self.atoms.GetCartesianForces()
+
+#    def _setmomenta(self, momenta):
+#        "Set the (cartesian) momenta of the atoms"
+#        self.atoms.SetCartesianMomenta(momenta)
+        
+    def _separatetrace(self, mat):
+        """return two matrices, one proportional to the identity
+        the other traceless, which sum to the given matrix
+        """
+        tracePart = ((mat[0][0] + mat[1][1] + mat[2][2]) / 3.) * identity(3)
+        return tracePart, mat - tracePart
+
+    # A number of convenient helper methods
+    def _warning(self, text):
+        "Emit a warning."
+        sys.stderr.write("WARNING: "+text+"\n")
+        sys.stderr.flush()
+    
+    def _calculate_q_future(self, force):
+        "Calculate future q.  Needed in Timestep and Initialization."
+        dt = self.dt
+        id3 = identity(3)
+        alpha = (dt * dt) * dot(force / self._getmasses(),
+                                self.inv_h)
+        beta = dt * dot(self.h, dot(self.eta + 0.5 * self.zeta * id3,
+                                    self.inv_h))
+        inv_b = linalg.inv(beta + id3)
+        self.q_future[:] = dot(2*self.q + dot(self.q_past, beta - id3) + alpha,
+                               inv_b)
+
+    def _calculate_q_past_and_future(self):
+        def ekin(p, m = self.atoms.get_masses()):
+            p2 = sum(p*p, -1)
+            return 0.5 * sum(p2 / m) / len(m)
+        p0 = self.atoms.get_momenta()
+        m = self._getmasses()
+        e0 = ekin(p0)
+        p = array(p0, copy=1)
+        dt = self.dt
+        for i in range(2):
+            self.q_past[:] = self.q - dt * dot(p / m, self.inv_h)
+            self._calculate_q_future(self.atoms.get_forces())
+            p = dot(self.q_future - self.q_past, self.h/(2*dt)) * m
+            e = ekin(p)
+            if e < 1e-5:
+                # The kinetic energy and momenta are virtually zero
+                return
+            p = (p0 - p) + p0
+
+    def _initialize_eta_h(self):
+        self.h_past = self.h - self.dt * dot(self.h, self.eta)
+        if self.pfactor_given is None:
+            deltaeta = zeros(6, float)
+        else:
+            deltaeta = (-self.dt * self.pfact * linalg.det(self.h)
+                        * (self.atoms.get_stress() - self.externalstress))
+        if self.frac_traceless == 1:
+            self.eta_past = self.eta - self.mask * self._makeuppertriangular(deltaeta)
+        else:
+            trace_part, traceless_part = self._separatetrace(self._makeuppertriangular(deltaeta))
+            self.eta_past = self.eta - trace_part - self.frac_traceless * traceless_part
+        
+    
+    def _makeuppertriangular(self, sixvector):
+        "Make an upper triangular matrix from a 6-vector."
+        return array(((sixvector[0], sixvector[5], sixvector[4]),
+                      (0,            sixvector[1], sixvector[3]),
+                      (0,            0,            sixvector[2])))
+
+    def _isuppertriangular(self, m):
+        "Check that a matrix is on upper triangular form."
+        return m[1,0] == m[2,0] == m[2,1] == 0.0
+    
+    def _calculateconstants(self):
+        "(Re)calculate some constants when pfactor, ttime or temperature have been changed."
+        n = self._getnatoms()
+        if self.ttime is None:
+            self.tfact = 0.0
+        else:
+            self.tfact = 2.0 / (3 * n * self.temperature *
+                                self.ttime * self.ttime)
+        if self.pfactor_given is None:
+            self.pfact = 0.0
+        else:
+            self.pfact = 1.0 / (self.pfactor_given
+                                * linalg.det(self._getbox()))
+            #self.pfact = 1.0/(n * self.temperature * self.ptime * self.ptime)
+        self.desiredEkin = 1.5 * (n - 1) * self.temperature
+
+    def _setbox_and_positions(self, h, q):
+        """Set the computational box and the positions."""
+        self.atoms.set_cell(h, scale_atoms=True)
+        r = dot(q + 0.5, h)
+        self.atoms.set_positions(r)
+
+    # A few helper methods, which have been placed in separate methods
+    # so they can be replaced in the parallel version.
+    def _synchronize(self):
+        """Synchronizes eta, h and zeta on all processors in a parallel simulation.
+
+        In a parallel simulation, eta, h and zeta are communicated
+        from the master to all slaves, to prevent numerical noise from
+        causing them to diverge.
+
+        In a serial simulation, do nothing.
+        """
+        pass  # This is a serial simulation object.  Do nothing.
+    
+    def _getnatoms(self):
+        """Get the number of atoms.
+
+        In a parallel simulation, this is the total number of atoms on all
+        processors.
+        """
+        return len(self.atoms)
+    
+    def _make_special_q_arrays(self):
+        """Make the arrays used to store data about the atoms.
+
+        In a parallel simulation, these are migrating arrays.  In a
+        serial simulation they are ordinary Numeric arrays.
+        """
+        natoms = len(self.atoms)
+        self.q = zeros((natoms,3), float)
+        self.q_past = zeros((natoms,3), float)
+        self.q_future = zeros((natoms,3), float)
+
+class WeakMethodWrapper:
+    """A weak reference to a method.
+    
+    Create an object storing a weak reference to an instance and 
+    the name of the method to call.  When called, calls the method.
+    
+    Just storing a weak reference to a bound method would not work,
+    as the bound method object would go away immediately.
+    """
+    def __init__(self, obj, method):
+        self.obj = weakref.proxy(obj)
+        self.method = method
+        
+    def __call__(self, *args, **kwargs):
+        m = getattr(self.obj, self.method)
+        return m(*args, **kwargs)
+
+# class _HooverNPTTrajectory:
+#     """A Trajectory-like object storing data in a HooverNPT object."""
+#     def InitForWrite(self):
+#         """Does initialization related to write mode."""
+#         self.CreateDimension('unlim', None)
+#         self.nc.history = 'ASE NPT trajectory'
+#         self.nc.version = '0.1'
+#         self.nc.classname = self.atoms.classname
+#         self.unlim = 0
+#         self.nc.lengthunit = units.GetLengthUnit()
+#         self.nc.energyunit = units.GetEnergyUnit()
+#         self.conversion = (1, 1)
+
+#     def InitForWriteOrAppend(self):
+#         """Does initialization related to write and append mode.
+
+#         Either InitForWrite or InitForReadOrAppend will have been
+#         called before calling this method.
+#         """
+#         names = copy.copy(self.known_names)
+#         if self.atoms.ttime is None:
+#             del names['ttime']
+#         if self.atoms.pfactor_given is None:
+#             del names['pfactor_given']
+#         for d in names.keys():
+#             def getdata(atoms=self.atoms, name=d):
+#                 return getattr(atoms, name)
+#             self.Add(d, data = getdata)
+                     
+#     known_names = {
+#         #    name                 shape        typecode  once    units
+#         # ----------------------------------------------------------------
+#         'dt':              ((),                Float,    True,   (1, -0.5)),
+#         'temperature':     ((),                Float,    True,   (0, 1)),
+#         'desiredEkin':     ((),                Float,    True,   (0, 1)),
+#         'externalstress':  ((6,),              Float,    True,   (-3, 1)),
+#         'mask':            ((3, 3),            Float,    True,   (0, 0)),
+#         'ttime':           ((),                Float,    True,   (1, -0.5)),
+#         'tfact':           ((),                Float,    True,   (-2, 0)),
+#         'pfactor_given':   ((),                Float,    True,   (-1, 0)),
+#         'pfact':           ((),                Float,    True,   (-2, 0)),
+#         'frac_traceless':  ((),                Float,    True,   (0, 0)),
+#         'eta':             ((3, 3),            Float,    False,  (-1, 0.5)),
+#         'eta_past':        ((3, 3),            Float,    False,  (-1, 0.5)),
+#         'zeta':            ((),                Float,    False,  (-1, 0.5)),
+#         'zeta_past':       ((),                Float,    False,  (-1, 0.5)),
+#         'zeta_integrated': ((),                Float,    False,  (0, 0)),
+#         'h':               ((3, 3),            Float,    False,  (1, 0)),
+#         'h_past':          ((3, 3),            Float,    False,  (1, 0)),
+#         'timeelapsed':     ((),                Float,    False,  (1, -0.5))
+#         }
+
+#     # This trajectory does not store a list of atoms
+#     def GetListOfAtoms(self, frame=None):
+#         raise AttributeError, "GetListOfAtoms makes no sense in a HooverNPTTrajectory"
+
+#     # Instead, we store a dynamics
+#     def GetDynamics(self, frame=None):
+#         """Get a HooverNPT Dynamics object.
+
+#         If a frame number is not given, the current frame is used.
+
+#         The variant of the object (ASE HooverNPT, ASAP Serial/Parallel NPT)
+#         will be the same as the stored object.
+
+#         After getting the dynamics, the atoms should be attached with the
+#         dynamics.attach_atoms(atoms) method.        
+#         """
+#         # Bypass calling the normal constructor
+#         class Dummy:
+#             pass
+#         dyn = Dummy()
+#         dyn.__class__ = self.getClass(self.nc.classname)
+#         vars = self.nc.variables
+#         for q in self.known_names.keys():
+#             if vars.has_key(q):
+#                 once = self.known_names[q][2]
+#                 if once:
+#                     setattr(dyn, q, vars[q].getValue())
+#                 else:
+#                     setattr(dyn, q, vars[q][frame])
+#         return dyn
+
+#     def getClass(self, classname):
+#         "Internal function: turns a class name into a class object."
+#         if self.nc.classname == "HooverNPT":
+#             return HooverNPT
+#         else:
+#             raise RuntimeError, ("Cannot create a dynamics of type "
+#                                  + self.nc.classname)
+
+# class HooverNPTTrajectory(_HooverNPTTrajectory,NetCDFTrajectory):
+#     """A Trajectory-like object storing data in a HooverNPT object."""
+#     def __init__(self, filename, dynamics=None, mode=None, interval=1):
+#         """Open the NetCDF file.
+
+#         If there is no ``dynamics`` argument, then the file is opened
+#         in read mode - otherwise, write or append mode is used.  The
+#         ``interval`` argument determines how often the configurations
+#         are written to file."""
+#         # Call the original constructor, but passing the dynamics instead of
+#         # the atoms.
+#         if dynamics is not None:
+#             # Prevents a circular reference when the trajectory is attached
+#             # to the dynamics it observes.
+#             dynamics = weakref.proxy(dynamics)
+#         NetCDFTrajectory.__init__(self, filename,
+#                                   atoms=dynamics,
+#                                   mode=mode, interval=interval)
+
+    
+
diff --git a/ase/md/nptberendsen.py b/ase/md/nptberendsen.py
new file mode 100644
index 0000000..8aeda81
--- /dev/null
+++ b/ase/md/nptberendsen.py
@@ -0,0 +1,190 @@
+"""Berendsen NPT dynamics class."""
+
+import numpy as np
+
+#from ase.md import MolecularDynamics
+from ase.md.nvtberendsen import NVTBerendsen
+import ase.units as units
+#import math
+
+
+class NPTBerendsen(NVTBerendsen):
+    """Berendsen (constant N, P, T) molecular dynamics.
+    
+    This dynamics scale the velocities and volumes to maintain a constant
+    pressure and temperature.  The shape of the simulation cell is not
+    altered, if that is desired use Inhomogenous_NPTBerendsen.
+
+    Usage: NPTBerendsen(atoms, timestep, temperature, taut, pressure, taup)
+
+    atoms
+        The list of atoms.
+        
+    timestep
+        The time step.
+
+    temperature
+        The desired temperature, in Kelvin.
+
+    taut
+        Time constant for Berendsen temperature coupling.
+
+    fixcm
+        If True, the position and momentum of the center of mass is
+        kept unperturbed.  Default: True.
+
+    pressure
+        The desired pressure, in bar (1 bar = 1e5 Pa).
+
+    taup
+        Time constant for Berendsen pressure coupling.
+
+    compressibility
+        The compressibility of the material, water 4.57E-5 bar-1, in bar-1
+
+    """
+
+    def __init__(self, atoms, timestep, temperature, taut=0.5e3*units.fs,
+                 pressure = 1.01325, taup=1e3*units.fs,
+                 compressibility=4.57e-5, fixcm=True,
+                 trajectory=None, logfile=None, loginterval=1):
+
+        NVTBerendsen.__init__(self, atoms, timestep, temperature, taut, fixcm,
+                              trajectory, 
+                              logfile, loginterval)
+        self.taup = taup
+        self.pressure = pressure
+        self.compressibility = compressibility
+
+    def set_taup(self, taut):
+        self.taut = taut
+
+    def get_taup(self):
+        return self.taut
+
+    def set_pressure(self, pressure):
+        self.pressure = pressure
+
+    def get_pressure(self):
+        return self.pressure
+
+    def set_compressibility(self, compressibility):
+        self.compressibility = compressibility
+
+    def get_compressibility(self):
+        return self.compressibility
+
+    def set_timestep(self, timestep):
+        self.dt = timestep
+
+    def get_timestep(self):
+        return self.dt
+
+
+
+    def scale_positions_and_cell(self):
+        """ Do the Berendsen pressure coupling,
+        scale the atom positon and the simulation cell."""
+
+        taupscl = self.dt / self.taup
+        stress = self.atoms.get_stress()
+        old_pressure = self.atoms.get_isotropic_pressure(stress)
+        scl_pressure = 1.0 - taupscl * self.compressibility / 3.0 * \
+                       (self.pressure - old_pressure)
+
+        #print "old_pressure", old_pressure
+        #print "volume scaling by:", scl_pressure
+
+        cell = self.atoms.get_cell()
+        cell = scl_pressure * cell
+        self.atoms.set_cell(cell, scale_atoms=True)
+
+    def step(self, f):
+        """ move one timestep forward using Berenden NPT molecular dynamics."""
+
+        NVTBerendsen.scale_velocities(self)
+        self.scale_positions_and_cell()
+
+        #one step velocity verlet
+        atoms = self.atoms
+        p = self.atoms.get_momenta()
+        p += 0.5 * self.dt * f
+
+        if self.fixcm:
+            # calculate the center of mass
+            # momentum and subtract it
+            psum = p.sum(axis=0) / float(len(p))
+            p = p - psum
+
+        self.atoms.set_positions(self.atoms.get_positions() +
+             self.dt * p / self.atoms.get_masses()[:,np.newaxis])
+
+        # We need to store the momenta on the atoms before calculating
+        # the forces, as in a parallel Asap calculation atoms may
+        # migrate during force calculations, and the momenta need to
+        # migrate along with the atoms.  For the same reason, we
+        # cannot use self.masses in the line above.
+
+        self.atoms.set_momenta(p)
+        f = self.atoms.get_forces()
+        atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * f)
+
+
+        return f
+
+class Inhomogenous_NPTBerendsen(NPTBerendsen):
+    """Berendsen (constant N, P, T) molecular dynamics.
+    
+    This dynamics scale the velocities and volumes to maintain a constant
+    pressure and temperature.  The size of the unit cell is allowed to change
+    independently in the three directions, but the angles remain constant.
+
+    Usage: NPTBerendsen(atoms, timestep, temperature, taut, pressure, taup)
+
+    atoms
+        The list of atoms.
+        
+    timestep
+        The time step.
+
+    temperature
+        The desired temperature, in Kelvin.
+
+    taut
+        Time constant for Berendsen temperature coupling.
+
+    fixcm
+        If True, the position and momentum of the center of mass is
+        kept unperturbed.  Default: True.
+
+    pressure
+        The desired pressure, in bar (1 bar = 1e5 Pa).
+
+    taup
+        Time constant for Berendsen pressure coupling.
+
+    compressibility
+        The compressibility of the material, water 4.57E-5 bar-1, in bar-1
+
+    """
+    def scale_positions_and_cell(self):
+        """ Do the Berendsen pressure coupling,
+        scale the atom positon and the simulation cell."""
+
+        taupscl = self.dt  * self.compressibility / self.taup / 3.0
+        stress = - self.atoms.get_stress() * 1e-5 / units.Pascal
+        if stress.shape == (6,):
+            stress = stress[:3]
+        elif stress.shape == (3,3):
+            stress = [stress[i][i] for i in range(3)]
+        else:
+            raise ValueError("Cannot use a stress tensor of shape " + str(stress.shape))
+        scl_pressurex = 1.0 - taupscl * (self.pressure - stress[0])
+        scl_pressurey = 1.0 - taupscl * (self.pressure - stress[1])
+        scl_pressurez = 1.0 - taupscl * (self.pressure - stress[2])
+
+        cell = self.atoms.get_cell()
+        cell = np.array([scl_pressurex * cell[0],scl_pressurey * cell[1],scl_pressurez * cell[2]])
+        self.atoms.set_cell(cell, scale_atoms=True)
+
+
diff --git a/ase/md/nvtberendsen.py b/ase/md/nvtberendsen.py
new file mode 100644
index 0000000..bee6f28
--- /dev/null
+++ b/ase/md/nvtberendsen.py
@@ -0,0 +1,110 @@
+"""Berendsen NVT dynamics class."""
+
+import sys
+import numpy as np
+from ase.md.md import MolecularDynamics
+from ase.parallel import world
+
+
+class NVTBerendsen(MolecularDynamics):
+    """Berendsen (constant N, V, T) molecular dynamics.
+
+    Usage: NVTBerendsen(atoms, timestep, temperature, taut, fixcm)
+
+    atoms
+        The list of atoms.
+        
+    timestep
+        The time step.
+
+    temperature
+        The desired temperature, in Kelvin.
+
+    taut
+        Time constant for Berendsen temperature coupling.
+
+    fixcm
+        If True, the position and momentum of the center of mass is
+        kept unperturbed.  Default: True.
+
+    """
+
+    def __init__(self, atoms, timestep, temperature, taut, fixcm=True,
+                 trajectory=None, logfile=None, loginterval=1,
+                 communicator=world):
+
+        MolecularDynamics.__init__(self, atoms, timestep, trajectory, 
+                                   logfile, loginterval)
+        self.taut = taut
+        self.temperature = temperature
+        self.fixcm = fixcm  # will the center of mass be held fixed?
+        self.communicator = communicator
+
+    def set_taut(self, taut):
+        self.taut = taut
+
+    def get_taut(self):
+        return self.taut
+
+    def set_temperature(self, temperature):
+        self.temperature = temperature
+
+    def get_temperature(self):
+        return self.temperature
+
+    def set_timestep(self, timestep):
+        self.dt = timestep
+
+    def get_timestep(self):
+        return self.dt
+
+    def scale_velocities(self):
+        """ Do the NVT Berendsen velocity scaling """
+        tautscl = self.dt / self.taut
+        old_temperature = self.atoms.get_temperature()
+
+        scl_temperature = np.sqrt(1.0+ (self.temperature/ old_temperature- 1.0)
+                                  *tautscl)
+        #limit the velocity scaling to reasonable values
+        if scl_temperature > 1.1:
+            scl_temperature = 1.1
+        if scl_temperature < 0.9:
+            scl_temperature = 0.9
+        
+        atoms = self.atoms
+        p = self.atoms.get_momenta()
+        p = scl_temperature * p 
+        self.atoms.set_momenta(p)
+        return 
+
+
+    def step(self, f):
+        """ move one timestep forward using Berenden NVT molecular dynamics."""
+        self.scale_velocities()
+
+        #one step velocity verlet
+        atoms = self.atoms
+        p = self.atoms.get_momenta()
+        p += 0.5 * self.dt * f
+
+        if self.fixcm:
+            # calculate the center of mass
+            # momentum and subtract it
+            psum = p.sum(axis=0) / float(len(p))
+            p = p - psum
+
+        self.atoms.set_positions(self.atoms.get_positions() +
+             self.dt * p / self.atoms.get_masses()[:,np.newaxis])
+
+        # We need to store the momenta on the atoms before calculating
+        # the forces, as in a parallel Asap calculation atoms may
+        # migrate during force calculations, and the momenta need to
+        # migrate along with the atoms.  For the same reason, we
+        # cannot use self.masses in the line above.
+
+        self.atoms.set_momenta(p)
+        f = self.atoms.get_forces()
+        atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * f)
+
+        return f
+
diff --git a/ase/md/velocitydistribution.py b/ase/md/velocitydistribution.py
new file mode 100644
index 0000000..109f184
--- /dev/null
+++ b/ase/md/velocitydistribution.py
@@ -0,0 +1,58 @@
+# VelocityDistributions.py -- set up a velocity distribution
+
+"""Module for setting up e.g. Maxwell-Boltzmann velocity distributions.
+
+Currently, only one function is defined, MaxwellBoltzmannDistribution,
+which sets the momenta of a list of atoms according to a
+Maxwell-Boltzmann distribution at a given temperature.
+"""
+
+import sys
+import numpy as np
+from ase.parallel import world
+
+
+def _maxwellboltzmanndistribution(masses, temp, communicator=world):
+    # For parallel GPAW simulations, the random velocities should be
+    # distributed.  Uses gpaw world communicator as default, but allow
+    # option of specifying other communicator (for ensemble runs)
+    xi = np.random.standard_normal((len(masses), 3))
+    communicator.broadcast(xi, 0)
+    momenta = xi * np.sqrt(masses * temp)[:, np.newaxis]
+    return momenta
+
+
+def MaxwellBoltzmannDistribution(atoms, temp, communicator=world):
+    """Sets the momenta to a Maxwell-Boltzmann distribution."""
+    momenta = _maxwellboltzmanndistribution(atoms.get_masses(), temp,
+                                            communicator)
+    atoms.set_momenta(momenta)
+
+
+def Stationary(atoms):
+    "Sets the center-of-mass momentum to zero."
+    p = atoms.get_momenta()
+    p0 = np.sum(p, 0)
+    # We should add a constant velocity, not momentum, to the atoms
+    m = atoms.get_masses()
+    mtot = np.sum(m)
+    v0 = p0 / mtot
+    p -= v0 * m[:, np.newaxis]
+    atoms.set_momenta(p)
+
+
+def ZeroRotation(atoms):
+    "Sets the total angular momentum to zero by counteracting rigid rotations."
+    # Find the principal moments of inertia and principal axes basis vectors   
+    Ip, basis = atoms.get_moments_of_inertia(vectors=True)
+    # Calculate the total angular momentum and transform to principal basis
+    Lp = np.dot(basis, atoms.get_angular_momentum())
+    # Calculate the rotation velocity vector in the principal basis, avoiding
+    # zero division, and transform it back to the cartesian coordinate system
+    omega = np.dot(np.linalg.inv(basis), np.select([Ip > 0], [Lp / Ip]))
+    # We subtract a rigid rotation corresponding to this rotation vector
+    com = atoms.get_center_of_mass()
+    positions = atoms.get_positions()
+    positions -= com  # translate center of mass to origin
+    velocities = atoms.get_velocities()
+    atoms.set_velocities(velocities - np.cross(omega, positions))
diff --git a/ase/md/verlet.py b/ase/md/verlet.py
new file mode 100644
index 0000000..5534daf
--- /dev/null
+++ b/ase/md/verlet.py
@@ -0,0 +1,25 @@
+import numpy as np
+
+from ase.md.md import MolecularDynamics
+
+
+class VelocityVerlet(MolecularDynamics):
+    def __init__(self, atoms, dt, trajectory=None, logfile=None,
+                 loginterval=1):
+        MolecularDynamics.__init__(self, atoms, dt, trajectory, logfile,
+                                   loginterval)
+            
+    def step(self, f):
+        p = self.atoms.get_momenta()
+        p += 0.5 * self.dt * f
+        self.atoms.set_positions(self.atoms.get_positions() +
+            self.dt * p / self.atoms.get_masses()[:,np.newaxis])
+        # We need to store the momenta on the atoms before calculating
+        # the forces, as in a parallel Asap calculation atoms may
+        # migrate during force calculations, and the momenta need to
+        # migrate along with the atoms.  For the same reason, we
+        # cannot use self.masses in the line above.
+        self.atoms.set_momenta(p)
+        f = self.atoms.get_forces()
+        self.atoms.set_momenta(self.atoms.get_momenta() + 0.5 * self.dt * f)
+        return f
diff --git a/ase/neb.py b/ase/neb.py
new file mode 100644
index 0000000..0848fc7
--- /dev/null
+++ b/ase/neb.py
@@ -0,0 +1,309 @@
+from math import sqrt
+
+import numpy as np
+
+import ase.parallel as mpi
+from ase.calculators.singlepoint import SinglePointCalculator
+from ase.io import read
+
+
+class NEB:
+    def __init__(self, images, k=0.1, climb=False, parallel=False,
+                 world=None):
+        self.images = images
+        self.k = k
+        self.climb = climb
+        self.parallel = parallel
+        self.natoms = len(images[0])
+        self.nimages = len(images)
+        self.emax = np.nan
+
+        if world is None:
+            world = mpi.world
+        self.world = world
+
+        assert not parallel or world.size % (self.nimages - 2) == 0
+
+    def interpolate(self):
+        pos1 = self.images[0].get_positions()
+        pos2 = self.images[-1].get_positions()
+        d = (pos2 - pos1) / (self.nimages - 1.0)
+        for i in range(1, self.nimages - 1):
+            self.images[i].set_positions(pos1 + i * d)
+            # Parallel NEB with Jacapo needs this:
+            try:
+                self.images[i].get_calculator().set_atoms(self.images[i])
+            except AttributeError:
+                pass
+            
+    def get_positions(self):
+        positions = np.empty(((self.nimages - 2) * self.natoms, 3))
+        n1 = 0
+        for image in self.images[1:-1]:
+            n2 = n1 + self.natoms
+            positions[n1:n2] = image.get_positions()
+            n1 = n2
+        return positions
+
+    def set_positions(self, positions):
+        n1 = 0
+        for image in self.images[1:-1]:
+            n2 = n1 + self.natoms
+            image.set_positions(positions[n1:n2])
+            n1 = n2
+
+            # Parallel NEB with Jacapo needs this:
+            try:
+                image.get_calculator().set_atoms(image)
+            except AttributeError:
+                pass
+        
+    def get_forces(self):
+        """Evaluate and return the forces."""
+        images = self.images
+        forces = np.empty(((self.nimages - 2), self.natoms, 3))
+        energies = np.empty(self.nimages - 2)
+
+        if not self.parallel:
+            # Do all images - one at a time:
+            for i in range(1, self.nimages - 1):
+                energies[i - 1] = images[i].get_potential_energy()
+                forces[i - 1] = images[i].get_forces()
+        else:
+            # Parallelize over images:
+            i = self.world.rank * (self.nimages - 2) // self.world.size + 1
+            try:
+                energies[i - 1] = images[i].get_potential_energy()
+                forces[i - 1] = images[i].get_forces()
+            except:
+                # Make sure other images also fail:
+                error = self.world.sum(1.0)
+                raise
+            else:
+                error = self.world.sum(0.0)
+                if error:
+                    raise RuntimeError('Parallel NEB failed!')
+                
+            for i in range(1, self.nimages - 1):
+                root = (i - 1) * self.world.size // (self.nimages - 2)
+                self.world.broadcast(energies[i - 1:i], root)
+                self.world.broadcast(forces[i - 1], root)
+
+        imax = 1 + np.argsort(energies)[-1]
+        self.emax = energies[imax - 1]
+        
+        tangent1 = images[1].get_positions() - images[0].get_positions()
+        for i in range(1, self.nimages - 1):
+            tangent2 = (images[i + 1].get_positions() -
+                        images[i].get_positions())
+            if i < imax:
+                tangent = tangent2
+            elif i > imax:
+                tangent = tangent1
+            else:
+                tangent = tangent1 + tangent2
+                
+            tt = np.vdot(tangent, tangent)
+            f = forces[i - 1]
+            ft = np.vdot(f, tangent)
+            if i == imax and self.climb:
+                f -= 2 * ft / tt * tangent
+            else:
+                f -= ft / tt * tangent
+                f -= (np.vdot(tangent1 - tangent2, tangent) *
+                      self.k / tt * tangent)
+                
+            tangent1 = tangent2
+
+        return forces.reshape((-1, 3))
+
+    def get_potential_energy(self):
+        return self.emax
+
+    def __len__(self):
+        return (self.nimages - 2) * self.natoms
+
+
+class SingleCalculatorNEB(NEB):
+    def __init__(self, images, k=0.1, climb=False):
+        if isinstance(images, str):
+            # this is a filename
+            traj = read(images, '0:')
+            images = []
+            for atoms in traj:
+                images.append(atoms)
+
+        NEB.__init__(self, images, k, climb, False)
+        self.calculators = [None] * self.nimages
+        self.energies_ok = False
+ 
+    def interpolate(self, initial=0, final=-1):
+        """Interpolate linearly between initial and final images."""
+        if final < 0:
+            final = self.nimages + final
+        n = final - initial
+        pos1 = self.images[initial].get_positions()
+        pos2 = self.images[final].get_positions()
+        d = (pos2 - pos1) / n
+        for i in range(1, n):
+            self.images[initial + i].set_positions(pos1 + i * d)
+
+    def refine(self, steps=1, begin=0, end=-1):
+        """Refine the NEB trajectory."""
+        if end < 0:
+            end = self.nimages + end
+        j = begin
+        n = end - begin
+        for i in range(n):
+            for k in range(steps):
+                self.images.insert(j + 1, self.images[j].copy())
+                self.calculators.insert(j + 1, None)
+            self.nimages = len(self.images)
+            self.interpolate(j, j + steps + 1)
+            j += steps + 1
+
+    def set_positions(self, positions):
+        # new positions -> new forces
+        if self.energies_ok:
+            # restore calculators
+            self.set_calculators(self.calculators[1:-1])
+        NEB.set_positions(self, positions)
+
+    def get_calculators(self):
+        """Return the original calculators."""
+        calculators = []
+        for i, image in enumerate(self.images):
+            if self.calculators[i] is None:
+                calculators.append(image.get_calculator())
+            else:
+                calculators.append(self.calculators[i])
+        return calculators
+    
+    def set_calculators(self, calculators):
+        """Set new calculators to the images."""
+        self.energies_ok = False
+
+        if not isinstance(calculators, list):
+            calculators = [calculators] * self.nimages
+
+        n = len(calculators)
+        if n == self.nimages:
+            for i in range(self.nimages):
+                self.images[i].set_calculator(calculators[i])   
+        elif n == self.nimages - 2:
+            for i in range(1, self.nimages -1):
+                self.images[i].set_calculator(calculators[i-1])   
+        else:
+            raise RuntimeError(
+                'len(calculators)=%d does not fit to len(images)=%d'
+                % (n, self.nimages))
+
+    def get_energies_and_forces(self, all=False):
+        """Evaluate energies and forces and hide the calculators"""
+        if self.energies_ok:
+            return
+
+        images = self.images
+        forces = np.zeros(((self.nimages - 2), self.natoms, 3))
+        energies = np.zeros(self.nimages - 2)
+        self.emax = -1.e32
+
+        def calculate_and_hide(i):
+            image = self.images[i]
+            calc = image.get_calculator()
+            if self.calculators[i] is None:
+                self.calculators[i] = calc
+            if calc is not None:
+                if not isinstance(calc, SinglePointCalculator):
+                    self.images[i].set_calculator(
+                        SinglePointCalculator(image.get_potential_energy(),
+                                              image.get_forces(),
+                                              None,
+                                              None,
+                                              image))
+                self.emax = min(self.emax, image.get_potential_energy())
+
+        if all and self.calculators[0] is None:
+            calculate_and_hide(0)
+
+        # Do all images - one at a time:
+        for i in range(1, self.nimages - 1):
+            calculate_and_hide(i)
+
+        if all and self.calculators[-1] is None:
+            calculate_and_hide(-1)
+
+        self.energies_ok = True
+       
+    def get_forces(self):
+        self.get_energies_and_forces()
+        return NEB.get_forces(self)
+
+    def n(self):
+        return self.nimages
+
+    def write(self, filename):
+        from ase.io.trajectory import PickleTrajectory
+        traj = PickleTrajectory(filename, 'w', self)
+        traj.write()
+        traj.close()
+
+    def __add__(self, other):
+        for image in other:
+            self.images.append(image)
+        return self
+
+
+def fit(images):
+    E = [i.get_potential_energy() for i in images]
+    F = [i.get_forces() for i in images]
+    R = [i.get_positions() for i in images]
+    return fit0(E, F, R)
+
+
+def fit0(E, F, R):
+    E = np.array(E) - E[0]
+    n = len(E)
+    Efit = np.empty((n - 1) * 20 + 1)
+    Sfit = np.empty((n - 1) * 20 + 1)
+
+    s = [0]
+    for i in range(n - 1):
+        s.append(s[-1] + sqrt(((R[i + 1] - R[i])**2).sum()))
+
+    lines = []
+    for i in range(n):
+        if i == 0:
+            d = R[1] - R[0]
+            ds = 0.5 * s[1]
+        elif i == n - 1:
+            d = R[-1] - R[-2]
+            ds = 0.5 * (s[-1] - s[-2])
+        else:
+            d = R[i + 1] - R[i - 1]
+            ds = 0.25 * (s[i + 1] - s[i - 1])
+
+        d = d / sqrt((d**2).sum())
+        dEds = -(F[i] * d).sum()
+        x = np.linspace(s[i] - ds, s[i] + ds, 3)
+        y = E[i] + dEds * (x - s[i])
+        lines.append((x, y))
+
+        if i > 0:
+            s0 = s[i - 1]
+            s1 = s[i]
+            x = np.linspace(s0, s1, 20, endpoint=False)
+            c = np.linalg.solve(np.array([(1, s0,   s0**2,     s0**3),
+                                          (1, s1,   s1**2,     s1**3),
+                                          (0,  1,  2 * s0, 3 * s0**2),
+                                          (0,  1,  2 * s1, 3 * s1**2)]),
+                                np.array([E[i - 1], E[i], dEds0, dEds]))
+            y = c[0] + x * (c[1] + x * (c[2] + x * c[3]))
+            Sfit[(i - 1) * 20:i * 20] = x
+            Efit[(i - 1) * 20:i * 20] = y
+        
+        dEds0 = dEds
+
+    Sfit[-1] = s[-1]
+    Efit[-1] = E[-1]
+    return s, E, Sfit, Efit, lines
diff --git a/ase/old.py b/ase/old.py
new file mode 100644
index 0000000..1a60476
--- /dev/null
+++ b/ase/old.py
@@ -0,0 +1,194 @@
+import numpy as np
+try:
+    import Numeric as num
+except ImportError:
+    pass
+else:
+    def npy2num(a, typecode=num.Float):
+        return num.array(a, typecode)
+    if num.__version__ <= '23.8':
+        #def npy2num(a, typecode=num.Float):
+        #    return num.array(a.tolist(), typecode)
+        def npy2num(a, typecode=num.Float):
+            b = num.fromstring(a.tostring(), typecode)
+            b.shape = a.shape
+            return b
+    
+from ase.data import chemical_symbols
+
+
+class OldASEListOfAtomsWrapper:
+    def __init__(self, atoms):
+        self.atoms = atoms
+        self.constraints = []
+
+    def get_positions(self):
+        return np.array(self.atoms.GetCartesianPositions())
+
+    def get_calculator(self):
+        calc = self.atoms.GetCalculator()
+        if calc is not None:
+            return OldASECalculatorWrapper(calc)
+
+    def get_potential_energy(self):
+        return self.atoms.GetPotentialEnergy()
+
+    def get_forces(self):
+        return np.array(self.atoms.GetCartesianForces())
+
+    def get_stress(self):
+        return np.array(self.atoms.GetStress())
+
+    def get_atomic_numbers(self):
+        return np.array(self.atoms.GetAtomicNumbers())
+
+    def get_tags(self):
+        return np.array(self.atoms.GetTags())
+    
+    def get_momenta(self):
+        return np.array(self.atoms.GetCartesianMomenta())
+    
+    def get_masses(self):
+        return np.array(self.atoms.GetMasses())
+    
+    def get_initial_magnetic_moments(self):
+        return np.array(self.atoms.GetMagneticMoments())
+    
+    def get_magnetic_moments(self):
+        return None
+    
+    def get_charges(self):
+        return np.zeros(len(self))
+
+    def has(self, name):
+        return True
+    
+    def get_cell(self):
+        return np.array(self.atoms.GetUnitCell())
+
+    def get_pbc(self):
+        return np.array(self.atoms.GetBoundaryConditions(), bool)
+
+    def __len__(self):
+        return len(self.atoms)
+
+    def copy(self):
+        from ase.atoms import Atoms
+        return Atoms(positions=self.get_positions(),
+                     numbers=self.get_atomic_numbers(),
+                     tags=self.get_tags(),
+                     momenta=self.get_momenta(),
+                     masses=self.get_masses(),
+                     magmoms=self.get_initial_magnetic_moments(),
+                     charges=self.get_charges(),
+                     cell=self.get_cell(),
+                     pbc=self.get_pbc(),
+                     constraint=None,
+                     calculator=None) # Don't copy the calculator
+
+
+class OldASECalculatorWrapper:
+    def __init__(self, calc, atoms=None):
+        self.calc = calc
+            
+        if atoms is None:
+            try:
+                self.atoms = calc.GetListOfAtoms()
+            except AttributeError:
+                self.atoms = None
+        else:
+            from ASE import Atom, ListOfAtoms
+            
+            numbers = atoms.get_atomic_numbers()
+            positions = atoms.get_positions()
+            magmoms = atoms.get_initial_magnetic_moments()
+            self.atoms = ListOfAtoms(
+                [Atom(Z=numbers[a], position=positions[a], magmom=magmoms[a])
+                 for a in range(len(atoms))],
+                cell=npy2num(atoms.get_cell()),
+                periodic=tuple(atoms.get_pbc()))
+            self.atoms.SetCalculator(calc)
+
+    def get_atoms(self):
+        return OldASEListOfAtomsWrapper(self.atoms)
+            
+    def get_potential_energy(self, atoms):
+        self.atoms.SetCartesianPositions(npy2num(atoms.get_positions()))
+        self.atoms.SetUnitCell(npy2num(atoms.get_cell()), fix=True)
+        return self.calc.GetPotentialEnergy()
+
+    def get_forces(self, atoms):
+        self.atoms.SetCartesianPositions(npy2num(atoms.get_positions()))
+        self.atoms.SetUnitCell(npy2num(atoms.get_cell()), fix=True)
+        return np.array(self.calc.GetCartesianForces())
+
+    def get_stress(self, atoms):
+        self.atoms.SetCartesianPositions(npy2num(atoms.get_positions()))
+        self.atoms.SetUnitCell(npy2num(atoms.get_cell()), fix=True)
+        return np.array(self.calc.GetStress())
+
+    def get_number_of_bands(self):
+        return self.calc.GetNumberOfBands()
+
+    def get_kpoint_weights(self):
+        return np.array(self.calc.GetIBZKPointWeights())
+
+    def get_number_of_spins(self):
+        return 1 + int(self.calc.GetSpinPolarized())
+
+    def get_eigenvalues(self, kpt=0, spin=0):
+        return np.array(self.calc.GetEigenvalues(kpt, spin))
+
+    def get_fermi_level(self):
+        return self.calc.GetFermiLevel()
+
+    def get_number_of_grid_points(self):
+        return np.array(self.get_pseudo_wave_function(0, 0, 0).shape)
+
+    def get_pseudo_wave_function(self, n=0, k=0, s=0, pad=True):
+        kpt = self.get_bz_k_points()[k]
+        state = self.calc.GetElectronicStates().GetState(band=n, spin=s,
+                                                         kptindex=k)
+
+        # Get wf, without bolch phase (Phase = True doesn't do anything!)
+        wave = state.GetWavefunctionOnGrid(phase=False)
+
+        # Add bloch phase if this is not the Gamma point
+        if np.all(kpt == 0):
+            return wave
+        coord = state.GetCoordinates()
+        phase = coord[0] * kpt[0] + coord[1] * kpt[1] + coord[2] * kpt[2]
+        return np.array(wave) * np.exp(-2.j * np.pi * phase) # sign! XXX
+
+        #return np.array(self.calc.GetWaveFunctionArray(n, k, s)) # No phase!
+
+    def get_bz_k_points(self):
+        return np.array(self.calc.GetBZKPoints())
+
+    def get_ibz_k_points(self):
+        return np.array(self.calc.GetIBZKPoints())
+
+    def get_wannier_localization_matrix(self, nbands, dirG, kpoint,
+                                        nextkpoint, G_I, spin):
+        return np.array(self.calc.GetWannierLocalizationMatrix(
+            G_I=G_I.tolist(), nbands=nbands, dirG=dirG.tolist(),
+            kpoint=kpoint, nextkpoint=nextkpoint, spin=spin))
+    
+    def initial_wannier(self, initialwannier, kpointgrid, fixedstates,
+                        edf, spin):
+        # Use initial guess to determine U and C
+        init = self.calc.InitialWannier(initialwannier, self.atoms,
+                                        npy2num(kpointgrid, num.Int))
+
+        states = self.calc.GetElectronicStates()
+        waves = [[state.GetWaveFunction()
+                  for state in states.GetStatesKPoint(k, spin)]
+                 for k in self.calc.GetIBZKPoints()] 
+
+        init.SetupMMatrix(waves, self.calc.GetBZKPoints())
+        c, U = init.GetListOfCoefficientsAndRotationMatrices(
+            (self.calc.GetNumberOfBands(), fixedstates, edf))
+        U = np.array(U)
+        for k in range(len(c)):
+            c[k] = np.array(c[k])
+        return c, U
diff --git a/ase/optimize/__init__.py b/ase/optimize/__init__.py
new file mode 100644
index 0000000..2d2de78
--- /dev/null
+++ b/ase/optimize/__init__.py
@@ -0,0 +1,11 @@
+"""Structure optimization. """
+
+from ase.optimize.optimize import NDPoly, polyfit
+from ase.optimize.mdmin import MDMin
+from ase.optimize.lbfgs import HessLBFGS, LineLBFGS
+from ase.optimize.fire import FIRE
+from ase.optimize.lbfgs import LBFGS, LBFGSLineSearch
+from ase.optimize.bfgslinesearch import BFGSLineSearch
+from ase.optimize.bfgs import BFGS
+
+QuasiNewton = BFGSLineSearch
diff --git a/ase/optimize/basin.py b/ase/optimize/basin.py
new file mode 100644
index 0000000..6416bcb
--- /dev/null
+++ b/ase/optimize/basin.py
@@ -0,0 +1,124 @@
+import numpy as np
+
+from ase.optimize.optimize import Dynamics
+from ase.optimize.fire import FIRE
+from ase.units import kB
+from ase.parallel import world
+from ase.io.trajectory import PickleTrajectory
+
+
+class BasinHopping(Dynamics):
+    """Basin hopping algorythm.
+
+    After Wales and Doye, J. Phys. Chem. A, vol 101 (1997) 5111-5116"""
+
+    def __init__(self, atoms,
+                 temperature=100 * kB,
+                 optimizer=FIRE,
+                 fmax=0.1,
+                 dr=0.1,
+                 logfile='-', 
+                 trajectory='lowest.traj',
+                 optimizer_logfile='-',
+                 local_minima_trajectory='local_minima.traj',
+                 adjust_cm=True):
+        Dynamics.__init__(self, atoms, logfile, trajectory)
+        self.kT = temperature
+        self.optimizer = optimizer
+        self.fmax = fmax
+        self.dr = dr
+        if adjust_cm:
+            self.cm = atoms.get_center_of_mass()
+        else:
+            self.cm = None
+
+        self.optimizer_logfile = optimizer_logfile
+        self.lm_trajectory = local_minima_trajectory
+        if isinstance(local_minima_trajectory, str):
+            self.lm_trajectory = PickleTrajectory(local_minima_trajectory,
+                                                  'w', atoms)
+
+        self.initialize()
+
+    def initialize(self):
+        self.positions = 0.0 * self.atoms.get_positions()
+        self.Emin = self.get_energy(self.atoms.get_positions()) or 1.e32
+        self.rmin = self.atoms.get_positions()
+        self.positions = self.atoms.get_positions()
+        self.call_observers()
+        self.log(-1, self.Emin, self.Emin)
+                
+    def run(self, steps):
+        """Hop the basins for defined number of steps."""
+
+        ro = self.positions
+        Eo = self.get_energy(ro)
+        En = None
+ 
+        for step in range(steps):
+            while En is None:
+                rn = self.move(ro)
+                En = self.get_energy(rn)
+
+            if En < self.Emin:
+                # new minimum found
+                self.Emin = En
+                self.rmin = self.atoms.get_positions()
+                self.call_observers()
+                rn = self.rmin
+            self.log(step, En, self.Emin)
+
+            accept = np.exp((Eo - En) / self.kT) > np.random.uniform()
+            if accept:
+                ro = rn
+                Eo = En
+
+    def log(self, step, En, Emin):
+        if self.logfile is None:
+            return
+        name = self.__class__.__name__
+        self.logfile.write('%s: step %d, energy %15.6f, emin %15.6f\n'
+                           % (name, step, En, Emin))
+        self.logfile.flush()
+
+    def move(self, ro):
+        """Move atoms by a random step."""
+        atoms = self.atoms
+        # displace coordinates
+        disp = np.random.uniform(-1., 1., (len(atoms), 3))
+        rn = ro + self.dr * disp
+        atoms.set_positions(rn)
+        if self.cm is not None:
+            cm = atoms.get_center_of_mass()
+            atoms.translate(self.cm - cm)
+        rn = atoms.get_positions()
+        world.broadcast(rn, 0)
+        atoms.set_positions(rn)
+        return atoms.get_positions()
+
+    def get_minimum(self):
+        """Return minimal energy and configuration."""
+        atoms = self.atoms.copy()
+        atoms.set_positions(self.rmin)
+        return self.Emin, atoms
+
+    def get_energy(self, positions):
+        """Return the energy of the nearest local minimum."""
+        if np.sometrue(self.positions != positions):
+            self.positions = positions
+            self.atoms.set_positions(positions)
+ 
+            try:
+                opt = self.optimizer(self.atoms, logfile=self.optimizer_logfile)
+                opt.run(fmax=self.fmax)
+                if self.lm_trajectory is not None:
+                    self.lm_trajectory.write(self.atoms)
+
+                self.energy = self.atoms.get_potential_energy()
+            except:
+                # Something went wrong.
+                # In GPAW the atoms are probably to near to each other.
+                return None
+            
+        return self.energy
+       
diff --git a/ase/optimize/bfgs.py b/ase/optimize/bfgs.py
new file mode 100644
index 0000000..aefdfbd
--- /dev/null
+++ b/ase/optimize/bfgs.py
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+import numpy as np
+from numpy.linalg import eigh, solve
+
+from ase.optimize.optimize import Optimizer
+from ase.parallel import rank, barrier
+
+class BFGS(Optimizer):
+    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
+                 maxstep=None):
+        """BFGS optimizer.
+
+        Parameters:
+
+        restart: string
+            Pickle file used to store hessian matrix. If set, file with 
+            such a name will be searched and hessian matrix stored will
+            be used, if the file exists.
+        trajectory: string
+            Pickle file used to store trajectory of atomic movement.
+        maxstep: float
+            Used to set the maximum distance an atom can move per
+            iteration (default value is 0.04 Å).
+        """
+        
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+        if maxstep is not None:
+            if maxstep > 1.0:
+                raise ValueError('You are using a much too large value for ' +
+                                 'the maximum step size: %.1f Å' % maxstep)
+            self.maxstep = maxstep
+
+    def initialize(self):
+        self.H = None
+        self.r0 = None
+        self.f0 = None
+        self.maxstep = 0.04
+
+    def read(self):
+        self.H, self.r0, self.f0, self.maxstep = self.load()
+
+    def step(self, f):
+        atoms = self.atoms
+        r = atoms.get_positions()
+        f = f.reshape(-1)
+        self.update(r.flat, f, self.r0, self.f0)
+        omega, V = eigh(self.H)
+        dr = np.dot(V, np.dot(f, V) / np.fabs(omega)).reshape((-1, 3))
+        steplengths = (dr**2).sum(1)**0.5
+        dr = self.determine_step(dr, steplengths)
+        atoms.set_positions(r + dr)
+        self.r0 = r.flat.copy()
+        self.f0 = f.copy()
+        self.dump((self.H, self.r0, self.f0, self.maxstep))
+
+    def determine_step(self, dr, steplengths):
+        """Determine step to take according to maxstep
+        
+        Normalize all steps as the largest step. This way
+        we still move along the eigendirection.
+        """
+        maxsteplength = np.max(steplengths)
+        if maxsteplength >= self.maxstep:
+            dr *= self.maxstep / maxsteplength
+        
+        return dr
+
+    def update(self, r, f, r0, f0):
+        if self.H is None:
+            self.H = np.eye(3 * len(self.atoms)) * 70.0
+            return
+        dr = r - r0
+
+        if np.abs(dr).max() < 1e-7:
+            # Same configuration again (maybe a restart):
+            return
+
+        df = f - f0
+        a = np.dot(dr, df)
+        dg = np.dot(self.H, dr)
+        b = np.dot(dr, dg)
+        self.H -= np.outer(df, df) / a + np.outer(dg, dg) / b
+
+    def replay_trajectory(self, traj):
+        """Initialize hessian from old trajectory."""
+        if isinstance(traj, str):
+            from ase.io.trajectory import PickleTrajectory
+            traj = PickleTrajectory(traj, 'r')
+        self.H = None
+        atoms = traj[0]
+        r0 = atoms.get_positions().ravel()
+        f0 = atoms.get_forces().ravel()
+        for atoms in traj:
+            r = atoms.get_positions().ravel()
+            f = atoms.get_forces().ravel()
+            self.update(r, f, r0, f0)
+            r0 = r
+            f0 = f
+
+        self.r0 = r0
+        self.f0 = f0
+
+class oldBFGS(BFGS):
+    def determine_step(self, dr, steplengths):
+        """Old BFGS behaviour for scaling step lengths
+
+        This keeps the behaviour of truncating individual steps. Some might
+        depend of this as some absurd kind of stimulated annealing to find the
+        global minimum.
+        """
+        dr /= np.maximum(steplengths / self.maxstep, 1.0).reshape(-1, 1)
+        return dr
diff --git a/ase/optimize/bfgslinesearch.py b/ase/optimize/bfgslinesearch.py
new file mode 100644
index 0000000..e9657c3
--- /dev/null
+++ b/ase/optimize/bfgslinesearch.py
@@ -0,0 +1,192 @@
+#__docformat__ = "restructuredtext en"
+# ******NOTICE***************
+# optimize.py module by Travis E. Oliphant
+#
+# You may copy and use this module as you see fit with no
+# guarantee implied provided you keep this notice in all copies.
+# *****END NOTICE************
+
+import numpy as np
+from numpy import atleast_1d, eye, mgrid, argmin, zeros, shape, empty, \
+     squeeze, vectorize, asarray, absolute, sqrt, Inf, asfarray, isinf
+from ase.utils.linesearch import LineSearch
+from ase.optimize.optimize import Optimizer
+from numpy import arange
+
+
+# These have been copied from Numeric's MLab.py
+# I don't think they made the transition to scipy_core
+
+# Modified from scipy_optimize
+abs = absolute
+import __builtin__
+pymin = __builtin__.min
+pymax = __builtin__.max
+__version__="0.1"
+
+class BFGSLineSearch(Optimizer):
+    def __init__(self, atoms, restart=None, logfile='-', maxstep=.2,
+                 trajectory=None, c1=.23, c2=0.46, alpha=10., stpmax=50.,
+                 use_free_energy=True):
+        """Minimize a function using the BFGS algorithm.
+
+        Notes:
+
+            Optimize the function, f, whose gradient is given by fprime
+            using the quasi-Newton method of Broyden, Fletcher, Goldfarb,
+            and Shanno (BFGS) See Wright, and Nocedal 'Numerical
+            Optimization', 1999, pg. 198.
+
+        *See Also*:
+
+          scikits.openopt : SciKit which offers a unified syntax to call
+                            this and other solvers.
+
+        """
+        self.maxstep = maxstep
+        self.stpmax = stpmax
+        self.alpha = alpha
+        self.H = None
+        self.c1 = c1
+        self.c2 = c2
+        self.force_calls = 0
+        self.function_calls = 0
+        self.r0 = None
+        self.g0 = None
+        self.e0 = None
+        self.load_restart = False
+        self.task = 'START'
+        self.rep_count = 0
+        self.p = None
+        self.alpha_k = None
+        self.no_update = False
+        self.replay = False
+        self.use_free_energy = use_free_energy
+
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+    def read(self):
+        self.r0, self.g0, self.e0, self.task, self.H = self.load()
+        self.load_restart = True    
+
+    def reset(self):
+        print 'reset'
+        self.H = None
+        self.r0 = None
+        self.g0 = None
+        self.e0 = None
+        self.rep_count = 0
+          
+
+    def step(self, f):
+        atoms = self.atoms
+        from ase.neb import NEB 
+        assert not isinstance(atoms, NEB) 
+        r = atoms.get_positions()
+        r = r.reshape(-1)
+        g = -f.reshape(-1) / self.alpha
+        p0 = self.p
+        self.update(r, g, self.r0, self.g0, p0)
+        #o,v = np.linalg.eigh(self.B)
+        e = self.func(r)
+
+        self.p = -np.dot(self.H,g)
+        p_size = np.sqrt((self.p **2).sum())
+        if self.nsteps != 0:
+            p0_size = np.sqrt((p0 **2).sum())
+            delta_p = self.p/p_size + p0/p0_size
+        if p_size <= np.sqrt(len(atoms) * 1e-10):
+            self.p /= (p_size / np.sqrt(len(atoms)*1e-10))
+        ls = LineSearch()
+        self.alpha_k, e, self.e0, self.no_update = \
+           ls._line_search(self.func, self.fprime, r, self.p, g, e, self.e0,
+                           maxstep=self.maxstep, c1=self.c1,
+                           c2=self.c2, stpmax=self.stpmax)
+        if self.alpha_k is None:
+            raise RuntimeError("LineSearch failed!")
+
+        dr = self.alpha_k * self.p
+        atoms.set_positions((r+dr).reshape(len(atoms),-1))
+        self.r0 = r
+        self.g0 = g
+        self.dump((self.r0, self.g0, self.e0, self.task, self.H))
+
+    def update(self, r, g, r0, g0, p0):
+        self.I = eye(len(self.atoms) * 3, dtype=int)
+        if self.H is None:
+            self.H = eye(3 * len(self.atoms))
+            #self.B = np.linalg.inv(self.H)
+            return
+        else:
+            dr = r - r0
+            dg = g - g0 
+            if not ((self.alpha_k > 0 and abs(np.dot(g,p0))-abs(np.dot(g0,p0)) < 0) \
+                or self.replay):
+                return
+            if self.no_update == True:
+                print 'skip update'
+                return
+
+            try: # this was handled in numeric, let it remaines for more safety
+                rhok = 1.0 / (np.dot(dg,dr))
+            except ZeroDivisionError:
+                rhok = 1000.0
+                print "Divide-by-zero encountered: rhok assumed large"
+            if isinf(rhok): # this is patch for np
+                rhok = 1000.0
+                print "Divide-by-zero encountered: rhok assumed large"
+            A1 = self.I - dr[:, np.newaxis] * dg[np.newaxis, :] * rhok
+            A2 = self.I - dg[:, np.newaxis] * dr[np.newaxis, :] * rhok
+            H0 = self.H
+            self.H = np.dot(A1, np.dot(self.H, A2)) + rhok * dr[:, np.newaxis] \
+                     * dr[np.newaxis, :]
+            #self.B = np.linalg.inv(self.H)
+
+    def func(self, x):
+        """Objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        calc = self.atoms.get_calculator()
+        self.function_calls += 1
+        # Scale the problem as SciPy uses I as initial Hessian.
+        if self.use_free_energy:
+            try:
+                return calc.get_potential_energy(self.atoms,force_consistent=True) / self.alpha
+            except TypeError:
+                return calc.get_potential_energy(self.atoms) / self.alpha
+        else:
+            return calc.get_potential_energy(self.atoms) / self.alpha
+    
+    def fprime(self, x):
+        """Gradient of the objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        self.force_calls += 1
+        # Remember that forces are minus the gradient!
+        # Scale the problem as SciPy uses I as initial Hessian.
+        f = self.atoms.get_forces().reshape(-1) 
+        return - f / self.alpha
+
+    def replay_trajectory(self, traj):
+        """Initialize hessian from old trajectory."""
+        self.replay = True
+        if isinstance(traj, str):
+            from ase.io.trajectory import PickleTrajectory
+            traj = PickleTrajectory(traj, 'r')
+        atoms = traj[0]
+        r0 = None
+        g0 = None
+        for i in range(0, len(traj) - 1):
+            r = traj[i].get_positions().ravel()
+            g = - traj[i].get_forces().ravel() / self.alpha
+            self.update(r, g, r0, g0, self.p)
+            self.p = -np.dot(self.H,g)
+            r0 = r.copy()
+            g0 = g.copy()
+        self.r0 = r0
+        self.g0 = g0
+
+def wrap_function(function, args):
+    ncalls = [0]
+    def function_wrapper(x):
+        ncalls[0] += 1
+        return function(x, *args)
+    return ncalls, function_wrapper
diff --git a/ase/optimize/fire.py b/ase/optimize/fire.py
new file mode 100644
index 0000000..f267570
--- /dev/null
+++ b/ase/optimize/fire.py
@@ -0,0 +1,55 @@
+import numpy as np
+
+from ase.optimize.optimize import Optimizer
+
+
+class FIRE(Optimizer):
+    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
+                 dt=0.1, maxmove=0.2, dtmax=1.0, Nmin=5, finc=1.1, fdec=0.5,
+                 astart=0.1, fa=0.99, a=0.1):
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+        self.dt = dt
+        self.Nsteps = 0
+        self.maxmove = maxmove
+        self.dtmax = dtmax
+        self.Nmin = Nmin
+        self.finc = finc
+        self.fdec = fdec
+        self.astart = astart
+        self.fa = fa
+        self.a = a
+
+    def initialize(self):
+        self.v = None
+
+    def read(self):
+        self.v, self.dt = self.load()
+       
+    def step(self,f):
+        atoms = self.atoms
+        if self.v is None:
+            self.v = np.zeros((len(atoms), 3))
+        else:
+            vf = np.vdot(f, self.v)
+            if vf > 0.0:
+                self.v = (1.0 - self.a) * self.v + self.a * f / np.sqrt(
+                    np.vdot(f, f)) * np.sqrt(np.vdot(self.v, self.v))
+                if self.Nsteps > self.Nmin:
+                    self.dt = min(self.dt * self.finc, self.dtmax)
+                    self.a *= self.fa
+                self.Nsteps += 1
+            else:
+                self.v[:] *= 0.0
+                self.a = self.astart
+                self.dt *= self.fdec
+                self.Nsteps = 0
+
+        self.v += self.dt * f
+        dr = self.dt * self.v
+        normdr = np.sqrt(np.vdot(dr, dr))
+        if normdr > self.maxmove:
+            dr = self.maxmove * dr / normdr
+        r = atoms.get_positions()
+        atoms.set_positions(r + dr)
+        self.dump((self.v, self.dt))
diff --git a/ase/optimize/fmin_bfgs.py b/ase/optimize/fmin_bfgs.py
new file mode 100644
index 0000000..17d0a4c
--- /dev/null
+++ b/ase/optimize/fmin_bfgs.py
@@ -0,0 +1,477 @@
+#__docformat__ = "restructuredtext en"
+# ******NOTICE***************
+# optimize.py module by Travis E. Oliphant
+#
+# You may copy and use this module as you see fit with no
+# guarantee implied provided you keep this notice in all copies.
+# *****END NOTICE************
+
+import numpy
+from numpy import atleast_1d, eye, mgrid, argmin, zeros, shape, empty, \
+     squeeze, vectorize, asarray, absolute, sqrt, Inf, asfarray, isinf
+from ase.utils.linesearch import LineSearch
+
+# These have been copied from Numeric's MLab.py
+# I don't think they made the transition to scipy_core
+
+# Copied and modified from scipy_optimize
+abs = absolute
+import __builtin__
+pymin = __builtin__.min
+pymax = __builtin__.max
+__version__="0.7"
+_epsilon = sqrt(numpy.finfo(float).eps)
+
+def fmin_bfgs(f, x0, fprime=None, args=(), gtol=1e-5, norm=Inf,
+              epsilon=_epsilon, maxiter=None, full_output=0, disp=1,
+              retall=0, callback=None, maxstep=0.2):
+    """Minimize a function using the BFGS algorithm.
+
+    Parameters:
+
+      f : callable f(x,*args)
+          Objective function to be minimized.
+      x0 : ndarray
+          Initial guess.
+      fprime : callable f'(x,*args)
+          Gradient of f.
+      args : tuple
+          Extra arguments passed to f and fprime.
+      gtol : float
+          Gradient norm must be less than gtol before succesful termination.
+      norm : float
+          Order of norm (Inf is max, -Inf is min)
+      epsilon : int or ndarray
+          If fprime is approximated, use this value for the step size.
+      callback : callable
+          An optional user-supplied function to call after each
+          iteration.  Called as callback(xk), where xk is the
+          current parameter vector.
+
+    Returns: (xopt, {fopt, gopt, Hopt, func_calls, grad_calls, warnflag}, <allvecs>)
+
+        xopt : ndarray
+            Parameters which minimize f, i.e. f(xopt) == fopt.
+        fopt : float
+            Minimum value.
+        gopt : ndarray
+            Value of gradient at minimum, f'(xopt), which should be near 0.
+        Bopt : ndarray
+            Value of 1/f''(xopt), i.e. the inverse hessian matrix.
+        func_calls : int
+            Number of function_calls made.
+        grad_calls : int
+            Number of gradient calls made.
+        warnflag : integer
+            1 : Maximum number of iterations exceeded.
+            2 : Gradient and/or function calls not changing.
+        allvecs  :  list
+            Results at each iteration.  Only returned if retall is True.
+
+    *Other Parameters*:
+        maxiter : int
+            Maximum number of iterations to perform.
+        full_output : bool
+            If True,return fopt, func_calls, grad_calls, and warnflag
+            in addition to xopt.
+        disp : bool
+            Print convergence message if True.
+        retall : bool
+            Return a list of results at each iteration if True.
+
+    Notes:
+
+        Optimize the function, f, whose gradient is given by fprime
+        using the quasi-Newton method of Broyden, Fletcher, Goldfarb,
+        and Shanno (BFGS) See Wright, and Nocedal 'Numerical
+        Optimization', 1999, pg. 198.
+
+    *See Also*:
+
+      scikits.openopt : SciKit which offers a unified syntax to call
+                        this and other solvers.
+
+    """
+    x0 = asarray(x0).squeeze()
+    if x0.ndim == 0:
+        x0.shape = (1,)
+    if maxiter is None:
+        maxiter = len(x0)*200
+    func_calls, f = wrap_function(f, args)
+    if fprime is None:
+        grad_calls, myfprime = wrap_function(approx_fprime, (f, epsilon))
+    else:
+        grad_calls, myfprime = wrap_function(fprime, args)
+    gfk = myfprime(x0)
+    k = 0
+    N = len(x0)
+    I = numpy.eye(N,dtype=int)
+    Hk = I
+    old_fval = f(x0)
+    old_old_fval = old_fval + 5000
+    xk = x0
+    if retall:
+        allvecs = [x0]
+    sk = [2*gtol]
+    warnflag = 0
+    gnorm = vecnorm(gfk,ord=norm)
+    while (gnorm > gtol) and (k < maxiter):
+        pk = -numpy.dot(Hk,gfk)
+        ls = LineSearch()
+        alpha_k, fc, gc, old_fval, old_old_fval, gfkp1 = \
+           ls._line_search(f,myfprime,xk,pk,gfk,
+                                  old_fval,old_old_fval,maxstep=maxstep)
+        if alpha_k is None:  # line search failed try different one.
+            alpha_k, fc, gc, old_fval, old_old_fval, gfkp1 = \
+                     line_search(f,myfprime,xk,pk,gfk,
+                                 old_fval,old_old_fval)
+            if alpha_k is None:
+                # This line search also failed to find a better solution.
+                warnflag = 2
+                break
+        xkp1 = xk + alpha_k * pk
+        if retall:
+            allvecs.append(xkp1)
+        sk = xkp1 - xk
+        xk = xkp1
+        if gfkp1 is None:
+            gfkp1 = myfprime(xkp1)
+
+        yk = gfkp1 - gfk
+        gfk = gfkp1
+        if callback is not None:
+            callback(xk)
+        k += 1
+        gnorm = vecnorm(gfk,ord=norm)
+        if (gnorm <= gtol):
+            break
+
+        try: # this was handled in numeric, let it remaines for more safety
+            rhok = 1.0 / (numpy.dot(yk,sk))
+        except ZeroDivisionError:
+            rhok = 1000.0
+            print "Divide-by-zero encountered: rhok assumed large"
+        if isinf(rhok): # this is patch for numpy
+            rhok = 1000.0
+            print "Divide-by-zero encountered: rhok assumed large"
+        A1 = I - sk[:,numpy.newaxis] * yk[numpy.newaxis,:] * rhok
+        A2 = I - yk[:,numpy.newaxis] * sk[numpy.newaxis,:] * rhok
+        Hk = numpy.dot(A1,numpy.dot(Hk,A2)) + rhok * sk[:,numpy.newaxis] \
+                 * sk[numpy.newaxis,:]
+
+    if disp or full_output:
+        fval = old_fval
+    if warnflag == 2:
+        if disp:
+            print "Warning: Desired error not necessarily achieved" \
+                  "due to precision loss"
+            print "         Current function value: %f" % fval
+            print "         Iterations: %d" % k
+            print "         Function evaluations: %d" % func_calls[0]
+            print "         Gradient evaluations: %d" % grad_calls[0]
+
+    elif k >= maxiter:
+        warnflag = 1
+        if disp:
+            print "Warning: Maximum number of iterations has been exceeded"
+            print "         Current function value: %f" % fval
+            print "         Iterations: %d" % k
+            print "         Function evaluations: %d" % func_calls[0]
+            print "         Gradient evaluations: %d" % grad_calls[0]
+    else:
+        if disp:
+            print "Optimization terminated successfully."
+            print "         Current function value: %f" % fval
+            print "         Iterations: %d" % k
+            print "         Function evaluations: %d" % func_calls[0]
+            print "         Gradient evaluations: %d" % grad_calls[0]
+
+    if full_output:
+        retlist = xk, fval, gfk, Hk, func_calls[0], grad_calls[0], warnflag
+        if retall:
+            retlist += (allvecs,)
+    else:
+        retlist = xk
+        if retall:
+            retlist = (xk, allvecs)
+
+    return retlist
+
+def vecnorm(x, ord=2):
+    if ord == Inf:
+        return numpy.amax(abs(x))
+    elif ord == -Inf:
+        return numpy.amin(abs(x))
+    else:
+        return numpy.sum(abs(x)**ord,axis=0)**(1.0/ord)
+
+def wrap_function(function, args):
+    ncalls = [0]
+    def function_wrapper(x):
+        ncalls[0] += 1
+        return function(x, *args)
+    return ncalls, function_wrapper
+
+def _cubicmin(a,fa,fpa,b,fb,c,fc):
+    # finds the minimizer for a cubic polynomial that goes through the
+    #  points (a,fa), (b,fb), and (c,fc) with derivative at a of fpa.
+    #
+    # if no minimizer can be found return None
+    #
+    # f(x) = A *(x-a)^3 + B*(x-a)^2 + C*(x-a) + D
+
+    C = fpa
+    D = fa
+    db = b-a
+    dc = c-a
+    if (db == 0) or (dc == 0) or (b==c): return None
+    denom = (db*dc)**2 * (db-dc)
+    d1 = empty((2,2))
+    d1[0,0] = dc**2
+    d1[0,1] = -db**2
+    d1[1,0] = -dc**3
+    d1[1,1] = db**3
+    [A,B] = numpy.dot(d1,asarray([fb-fa-C*db,fc-fa-C*dc]).flatten())
+    A /= denom
+    B /= denom
+    radical = B*B-3*A*C
+    if radical < 0:  return None
+    if (A == 0): return None
+    xmin = a + (-B + sqrt(radical))/(3*A)
+    return xmin
+
+def _quadmin(a,fa,fpa,b,fb):
+    # finds the minimizer for a quadratic polynomial that goes through
+    #  the points (a,fa), (b,fb) with derivative at a of fpa
+    # f(x) = B*(x-a)^2 + C*(x-a) + D
+    D = fa
+    C = fpa
+    db = b-a*1.0
+    if (db==0): return None
+    B = (fb-D-C*db)/(db*db)
+    if (B <= 0): return None
+    xmin = a  - C / (2.0*B)
+    return xmin
+
+def zoom(a_lo, a_hi, phi_lo, phi_hi, derphi_lo,
+         phi, derphi, phi0, derphi0, c1, c2):
+    maxiter = 10
+    i = 0
+    delta1 = 0.2  # cubic interpolant check
+    delta2 = 0.1  # quadratic interpolant check
+    phi_rec = phi0
+    a_rec = 0
+    while 1:
+        # interpolate to find a trial step length between a_lo and a_hi
+        # Need to choose interpolation here.  Use cubic interpolation and then if the
+        #  result is within delta * dalpha or outside of the interval bounded by a_lo or a_hi
+        #  then use quadratic interpolation, if the result is still too close, then use bisection
+
+        dalpha = a_hi-a_lo;
+        if dalpha < 0: a,b = a_hi,a_lo
+        else: a,b = a_lo, a_hi
+
+        # minimizer of cubic interpolant
+        #    (uses phi_lo, derphi_lo, phi_hi, and the most recent value of phi)
+        #      if the result is too close to the end points (or out of the interval)
+        #         then use quadratic interpolation with phi_lo, derphi_lo and phi_hi
+        #      if the result is stil too close to the end points (or out of the interval)
+        #         then use bisection
+
+        if (i > 0):
+            cchk = delta1*dalpha
+            a_j = _cubicmin(a_lo, phi_lo, derphi_lo, a_hi, phi_hi, a_rec, phi_rec)
+        if (i==0) or (a_j is None) or (a_j > b-cchk) or (a_j < a+cchk):
+            qchk = delta2*dalpha
+            a_j = _quadmin(a_lo, phi_lo, derphi_lo, a_hi, phi_hi)
+            if (a_j is None) or (a_j > b-qchk) or (a_j < a+qchk):
+                a_j = a_lo + 0.5*dalpha
+#                print "Using bisection."
+#            else: print "Using quadratic."
+#        else: print "Using cubic."
+
+        # Check new value of a_j
+
+        phi_aj = phi(a_j)
+        if (phi_aj > phi0 + c1*a_j*derphi0) or (phi_aj >= phi_lo):
+            phi_rec = phi_hi
+            a_rec = a_hi
+            a_hi = a_j
+            phi_hi = phi_aj
+        else:
+            derphi_aj = derphi(a_j)
+            if abs(derphi_aj) <= -c2*derphi0:
+                a_star = a_j
+                val_star = phi_aj
+                valprime_star = derphi_aj
+                break
+            if derphi_aj*(a_hi - a_lo) >= 0:
+                phi_rec = phi_hi
+                a_rec = a_hi
+                a_hi = a_lo
+                phi_hi = phi_lo
+            else:
+                phi_rec = phi_lo
+                a_rec = a_lo
+            a_lo = a_j
+            phi_lo = phi_aj
+            derphi_lo = derphi_aj
+        i += 1
+        if (i > maxiter):
+            a_star = a_j
+            val_star = phi_aj
+            valprime_star = None
+            break
+    return a_star, val_star, valprime_star
+
+def line_search(f, myfprime, xk, pk, gfk, old_fval, old_old_fval,
+                args=(), c1=1e-4, c2=0.9, amax=50):
+    """Find alpha that satisfies strong Wolfe conditions.
+
+    Parameters:
+
+        f : callable f(x,*args)
+            Objective function.
+        myfprime : callable f'(x,*args)
+            Objective function gradient (can be None).
+        xk : ndarray
+            Starting point.
+        pk : ndarray
+            Search direction.
+        gfk : ndarray
+            Gradient value for x=xk (xk being the current parameter
+            estimate).
+        args : tuple
+            Additional arguments passed to objective function.
+        c1 : float
+            Parameter for Armijo condition rule.
+        c2 : float
+            Parameter for curvature condition rule.
+
+    Returns:
+
+        alpha0 : float
+            Alpha for which ``x_new = x0 + alpha * pk``.
+        fc : int
+            Number of function evaluations made.
+        gc : int
+            Number of gradient evaluations made.
+
+    Notes:
+
+        Uses the line search algorithm to enforce strong Wolfe
+        conditions.  See Wright and Nocedal, 'Numerical Optimization',
+        1999, pg. 59-60.
+
+        For the zoom phase it uses an algorithm by [...].
+
+    """
+
+    global _ls_fc, _ls_gc, _ls_ingfk
+    _ls_fc = 0
+    _ls_gc = 0
+    _ls_ingfk = None
+    def phi(alpha):
+        global _ls_fc
+        _ls_fc += 1
+        return f(xk+alpha*pk,*args)
+
+    if isinstance(myfprime,type(())):
+        def phiprime(alpha):
+            global _ls_fc, _ls_ingfk
+            _ls_fc += len(xk)+1
+            eps = myfprime[1]
+            fprime = myfprime[0]
+            newargs = (f,eps) + args
+            _ls_ingfk = fprime(xk+alpha*pk,*newargs)  # store for later use
+            return numpy.dot(_ls_ingfk,pk)
+    else:
+        fprime = myfprime
+        def phiprime(alpha):
+            global _ls_gc, _ls_ingfk
+            _ls_gc += 1
+            _ls_ingfk = fprime(xk+alpha*pk,*args)  # store for later use
+            return numpy.dot(_ls_ingfk,pk)
+
+    alpha0 = 0
+    phi0 = old_fval
+    derphi0 = numpy.dot(gfk,pk)
+
+    alpha1 = pymin(1.0,1.01*2*(phi0-old_old_fval)/derphi0)
+
+    if alpha1 == 0:
+        # This shouldn't happen. Perhaps the increment has slipped below
+        # machine precision?  For now, set the return variables skip the
+        # useless while loop, and raise warnflag=2 due to possible imprecision.
+        alpha_star = None
+        fval_star = old_fval
+        old_fval = old_old_fval
+        fprime_star = None
+
+    phi_a1 = phi(alpha1)
+    #derphi_a1 = phiprime(alpha1)  evaluated below
+
+    phi_a0 = phi0
+    derphi_a0 = derphi0
+
+    i = 1
+    maxiter = 10
+    while 1:         # bracketing phase
+        if alpha1 == 0:
+            break
+        if (phi_a1 > phi0 + c1*alpha1*derphi0) or \
+           ((phi_a1 >= phi_a0) and (i > 1)):
+            alpha_star, fval_star, fprime_star = \
+                        zoom(alpha0, alpha1, phi_a0,
+                             phi_a1, derphi_a0, phi, phiprime,
+                             phi0, derphi0, c1, c2)
+            break
+
+        derphi_a1 = phiprime(alpha1)
+        if (abs(derphi_a1) <= -c2*derphi0):
+            alpha_star = alpha1
+            fval_star = phi_a1
+            fprime_star = derphi_a1
+            break
+
+        if (derphi_a1 >= 0):
+            alpha_star, fval_star, fprime_star = \
+                        zoom(alpha1, alpha0, phi_a1,
+                             phi_a0, derphi_a1, phi, phiprime,
+                             phi0, derphi0, c1, c2)
+            break
+
+        alpha2 = 2 * alpha1   # increase by factor of two on each iteration
+        i = i + 1
+        alpha0 = alpha1
+        alpha1 = alpha2
+        phi_a0 = phi_a1
+        phi_a1 = phi(alpha1)
+        derphi_a0 = derphi_a1
+
+        # stopping test if lower function not found
+        if (i > maxiter):
+            alpha_star = alpha1
+            fval_star = phi_a1
+            fprime_star = None
+            break
+
+    if fprime_star is not None:
+        # fprime_star is a number (derphi) -- so use the most recently
+        # calculated gradient used in computing it derphi = gfk*pk
+        # this is the gradient at the next step no need to compute it
+        # again in the outer loop.
+        fprime_star = _ls_ingfk
+
+    return alpha_star, _ls_fc, _ls_gc, fval_star, old_fval, fprime_star
+
+def approx_fprime(xk,f,epsilon,*args):
+    f0 = f(*((xk,)+args))
+    grad = numpy.zeros((len(xk),), float)
+    ei = numpy.zeros((len(xk),), float)
+    for k in range(len(xk)):
+        ei[k] = epsilon
+        grad[k] = (f(*((xk+ei,)+args)) - f0)/epsilon
+        ei[k] = 0.0
+    return grad
+
diff --git a/ase/optimize/lbfgs.py b/ase/optimize/lbfgs.py
new file mode 100644
index 0000000..164f5b5
--- /dev/null
+++ b/ase/optimize/lbfgs.py
@@ -0,0 +1,320 @@
+# -*- coding: utf-8 -*-
+import sys
+import numpy as np
+from ase.optimize.optimize import Optimizer
+from ase.utils.linesearch import LineSearch
+
+class LBFGS(Optimizer):
+    """Limited memory BFGS optimizer.
+    
+    A limited memory version of the bfgs algorithm. Unlike the bfgs algorithm
+    used in bfgs.py, the inverse of Hessian matrix is updated.  The inverse
+    Hessian is represented only as a diagonal matrix to save memory
+
+    """
+    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
+                 maxstep=None, memory=100, damping = 1.0, alpha = 10.0,
+                 use_line_search=False):
+        """
+        Parameters:
+
+        restart: string
+            Pickle file used to store vectors for updating the inverse of Hessian
+            matrix. If set, file with such a name will be searched and information
+            stored will be used, if the file exists.
+
+        logfile: string
+            Where should output go. None for no output, '-' for stdout.
+
+        trajectory: string
+            Pickle file used to store trajectory of atomic movement.
+
+        maxstep: float
+            How far is a single atom allowed to move. This is useful for DFT
+            calculations where wavefunctions can be reused if steps are small.
+            Default is 0.04 Angstrom.
+
+        memory: int
+            Number of steps to be stored. Default value is 100. Three numpy
+            arrays of this length containing floats are stored.
+
+        damping: float
+            The calculated step is multiplied with this number before added to
+            the positions. 
+
+        alpha: float
+            Initial guess for the Hessian (curvature of energy surface). A
+            conservative value of 70.0 is the default, but number of needed
+            steps to converge might be less if a lower value is used. However,
+            a lower value also means risk of instability.
+            
+        """
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+        if maxstep is not None:
+            if maxstep > 1.0:
+                raise ValueError('You are using a much too large value for ' +
+                                 'the maximum step size: %.1f Angstrom' % maxstep)
+            self.maxstep = maxstep
+        else:
+            self.maxstep = 0.04
+
+        self.memory = memory
+        self.H0 = 1. / alpha  # Initial approximation of inverse Hessian
+                            # 1./70. is to emulate the behaviour of BFGS
+                            # Note that this is never changed!
+        self.damping = damping
+        self.use_line_search = use_line_search
+        self.p = None
+        self.function_calls = 0
+        self.force_calls = 0
+
+    def initialize(self):
+        """Initalize everything so no checks have to be done in step"""
+        self.iteration = 0
+        self.s = []
+        self.y = []
+        self.rho = [] # Store also rho, to avoid calculationg the dot product
+                      # again and again
+
+        self.r0 = None
+        self.f0 = None
+        self.e0 = None
+        self.task = 'START'
+        self.load_restart = False
+
+    def read(self):
+        """Load saved arrays to reconstruct the Hessian"""
+        self.iteration, self.s, self.y, self.rho, \
+        self.r0, self.f0, self.e0, self.task = self.load()
+        self.load_restart = True
+
+    def step(self, f):
+        """Take a single step
+        
+        Use the given forces, update the history and calculate the next step --
+        then take it"""
+        r = self.atoms.get_positions()
+        p0 = self.p
+    
+        self.update(r, f, self.r0, self.f0)
+        
+        s = self.s
+        y = self.y
+        rho = self.rho
+        H0 = self.H0
+
+        loopmax = np.min([self.memory, self.iteration])
+        a = np.empty((loopmax,), dtype=np.float64)
+
+        ### The algorithm itself:
+        q = - f.reshape(-1) 
+        for i in range(loopmax - 1, -1, -1):
+            a[i] = rho[i] * np.dot(s[i], q)
+            q -= a[i] * y[i]
+        z = H0 * q
+        
+        for i in range(loopmax):
+            b = rho[i] * np.dot(y[i], z)
+            z += s[i] * (a[i] - b)
+
+        self.p = - z.reshape((-1, 3))
+        ###
+ 
+        g = -f
+        if self.use_line_search == True:
+            e = self.func(r)
+            self.line_search(r, g, e)
+            dr = (self.alpha_k * self.p).reshape(len(self.atoms),-1)
+        else:
+            self.force_calls += 1
+            self.function_calls += 1
+            dr = self.determine_step(self.p) * self.damping
+        self.atoms.set_positions(r+dr)
+        
+        self.iteration += 1
+        self.r0 = r
+        self.f0 = -g
+        self.dump((self.iteration, self.s, self.y, 
+                   self.rho, self.r0, self.f0, self.e0, self.task))
+
+    def determine_step(self, dr):
+        """Determine step to take according to maxstep
+        
+        Normalize all steps as the largest step. This way
+        we still move along the eigendirection.
+        """
+        steplengths = (dr**2).sum(1)**0.5
+        longest_step = np.max(steplengths)
+        if longest_step >= self.maxstep:
+            dr *= self.maxstep / longest_step
+        
+        return dr
+
+    def update(self, r, f, r0, f0):
+        """Update everything that is kept in memory
+
+        This function is mostly here to allow for replay_trajectory.
+        """
+        if self.iteration > 0:
+            s0 = r.reshape(-1) - r0.reshape(-1)
+            self.s.append(s0)
+
+            # We use the gradient which is minus the force!
+            y0 = f0.reshape(-1) - f.reshape(-1)
+            self.y.append(y0)
+            
+            rho0 = 1.0 / np.dot(y0, s0)
+            self.rho.append(rho0)
+
+        if self.iteration > self.memory:
+            self.s.pop(0)
+            self.y.pop(0)
+            self.rho.pop(0)
+
+
+    def replay_trajectory(self, traj):
+        """Initialize history from old trajectory."""
+        if isinstance(traj, str):
+            from ase.io.trajectory import PickleTrajectory
+            traj = PickleTrajectory(traj, 'r')
+        r0 = None
+        f0 = None
+        # The last element is not added, as we get that for free when taking
+        # the first qn-step after the replay
+        for i in range(0, len(traj) - 1):
+            r = traj[i].get_positions()
+            f = traj[i].get_forces()
+            self.update(r, f, r0, f0)
+            r0 = r.copy()
+            f0 = f.copy()
+            self.iteration += 1
+        self.r0 = r0
+        self.f0 = f0
+
+    def func(self, x):
+        """Objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        self.function_calls += 1
+        return self.atoms.get_potential_energy()
+
+    def fprime(self, x):
+        """Gradient of the objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        self.force_calls += 1
+        # Remember that forces are minus the gradient!
+        return - self.atoms.get_forces().reshape(-1)
+
+    def line_search(self, r, g, e):
+        self.p = self.p.ravel()
+        p_size = np.sqrt((self.p **2).sum())
+        if p_size <= np.sqrt(len(self.atoms) * 1e-10):
+            self.p /= (p_size / np.sqrt(len(self.atoms)*1e-10))
+        g = g.ravel()
+        r = r.ravel()
+        ls = LineSearch()
+        self.alpha_k, e, self.e0, self.no_update = \
+           ls._line_search(self.func, self.fprime, r, self.p, g, e, self.e0,
+                           maxstep=self.maxstep, c1=.23,
+                           c2=.46, stpmax=50.)
+        if self.alpha_k is None:
+            raise RuntimeError("LineSearch failed!")
+
+class LBFGSLineSearch(LBFGS):
+    """This optimizer uses the LBFGS algorithm, but does a line search that fulfills
+    the Wolff conditions.
+    """
+
+    def __init__(self, *args, **kwargs):
+        kwargs['use_line_search'] = True
+        LBFGS.__init__(self, *args, **kwargs)
+
+#    """Modified version of LBFGS.
+#
+#    This optimizer uses the LBFGS algorithm, but does a line search for the
+#    minimum along the search direction. This is done by issuing an additional
+#    force call for each step, thus doubling the number of calculations.
+#
+#    Additionally the Hessian is reset if the new guess is not sufficiently
+#    better than the old one.
+#    """
+#    def __init__(self, *args, **kwargs):
+#        self.dR = kwargs.pop('dR', 0.1)         
+#        LBFGS.__init__(self, *args, **kwargs)
+#
+#    def update(self, r, f, r0, f0):
+#        """Update everything that is kept in memory
+#
+#        This function is mostly here to allow for replay_trajectory.
+#        """
+#        if self.iteration > 0:
+#            a1 = abs(np.dot(f.reshape(-1), f0.reshape(-1)))
+#            a2 = np.dot(f0.reshape(-1), f0.reshape(-1))
+#            if not (a1 <= 0.5 * a2 and a2 != 0):
+#                # Reset optimization
+#                self.initialize()
+#
+#        # Note that the reset above will set self.iteration to 0 again
+#        # which is why we should check again
+#        if self.iteration > 0:
+#            s0 = r.reshape(-1) - r0.reshape(-1)
+#            self.s.append(s0)
+#
+#            # We use the gradient which is minus the force!
+#            y0 = f0.reshape(-1) - f.reshape(-1)
+#            self.y.append(y0)
+#            
+#            rho0 = 1.0 / np.dot(y0, s0)
+#            self.rho.append(rho0)
+#
+#        if self.iteration > self.memory:
+#            self.s.pop(0)
+#            self.y.pop(0)
+#            self.rho.pop(0)
+#
+#    def determine_step(self, dr):
+#        f = self.atoms.get_forces()
+#        
+#        # Unit-vector along the search direction
+#        du = dr / np.sqrt(np.dot(dr.reshape(-1), dr.reshape(-1)))
+#
+#        # We keep the old step determination before we figure 
+#        # out what is the best to do.
+#        maxstep = self.maxstep * np.sqrt(3 * len(self.atoms))
+#
+#        # Finite difference step using temporary point
+#        self.atoms.positions += (du * self.dR)
+#        # Decide how much to move along the line du
+#        Fp1 = np.dot(f.reshape(-1), du.reshape(-1))
+#        Fp2 = np.dot(self.atoms.get_forces().reshape(-1), du.reshape(-1))
+#        CR = (Fp1 - Fp2) / self.dR
+#        #RdR = Fp1*0.1
+#        if CR < 0.0:
+#            #print "negcurve"
+#            RdR = maxstep
+#            #if(abs(RdR) > maxstep):
+#            #    RdR = self.sign(RdR) * maxstep
+#        else:
+#            Fp = (Fp1 + Fp2) * 0.5
+#            RdR = Fp / CR 
+#            if abs(RdR) > maxstep:
+#                RdR = np.sign(RdR) * maxstep
+#            else:
+#                RdR += self.dR * 0.5
+#        return du * RdR
+
+class HessLBFGS(LBFGS):
+    """Backwards compatibiliyt class"""
+    def __init__(self, *args, **kwargs):
+        if 'method' in kwargs:
+            del kwargs['method']
+        sys.stderr.write('Please use LBFGS instead of HessLBFGS!')
+        LBFGS.__init__(self, *args, **kwargs)
+
+class LineLBFGS(LBFGSLineSearch):
+    """Backwards compatibiliyt class"""
+    def __init__(self, *args, **kwargs):
+        if 'method' in kwargs:
+            del kwargs['method']
+        sys.stderr.write('Please use LBFGSLineSearch instead of LineLBFGS!')
+        LBFGSLineSearch.__init__(self, *args, **kwargs)
diff --git a/ase/optimize/mdmin.py b/ase/optimize/mdmin.py
new file mode 100644
index 0000000..05db11a
--- /dev/null
+++ b/ase/optimize/mdmin.py
@@ -0,0 +1,38 @@
+import numpy as np
+
+from ase.optimize.optimize import Optimizer
+
+
+class MDMin(Optimizer):
+    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
+                 dt=None):
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+        if dt is not None:
+            self.dt = dt
+
+    def initialize(self):
+        self.v = None
+        self.dt = 0.2
+
+    def read(self):
+        self.v, self.dt = self.load()
+        
+    def step(self, f):
+        atoms = self.atoms
+
+        if self.v is None:
+            self.v = np.zeros((len(atoms), 3))
+        else:
+            self.v += 0.5 * self.dt * f
+            # Correct velocities:
+            vf = np.vdot(self.v, f)
+            if vf < 0.0:
+                self.v[:] = 0.0
+            else:
+                self.v[:] = f * vf / np.vdot(f, f)
+
+        self.v += 0.5 * self.dt * f
+        r = atoms.get_positions()
+        atoms.set_positions(r + self.dt * self.v)
+        self.dump((self.v, self.dt))
diff --git a/ase/optimize/oldqn.py b/ase/optimize/oldqn.py
new file mode 100644
index 0000000..282cf5a
--- /dev/null
+++ b/ase/optimize/oldqn.py
@@ -0,0 +1,445 @@
+# Copyright (C) 2003  CAMP
+# Please see the accompanying LICENSE file for further information.
+
+"""
+Quasi-Newton algorithm
+"""
+
+__docformat__ = 'reStructuredText'
+
+import numpy as np
+import weakref,time,sys
+
+
+def f(lamda,Gbar,b,radius): 
+        b1 = b - lamda
+        g = radius**2 - np.dot(Gbar/b1, Gbar/b1)
+        return g
+
+
+
+def scale_radius_energy(f,r):
+        scale = 1.0
+#       if(r<=0.01):
+#               return scale
+        
+        if f<0.01: scale*=1.4
+        if f<0.05: scale*=1.4
+        if f<0.10: scale*=1.4
+        if f<0.40: scale*=1.4
+
+        if f>0.5: scale *= 1./1.4               
+        if f>0.7: scale *= 1./1.4               
+        if f>1.0: scale *= 1./1.4
+
+        return scale
+
+def scale_radius_force(f,r):
+        scale = 1.0
+#       if(r<=0.01):
+#               return scale
+        g = abs(f -1)
+        if g<0.01: scale*=1.4
+        if g<0.05: scale*=1.4
+        if g<0.10: scale*=1.4
+        if g<0.40: scale*=1.4
+
+        if g>0.5: scale *= 1./1.4               
+        if g>0.7: scale *= 1./1.4               
+        if g>1.0: scale *= 1./1.4
+
+        return scale
+
+def find_lamda(upperlimit,Gbar,b,radius):
+        lowerlimit = upperlimit
+        eps = 1e-12
+        step = 0.1
+        while  f(lowerlimit,Gbar,b,radius) < 0:
+                lowerlimit -= step
+                
+        converged = False
+
+        while not converged: 
+
+                midt = (upperlimit+lowerlimit)/2.
+                lamda = midt
+                fmidt = f(midt,Gbar,b,radius)
+                fupper = f(upperlimit,Gbar,b,radius)
+                flower = f(lowerlimit,Gbar,b,radius)
+        
+                if fupper*fmidt<0: 
+                        lowerlimit = midt 
+                else: 
+                        upperlimit = midt
+
+                if abs(upperlimit-lowerlimit)<1e-6: 
+                        converged = True
+
+        return lamda
+
+def get_hessian_inertia(eigenvalues):
+        # return number of negative modes
+        n = 0
+        print 'eigenvalues ',eigenvalues[0],eigenvalues[1],eigenvalues[2]
+        while eigenvalues[n]<0:
+                n+=1
+        return n 
+
+
+from numpy.linalg import eigh, solve
+
+from ase.optimize.optimize import Optimizer
+
+
+
+class GoodOldQuasiNewton(Optimizer):
+
+    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
+                 fmax=None, converged=None,
+                hessianupdate='BFGS',hessian=None,forcemin=True,
+                verbosity=None,maxradius=None,
+                diagonal=20.,radius=None,
+                transitionstate = False):
+            
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+
+        self.eps = 1e-12
+        self.hessianupdate = hessianupdate
+        self.forcemin = forcemin
+        self.verbosity = verbosity
+        self.diagonal = diagonal
+
+        self.atoms = atoms
+
+        n = len(self.atoms) * 3
+        if radius is None: 
+                self.radius = 0.05*np.sqrt(n)/10.0
+        else:
+                self.radius = radius
+
+        if maxradius is None: 
+                self.maxradius = 0.5*np.sqrt(n)
+        else:
+                self.maxradius = maxradius
+                
+        # 0.01 < radius < maxradius
+        self.radius = max(min( self.radius, self.maxradius ), 0.0001)
+
+        self.transitionstate = transitionstate
+
+        # check if this is a nudged elastic band calculation
+        if hasattr(atoms,'springconstant'): 
+                self.forcemin=False
+
+        self.t0 = time.time() 
+
+    def initialize(self):pass
+
+    def write_log(self,text):
+        if self.logfile is not None:
+            self.logfile.write(text + '\n')
+            self.logfile.flush()
+
+    def set_max_radius(self, maxradius):
+                self.maxradius = maxradius
+                self.radius = min(self.maxradius, self.radius)
+                
+    def set_hessian(self,hessian):
+        self.hessian = hessian
+
+    def get_hessian(self):
+        if not hasattr(self,'hessian'): 
+                self.set_default_hessian() 
+        return self.hessian
+
+    def set_default_hessian(self): 
+        # set unit matrix
+        n = len(self.atoms) * 3
+        hessian = np.zeros((n,n)) 
+        for i in range(n): 
+                        hessian[i][i] = self.diagonal
+        self.set_hessian(hessian) 
+
+    def read_hessian(self,filename): 
+        import cPickle
+        f = open(filename,'r')
+        self.set_hessian(cPickle.load(f))
+        f.close()
+
+    def write_hessian(self,filename): 
+        import cPickle
+        f = paropen(filename,'w')
+        cPickle.dump(self.get_hessian(),f)
+        f.close()
+
+    def write_to_restartfile(self):
+        import cPickle
+        f = paropen(self.restartfile,'w')
+        cPickle.dump((self.oldpos,
+                      self.oldG,
+                      self.oldenergy,
+                      self.radius,
+                      self.hessian,
+                      self.energy_estimate),f)
+        f.close()
+        
+
+
+    def update_hessian(self,pos,G):
+        import copy
+        if hasattr(self,'oldG'): 
+                if self.hessianupdate=='BFGS': 
+                        self.update_hessian_bfgs(pos,G) 
+                elif self.hessianupdate== 'Powell': 
+                        self.update_hessian_powell(pos,G) 
+                else:           
+                        self.update_hessian_bofill(pos,G) 
+        else: 
+                if not hasattr(self,'hessian'): 
+                        self.set_default_hessian()
+
+        self.oldpos = copy.copy(pos)
+        self.oldG = copy.copy(G)
+
+        if self.verbosity: 
+                print 'hessian ',self.hessian
+
+
+        
+    def update_hessian_bfgs(self,pos,G): 
+        n = len(self.hessian)
+        dgrad = G - self.oldG
+        dpos  = pos - self.oldpos
+        absdpos = np.sqrt(np.dot(dpos, dpos))
+        dotg  = np.dot(dgrad,dpos) 
+        tvec  = np.dot(dpos,self.hessian)
+        dott  = np.dot(dpos,tvec)
+        if (abs(dott)>self.eps) and (abs(dotg)>self.eps): 
+                for i in range(n): 
+                        for j in range(n): 
+                                h = dgrad[i]*dgrad[j]/dotg - tvec[i]*tvec[j]/dott
+                                self.hessian[i][j] += h
+
+
+
+    def update_hessian_powell(self,pos,G):          
+        n = len(self.hessian)
+        dgrad = G - self.oldG
+        dpos  = pos - self.oldpos
+        absdpos = np.dot(dpos, dpos)
+        if absdpos<self.eps: 
+                return
+
+        dotg  = np.dot(dgrad,dpos) 
+        tvec  = dgrad-np.dot(dpos,self.hessian)
+        tvecdot = np.dot(tvec,tvec)
+        tvecdpos = np.dot(tvec,dpos) 
+        ddot = tvecdpos/absdpos
+
+        dott  = np.dot(dpos,tvec)
+        if (abs(dott)>self.eps) and (abs(dotg)>self.eps): 
+                for i in range(n): 
+                        for j in range(n): 
+                                h = tvec[i]*dpos[j] + dpos[i]*tvec[j]-ddot*dpos[i]*dpos[j]
+                                h *= 1./absdpos
+                                self.hessian[i][j] += h
+
+
+    def update_hessian_bofill(self,pos,G):                                                                     
+        print 'update Bofill'
+        n = len(self.hessian)                                                                               
+        dgrad = G - self.oldG                                                                               
+        dpos  = pos - self.oldpos                                                                           
+        absdpos = np.dot(dpos, dpos)                                                                          
+        if absdpos<self.eps: 
+                return
+        dotg  = np.dot(dgrad,dpos)                                                                         
+        tvec  = dgrad-np.dot(dpos,self.hessian)                                                 
+        tvecdot = np.dot(tvec,tvec)                                                                        
+        tvecdpos = np.dot(tvec,dpos)                                                                       
+        ddot = tvecdpos/absdpos                                                                             
+
+        coef1 = 1. - tvecdpos*tvecdpos/(absdpos*tvecdot)
+        coef2 = (1. - coef1)*absdpos/tvecdpos
+        coef3 = coef1*tvecdpos/absdpos
+
+        dott  = np.dot(dpos,tvec)                                                                          
+        if (abs(dott)>self.eps) and (abs(dotg)>self.eps):                                                   
+                for i in range(n):                                                                          
+                        for j in range(n):                                                                  
+                                h = coef1*(tvec[i]*dpos[j] + dpos[i]*tvec[j])-dpos[i]*dpos[j]*coef3 + coef2*tvec[i]*tvec[j]
+                                h *= 1./absdpos
+                                self.hessian[i][j] += h                                                     
+
+
+
+    def step(self, f):
+        """ Do one QN step
+        """
+
+        pos = self.atoms.get_positions().ravel()
+        G = -self.atoms.get_forces().ravel()
+        energy = self.atoms.get_potential_energy()
+
+
+        self.write_iteration(energy,G)
+
+        if hasattr(self,'oldenergy'):
+
+                self.write_log('energies ' + str(energy) + ' ' + str(self.oldenergy))
+
+                if self.forcemin:
+                        de = 1e-4
+                else:
+                        de = 1e-2
+
+                if self.transitionstate:
+                        de = 0.2
+
+                if (energy-self.oldenergy)>de:
+                        self.write_log('reject step')
+                        self.atoms.set_positions(self.oldpos.reshape((-1, 3)))
+                        G = self.oldG
+                        energy = self.oldenergy
+                        self.radius *= 0.5
+                else: 
+                        self.update_hessian(pos,G)
+                        de = energy - self.oldenergy
+                        f = 1.0
+                        if self.forcemin: 
+                                self.write_log("energy change; actual: %f estimated: %f "%(de,self.energy_estimate))
+                                if abs(self.energy_estimate)>self.eps: 
+                                        f = abs((de/self.energy_estimate)-1)
+                                        self.write_log('Energy prediction factor ' + str(f))
+                                        # fg = self.get_force_prediction(G)
+                                        self.radius *= scale_radius_energy(f,self.radius) 
+
+                        else:
+                                self.write_log("energy change; actual: %f "%(de))
+                                self.radius*=1.5
+
+                        fg = self.get_force_prediction(G)
+                        self.write_log("Scale factors %f %f "%(scale_radius_energy(f,self.radius),
+                                                                scale_radius_force(fg,self.radius)))
+                        
+                                   
+                self.radius = max(min(self.radius,self.maxradius), 0.0001)
+        else: 
+                self.update_hessian(pos,G)
+
+        self.write_log("new radius %f "%(self.radius))          
+        self.oldenergy = energy
+
+        b,V = eigh(self.hessian)
+        V=V.T.copy()
+        self.V = V
+
+        # calculate projection of G onto eigenvectors V
+        Gbar = np.dot(G,np.transpose(V))
+        
+        lamdas = self.get_lambdas(b,Gbar)
+
+        D = -Gbar/(b-lamdas) 
+        n = len(D)
+        step = np.zeros((n))
+        for i in range(n): 
+                step += D[i]*V[i]
+
+        pos = self.atoms.get_positions().ravel()
+        pos += step
+
+        energy_estimate = self.get_energy_estimate(D,Gbar,b) 
+        self.energy_estimate = energy_estimate
+        self.gbar_estimate = self.get_gbar_estimate(D,Gbar,b)
+        self.old_gbar = Gbar
+
+        self.atoms.set_positions(pos.reshape((-1, 3)))
+
+
+
+
+    def get_energy_estimate(self,D,Gbar,b): 
+
+        de = 0.0
+        for n in range(len(D)): 
+                de += D[n]*Gbar[n] + 0.5*D[n]*b[n]*D[n]
+        return de
+
+    def get_gbar_estimate(self,D,Gbar,b):
+        gbar_est = (D*b) + Gbar
+        self.write_log('Abs Gbar estimate ' + str(np.dot(gbar_est,gbar_est)))
+        return gbar_est
+
+    def get_lambdas(self,b,Gbar):
+        lamdas = np.zeros((len(b)))
+
+        D = -Gbar/b
+        #absD = np.sqrt(np.sum(D**2))
+        absD = np.sqrt(np.dot(D, D))
+
+        eps = 1e-12
+        nminus = self.get_hessian_inertia(b)
+
+        if absD < self.radius:
+                if not self.transitionstate:
+                        self.write_log('Newton step') 
+                        return lamdas
+                else:
+                        if nminus==1:
+                                self.write_log('Newton step')
+                                return lamdas
+                        else:
+                                self.write_log("Wrong inertia of Hessian matrix: %2.2f %2.2f "%(b[0],b[1]))
+
+        else:
+                self.write_log("Corrected Newton step: abs(D) = %2.2f "%(absD))
+
+        if not self.transitionstate: 
+                # upper limit
+                upperlimit = min(0,b[0])-eps
+                lowerlimit = upperlimit
+                lamda = find_lamda(upperlimit,Gbar,b,self.radius)
+                lamdas += lamda
+        else:
+                # upperlimit
+                upperlimit = min(-b[0],b[1],0)-eps
+                lamda = find_lamda(upperlimit,Gbar,b,self.radius)
+                lamdas += lamda
+                lamdas[0] -= 2*lamda
+                
+        return lamdas
+
+
+
+    def print_hessian(self): 
+        hessian = self.get_hessian()
+        n = len(hessian)
+        for i in range(n): 
+            for j in range(n): 
+                print "%2.4f " %(hessian[i][j]),
+            print " "
+
+
+    
+
+    def get_hessian_inertia(self,eigenvalues):
+        # return number of negative modes
+        self.write_log("eigenvalues %2.2f %2.2f %2.2f "%(eigenvalues[0],
+                                                        eigenvalues[1],
+                                                        eigenvalues[2]))
+        n = 0
+        while eigenvalues[n]<0:
+                n+=1
+        return n
+
+    def get_force_prediction(self,G):
+        # return measure of how well the forces are predicted
+        Gbar = np.dot(G,np.transpose(self.V))
+        dGbar_actual = Gbar-self.old_gbar
+        dGbar_predicted = Gbar-self.gbar_estimate
+
+        f = np.dot(dGbar_actual,dGbar_predicted)/np.dot(dGbar_actual,dGbar_actual)
+        self.write_log('Force prediction factor ' + str(f))
+        return f
+
+    def write_iteration(self,energy,G):pass
diff --git a/ase/optimize/optimize.py b/ase/optimize/optimize.py
new file mode 100644
index 0000000..4e3e32d
--- /dev/null
+++ b/ase/optimize/optimize.py
@@ -0,0 +1,187 @@
+"""Structure optimization. """
+
+import sys
+import pickle
+import time
+from math import sqrt
+from os.path import isfile
+
+import numpy as np
+
+from ase.parallel import rank, barrier
+from ase.io.trajectory import PickleTrajectory
+
+
+class Dynamics:
+    """Base-class for all MD and structure optimization classes.
+
+    Dynamics(atoms, logfile)
+
+    atoms: Atoms object
+        The Atoms object to operate on
+    logfile: file object or str
+        If *logfile* is a string, a file with that name will be opened.
+        Use '-' for stdout.
+    trajectory: Trajectory object or str
+        Attach trajectory object.  If *trajectory* is a string a
+        PickleTrajectory will be constructed.  Use *None* for no
+        trajectory.
+    """
+    def __init__(self, atoms, logfile, trajectory):
+        self.atoms = atoms
+
+        if rank != 0:
+            logfile = None
+        elif isinstance(logfile, str):
+            if logfile == '-':
+                logfile = sys.stdout
+            else:
+                logfile = open(logfile, 'a')
+        self.logfile = logfile
+        
+        self.observers = []
+        self.nsteps = 0
+
+        if trajectory is not None:
+            if isinstance(trajectory, str):
+                trajectory = PickleTrajectory(trajectory, 'w', atoms)
+            self.attach(trajectory)
+
+    def get_number_of_steps(self):
+        return self.nsteps
+
+    def insert_observer(self, function, position=0, interval=1, 
+                        *args, **kwargs):
+        """Insert an observer."""
+        if not callable(function):
+            function = function.write
+        self.observers.insert(position, (function, interval, args, kwargs))
+
+    def attach(self, function, interval=1, *args, **kwargs):
+        """Attach callback function.
+
+        At every *interval* steps, call *function* with arguments
+        *args* and keyword arguments *kwargs*."""
+
+        if not hasattr(function, '__call__'):
+            function = function.write
+        self.observers.append((function, interval, args, kwargs))
+
+    def call_observers(self):
+        for function, interval, args, kwargs in self.observers:
+            if self.nsteps % interval == 0:
+                function(*args, **kwargs)
+
+
+class Optimizer(Dynamics):
+    """Base-class for all structure optimization classes."""
+    def __init__(self, atoms, restart, logfile, trajectory):
+        """Structure optimizer object.
+
+        atoms: Atoms object
+            The Atoms object to relax.
+        restart: str
+            Filename for restart file.  Default value is *None*.
+        logfile: file object or str
+            If *logfile* is a string, a file with that name will be opened.
+            Use '-' for stdout.
+        trajectory: Trajectory object or str
+            Attach trajectory object.  If *trajectory* is a string a
+            PickleTrajectory will be constructed.  Use *None* for no
+            trajectory.
+        """
+        Dynamics.__init__(self, atoms, logfile, trajectory)
+        self.restart = restart
+
+        if restart is None or not isfile(restart):
+            self.initialize()
+        else:
+            self.read()
+            barrier()
+    def initialize(self):
+        pass
+
+    def run(self, fmax=0.05, steps=100000000):
+        """Run structure optimization algorithm.
+
+        This method will return when the forces on all individual
+        atoms are less than *fmax* or when the number of steps exceeds
+        *steps*."""
+
+        self.fmax = fmax
+        step = 0
+        while step < steps:
+            f = self.atoms.get_forces()
+            self.log(f)
+            self.call_observers()
+            if self.converged(f):
+                return
+            self.step(f)
+            self.nsteps += 1
+            step += 1
+
+    def converged(self, forces=None):
+        """Did the optimization converge?"""
+        if forces is None:
+            forces = self.atoms.get_forces()
+        if hasattr(self.atoms, 'get_curvature'):
+            return (forces**2).sum(axis=1).max() < self.fmax**2 and \
+                   self.atoms.get_curvature() < 0.0
+        return (forces**2).sum(axis=1).max() < self.fmax**2
+
+    def log(self, forces):
+        fmax = sqrt((forces**2).sum(axis=1).max())
+        e = self.atoms.get_potential_energy()
+        T = time.localtime()
+        if self.logfile is not None:
+            name = self.__class__.__name__
+            self.logfile.write('%s: %3d  %02d:%02d:%02d %15.6f %12.4f\n' %
+                               (name, self.nsteps, T[3], T[4], T[5], e, fmax))
+            self.logfile.flush()
+        
+    def dump(self, data):
+        if rank == 0 and self.restart is not None:
+            pickle.dump(data, open(self.restart, 'wb'), protocol=2)
+
+    def load(self):
+        return pickle.load(open(self.restart))
+
+
+class NDPoly:
+    def __init__(self, ndims=1, order=3):
+        """Multivariate polynomium.
+
+        ndims: int
+            Number of dimensions.
+        order: int
+            Order of polynomium."""
+        
+        if ndims == 0:
+            exponents = [()]
+        else:
+            exponents = []
+            for i in range(order + 1):
+                E = NDPoly(ndims - 1, order - i).exponents
+                exponents += [(i,) + tuple(e) for e in E]
+        self.exponents = np.array(exponents)
+        self.c = None
+        
+    def __call__(self, *x):
+        """Evaluate polynomial at x."""
+        return np.dot(self.c, (x**self.exponents).prod(1))
+
+    def fit(self, x, y):
+        """Fit polynomium at points in x to values in y."""
+        A = (x**self.exponents[:, np.newaxis]).prod(2)
+        self.c = np.linalg.solve(np.inner(A, A), np.dot(A, y))
+
+
+def polyfit(x, y, order=3):
+    """Fit polynomium at points in x to values in y.
+
+    With D dimensions and N points, x must have shape (N, D) and y
+    must have length N."""
+    
+    p = NDPoly(len(x[0]), order)
+    p.fit(x, y)
+    return p
diff --git a/ase/optimize/sciopt.py b/ase/optimize/sciopt.py
new file mode 100644
index 0000000..f3fdd2c
--- /dev/null
+++ b/ase/optimize/sciopt.py
@@ -0,0 +1,272 @@
+import numpy as np
+try:
+    import scipy.optimize as opt
+except ImportError:
+    pass
+
+from ase.optimize.optimize import Optimizer
+
+
+class Converged(Exception):
+    pass
+
+class OptimizerConvergenceError(Exception):
+    pass
+
+class SciPyOptimizer(Optimizer):
+    """General interface for SciPy optimizers
+
+    Only the call to the optimizer is still needed
+    """
+    def __init__(self, atoms, logfile='-', trajectory=None,
+                 callback_always=False, alpha=70.0):
+        """Initialize object
+
+        Parameters:
+
+        callback_always: book
+            Should the callback be run after each force call (also in the
+            linesearch)
+
+        alpha: float
+            Initial guess for the Hessian (curvature of energy surface). A
+            conservative value of 70.0 is the default, but number of needed
+            steps to converge might be less if a lower value is used. However,
+            a lower value also means risk of instability.
+
+        """
+        restart = None
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+        self.force_calls = 0
+        self.callback_always = callback_always
+        self.H0 = alpha
+
+    def x0(self):
+        """Return x0 in a way SciPy can use
+
+        This class is mostly usable for subclasses wanting to redefine the
+        parameters (and the objective function)"""
+        return self.atoms.get_positions().reshape(-1)
+
+    def f(self, x):
+        """Objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        # Scale the problem as SciPy uses I as initial Hessian.
+        return self.atoms.get_potential_energy() / self.H0
+
+    def fprime(self, x):
+        """Gradient of the objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        self.force_calls += 1
+
+        if self.callback_always:
+            self.callback(x)
+
+        # Remember that forces are minus the gradient!
+        # Scale the problem as SciPy uses I as initial Hessian.
+        return - self.atoms.get_forces().reshape(-1) / self.H0
+
+    def callback(self, x):
+        """Callback function to be run after each iteration by SciPy
+
+        This should also be called once before optimization starts, as SciPy
+        optimizers only calls it after each iteration, while ase optimizers
+        call something similar before as well.
+        """
+        f = self.atoms.get_forces()
+        self.log(f)
+        self.call_observers()
+        if self.converged(f):
+            raise Converged
+        self.nsteps += 1
+
+    def run(self, fmax=0.05, steps=100000000):
+        self.fmax = fmax
+        # As SciPy does not log the zeroth iteration, we do that manually
+        self.callback(None)
+        try:
+            # Scale the problem as SciPy uses I as initial Hessian.
+            self.call_fmin(fmax / self.H0, steps)
+        except Converged:
+            pass
+
+    def dump(self, data):
+        pass
+
+    def load(self):
+        pass
+
+    def call_fmin(self, fmax, steps):
+        raise NotImplementedError
+
+class SciPyFminCG(SciPyOptimizer):
+    """Non-linear (Polak-Ribiere) conjugate gradient algorithm"""
+    def call_fmin(self, fmax, steps):
+        output = opt.fmin_cg(self.f,
+                             self.x0(),
+                             fprime=self.fprime,
+                             #args=(),
+                             gtol=fmax * 0.1, #Should never be reached
+                             norm=np.inf,
+                             #epsilon=
+                             maxiter=steps,
+                             full_output=1,
+                             disp=0,
+                             #retall=0, 
+                             callback=self.callback
+                            )
+        warnflag = output[-1]
+        if warnflag == 2:
+            raise OptimizerConvergenceError('Warning: Desired error not necessarily achieved ' \
+                                            'due to precision loss')
+
+class SciPyFminBFGS(SciPyOptimizer):
+    """Quasi-Newton method (Broydon-Fletcher-Goldfarb-Shanno)"""
+    def call_fmin(self, fmax, steps):
+        output = opt.fmin_bfgs(self.f,
+                               self.x0(),
+                               fprime=self.fprime,
+                               #args=(), 
+                               gtol=fmax * 0.1, #Should never be reached
+                               norm=np.inf,
+                               #epsilon=1.4901161193847656e-08, 
+                               maxiter=steps,
+                               full_output=1,
+                               disp=0,
+                               #retall=0, 
+                               callback=self.callback
+                              )
+        warnflag = output[-1]
+        if warnflag == 2:
+            raise OptimizerConvergenceError('Warning: Desired error not necessarily achieved' \
+                                            'due to precision loss')
+
+class SciPyGradientlessOptimizer(Optimizer):
+    """General interface for gradient less SciPy optimizers
+
+    Only the call to the optimizer is still needed
+
+    Note: If you redefien x0() and f(), you don't even need an atoms object.
+    Redefining these also allows you to specify an arbitrary objective
+    function.
+
+    XXX: This is still a work in progress
+    """
+    def __init__(self, atoms, logfile='-', trajectory=None,
+                 callback_always=False):
+        """Parameters:
+
+        callback_always: book
+            Should the callback be run after each force call (also in the
+            linesearch)
+        """
+        restart = None
+        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
+        self.function_calls = 0
+        self.callback_always = callback_always
+
+    def x0(self):
+        """Return x0 in a way SciPy can use
+
+        This class is mostly usable for subclasses wanting to redefine the
+        parameters (and the objective function)"""
+        return self.atoms.get_positions().reshape(-1)
+
+    def f(self, x):
+        """Objective function for use of the optimizers"""
+        self.atoms.set_positions(x.reshape(-1, 3))
+        self.function_calls += 1
+        # Scale the problem as SciPy uses I as initial Hessian.
+        return self.atoms.get_potential_energy()
+
+    def callback(self, x):
+        """Callback function to be run after each iteration by SciPy
+
+        This should also be called once before optimization starts, as SciPy
+        optimizers only calls it after each iteration, while ase optimizers
+        call something similar before as well.
+        """
+        # We can't assume that forces are available!
+        #f = self.atoms.get_forces()
+        #self.log(f)
+        self.call_observers()
+        #if self.converged(f):
+        #    raise Converged
+        self.nsteps += 1
+
+    def run(self, ftol=0.01, xtol=0.01, steps=100000000):
+        self.xtol = xtol
+        self.ftol = ftol
+        # As SciPy does not log the zeroth iteration, we do that manually
+        self.callback(None)
+        try:
+            # Scale the problem as SciPy uses I as initial Hessian.
+            self.call_fmin(xtol, ftol, steps)
+        except Converged:
+            pass
+
+    def dump(self, data):
+        pass
+
+    def load(self):
+        pass
+
+    def call_fmin(self, fmax, steps):
+        raise NotImplementedError
+
+class SciPyFmin(SciPyGradientlessOptimizer):
+    """Nelder-Mead Simplex algorithm
+
+    Uses only function calls.
+
+    XXX: This is still a work in progress
+    """
+    def call_fmin(self, xtol, ftol, steps):
+        output = opt.fmin(self.f,
+                          self.x0(),
+                          #args=(),
+                          xtol=xtol,
+                          ftol=ftol,
+                          maxiter=steps,
+                          #maxfun=None,
+                          #full_output=1,
+                          disp=0,
+                          #retall=0,
+                          callback=self.callback
+                         )
+
+class SciPyFminPowell(SciPyGradientlessOptimizer):
+    """Powell's (modified) level set method
+
+    Uses only function calls.
+
+    XXX: This is still a work in progress
+    """
+    def __init__(self, *args, **kwargs):
+        """Parameters:
+
+        direc: float
+            How much to change x to initially. Defaults to 0.04.
+        """
+        direc = kwargs.pop('direc', None)
+        SciPyGradientlessOptimizer.__init__(self, *args, **kwargs)
+
+        if direc is None:
+            self.direc = np.eye(len(self.x0()), dtype=float) * 0.04
+        else:
+            self.direc = np.eye(len(self.x0()), dtype=float) * direc
+
+    def call_fmin(self, xtol, ftol, steps):
+        output = opt.fmin_powell(self.f,
+                                 self.x0(),
+                                 #args=(),
+                                 xtol=xtol,
+                                 ftol=ftol,
+                                 maxiter=steps,
+                                 #maxfun=None,
+                                 #full_output=1,
+                                 disp=0,
+                                 #retall=0,
+                                 callback=self.callback,
+                                 direc=self.direc
+                                )
diff --git a/ase/optimize/test/C2_Cu100.py b/ase/optimize/test/C2_Cu100.py
new file mode 100755
index 0000000..6a373a2
--- /dev/null
+++ b/ase/optimize/test/C2_Cu100.py
@@ -0,0 +1,103 @@
+#PBS -l nodes=4:ppn=8
+#PBS -l walltime=13:00:00
+import numpy as np
+from ase import Atoms
+from ase.constraints import FixedPlane, FixAtoms
+from ase.optimize.test import run_test
+from gpaw import GPAW, Mixer
+from gpaw.poisson import PoissonSolver
+
+def get_calculator():
+    basis = 'szp(dzp)'
+    calc = GPAW(gpts=(64, 64, 128), # ca h=0.25
+                width=0.1,
+                nbands=-5,
+                xc='LDA',
+                mode='lcao',
+                txt='C2_Cu100.txt',
+                mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0),
+                poissonsolver=PoissonSolver(nn='M', relax='GS'),
+                stencils=(3, 3),
+                maxiter=400,
+                basis=basis)
+    return calc
+
+def get_atoms():
+    srf = Atoms('Cu64',[(1.2763,    1.2763,    4.0000),
+                        (3.8290,    1.2763,    4.0000),
+                        (6.3816,    1.2763,    4.0000),
+                        (8.9343,    1.2763,    4.0000),
+                        (1.2763,    3.8290,    4.0000),
+                        (3.8290,    3.8290,    4.0000),
+                        (6.3816,    3.8290,    4.0000),
+                        (8.9343,    3.8290,    4.0000),
+                        (1.2763,    6.3816,    4.0000),
+                        (3.8290,    6.3816,    4.0000),
+                        (6.3816,    6.3816,    4.0000),
+                        (8.9343,    6.3816,    4.0000),
+                        (1.2763,    8.9343,    4.0000),
+                        (3.8290,    8.9343,    4.0000),
+                        (6.3816,    8.9343,    4.0000),
+                        (8.9343,    8.9343,    4.0000),
+                        (0.0000,    0.0000,    5.8050),
+                        (2.5527,    0.0000,    5.8050),
+                        (5.1053,    0.0000,    5.8050),
+                        (7.6580,    0.0000,    5.8050),
+                        (0.0000,    2.5527,    5.8050),
+                        (2.5527,    2.5527,    5.8050),
+                        (5.1053,    2.5527,    5.8050),
+                        (7.6580,    2.5527,    5.8050),
+                        (0.0000,    5.1053,    5.8050),
+                        (2.5527,    5.1053,    5.8050),
+                        (5.1053,    5.1053,    5.8050),
+                        (7.6580,    5.1053,    5.8050),
+                        (0.0000,    7.6580,    5.8050),
+                        (2.5527,    7.6580,    5.8050),
+                        (5.1053,    7.6580,    5.8050),
+                        (7.6580,    7.6580,    5.8050),
+                        (1.2409,    1.2409,    7.6081),
+                        (3.7731,    1.2803,    7.6603),
+                        (6.3219,    1.3241,    7.6442),
+                        (8.8935,    1.2669,    7.6189),
+                        (1.2803,    3.7731,    7.6603),
+                        (3.8188,    3.8188,    7.5870),
+                        (6.3457,    3.8718,    7.6649),
+                        (8.9174,    3.8340,    7.5976),
+                        (1.3241,    6.3219,    7.6442),
+                        (3.8718,    6.3457,    7.6649),
+                        (6.3945,    6.3945,    7.6495),
+                        (8.9576,    6.3976,    7.6213),
+                        (1.2669,    8.8935,    7.6189),
+                        (3.8340,    8.9174,    7.5976),
+                        (6.3976,    8.9576,    7.6213),
+                        (8.9367,    8.9367,    7.6539),
+                        (0.0582,    0.0582,    9.4227),
+                        (2.5965,   -0.2051,    9.4199),
+                        (5.1282,    0.0663,    9.4037),
+                        (7.6808,   -0.0157,    9.4235),
+                        (-0.2051,   2.5965,    9.4199),
+                        (2.1913,    2.1913,    9.6123),
+                        (5.0046,    2.5955,    9.4873),
+                        (7.5409,    2.5336,    9.4126),
+                        (0.0663,    5.1282,    9.4037),
+                        (2.5955,    5.0046,    9.4873),
+                        (5.3381,    5.3381,    9.6106),
+                        (7.8015,    5.0682,    9.4237),
+                        (-0.0157,   7.6808,    9.4235),
+                        (2.5336,    7.5409,    9.4126),
+                        (5.0682,    7.8015,    9.4237),
+                        (7.6155,    7.6155,    9.4317)])
+    c2=Atoms('C2', [(3.2897,    3.2897,   10.6627),
+                    (4.2113,    4.2113,   10.6493)])
+    srf.extend(c2)
+    srf.pbc=(1, 1, 0)
+    srf.set_cell([ 10.2106, 10.2106, 20.6572],scale_atoms=False)
+
+    mask=[a.index < 32  for a in srf]
+    c1 = FixedPlane(-1, (1/np.sqrt(2), 1/np.sqrt(2), 1))
+    c2 = FixedPlane(-2, (1/np.sqrt(2), 1/np.sqrt(2), 1))
+    constraint = FixAtoms(mask=mask)
+    srf.set_constraint([constraint, c1, c2])
+    return srf
+
+run_test(get_atoms, get_calculator, 'C2_Cu100')
diff --git a/ase/optimize/test/C5H12.py b/ase/optimize/test/C5H12.py
new file mode 100644
index 0000000..a4a6746
--- /dev/null
+++ b/ase/optimize/test/C5H12.py
@@ -0,0 +1,50 @@
+#/usr/bin/env python
+#PBS -l nodes=4:ppn=8
+#PBS -l walltime=13:00:00
+from ase import Atoms
+from ase.optimize.test import run_test
+from gpaw import GPAW
+from gpaw import Mixer
+from gpaw.poisson import PoissonSolver
+
+name = 'C5H12'
+
+def get_atoms():
+    atoms = Atoms(symbols='C5H12',
+                  pbc=[False, False, False],
+                  cell=[
+                      [ 16.83752497,   0.        ,   0.        ],
+                      [  0.        ,  12.18645905,   0.        ],
+                      [  0.        ,   0.        ,  11.83462179]
+                  ],
+                  positions=[
+                      [  5.90380523,   5.65545388,   5.91569796],
+                      [  7.15617518,   6.52907738,   5.91569796],
+                      [  8.41815022,   5.66384716,   5.92196554],
+                      [  9.68108996,   6.52891016,   5.91022362],
+                      [ 10.93006206,   5.65545388,   5.91569796],
+                      [  5.00000011,   6.30002353,   5.9163716 ],
+                      [  5.88571848,   5.0122839 ,   6.82246859],
+                      [  5.88625613,   5.01308931,   5.01214155],
+                      [  7.14329342,   7.18115393,   6.81640316],
+                      [  7.14551332,   7.17200869,   5.00879027],
+                      [  8.41609966,   5.00661165,   5.02355167],
+                      [  8.41971183,   5.0251482 ,   6.83462168],
+                      [  9.69568096,   7.18645894,   6.8078633 ],
+                      [  9.68914668,   7.16663649,   5.00000011],
+                      [ 10.95518898,   5.02163182,   6.8289018 ],
+                      [ 11.83752486,   6.29836826,   5.90274952],
+                      [ 10.94464142,   5.00000011,   5.01802495]
+                  ])
+    return atoms
+
+def get_calculator():
+    calc = GPAW(h=0.2,
+                mode = 'lcao',
+                basis = 'szp(dzp)',
+                mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0),
+                poissonsolver=PoissonSolver(nn='M', relax='GS'),
+                txt='C5H12.txt')
+    return calc
+
+run_test(get_atoms, get_calculator, name + '-gpaw')
diff --git a/ase/optimize/test/CO_Au111.py b/ase/optimize/test/CO_Au111.py
new file mode 100644
index 0000000..1952999
--- /dev/null
+++ b/ase/optimize/test/CO_Au111.py
@@ -0,0 +1,30 @@
+from math import sin, cos, pi
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.constraints import FixAtoms
+from ase.optimize.test import run_test
+from ase.lattice.surface import fcc111, add_adsorbate
+
+name = 'CO_Au111'
+
+def get_atoms():
+    zpos = cos(134.3/2.0*pi/180.0)*1.197
+    xpos = sin(134.3/2.0*pi/180.0)*1.19
+    no2 =Atoms('CO', positions=[(-xpos+1.2,0,-zpos), (-xpos+1.2,-1.1,-zpos)])
+
+    # Surface slab
+    slab =fcc111('Au', size=(2, 2, 4),vacuum=2*5, orthogonal = True )
+    slab.center()
+    add_adsorbate(slab,no2,1.5,'bridge')
+    slab.set_pbc((True,True,False))
+
+    #constraints
+    constraint = FixAtoms(mask=[(a.tag == 4) or (a.tag == 3) or (a.tag==2) for a in slab])
+    slab.set_constraint(constraint)
+    return slab
+
+def get_calculator():
+    calc = EMT()
+    return calc
+
+run_test(get_atoms, get_calculator, name, steps=200)
diff --git a/ase/optimize/test/Cu_bulk.py b/ase/optimize/test/Cu_bulk.py
new file mode 100644
index 0000000..f6ee7b5
--- /dev/null
+++ b/ase/optimize/test/Cu_bulk.py
@@ -0,0 +1,16 @@
+from ase.calculators.emt import EMT
+from ase.lattice.cubic import FaceCenteredCubic
+from ase.optimize.test import run_test
+
+name = 'Cu_bulk'
+
+def get_atoms():
+    atoms = FaceCenteredCubic(directions=[[1,-1,0], [1,1,0], [0,0,1]],
+                              size=(3,3,3), symbol='Cu', pbc=(1,1,1))
+    atoms.rattle(stdev=0.1,seed=42)
+    return atoms
+
+def get_calculator():
+    return EMT()
+
+run_test(get_atoms, get_calculator, name, fmax=0.02)
diff --git a/ase/optimize/test/H2.py b/ase/optimize/test/H2.py
new file mode 100644
index 0000000..3d6088e
--- /dev/null
+++ b/ase/optimize/test/H2.py
@@ -0,0 +1,23 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.optimize.test import run_test
+from gpaw import GPAW
+
+name = 'H2'
+
+def get_atoms():
+    cell = (5, 5, 5)
+    atoms = Atoms('H2', [(0, 0, 0), (0, 0, 1.4)], cell=cell)
+    atoms.center()
+    return atoms
+
+def get_calculator_emt():
+    calc = EMT()
+    return calc
+
+def get_calculator_gpaw():
+    calc = GPAW(xc='PBE',txt=None)
+    return calc
+
+run_test(get_atoms, get_calculator_emt, name + '-emt')
+run_test(get_atoms, get_calculator_gpaw, name + '-gpaw', steps=25)
diff --git a/ase/optimize/test/N2Cu_relax.py b/ase/optimize/test/N2Cu_relax.py
new file mode 100644
index 0000000..6844587
--- /dev/null
+++ b/ase/optimize/test/N2Cu_relax.py
@@ -0,0 +1,80 @@
+from ase import Atoms, Atom
+from ase.calculators.emt import EMT
+from ase.constraints import FixAtoms
+from ase.optimize.test import run_test
+
+name = 'N2Cu'
+
+def get_atoms_surf():
+    a = 2.70
+    c = 1.59 * a
+    h = 1.85
+    d = 1.10
+
+    slab = Atoms('2Cu', [(0., 0., 0.), (1/3., 1/3., -0.5*c)], 
+                 tags=(0, 1),
+                 pbc=(1, 1, 0))
+    slab.set_cell([(a, 0, 0),
+                   (a / 2, 3**0.5 * a / 2, 0),
+                   (0, 0, 1)])
+    slab = slab.repeat((4, 4, 1))
+    mask = [a.tag == 1 for a in slab]
+    slab.set_constraint(FixAtoms(mask=mask))
+    return slab
+
+def get_atoms_adsorbate():
+    # We need the relaxed slab here! 
+    slab = Atoms([
+        Atom('Cu', [     -1.028468159509163,     -0.432387156877267,     -0.202086055768265]),
+        Atom('Cu', [      0.333333333333333,      0.333333333333333,     -2.146500000000000]),
+        Atom('Cu', [      1.671531840490805,     -0.432387156877287,     -0.202086055768242]),
+        Atom('Cu', [      3.033333333333334,      0.333333333333333,     -2.146500000000000]),
+        Atom('Cu', [      4.371531840490810,     -0.432387156877236,     -0.202086055768261]),
+        Atom('Cu', [      5.733333333333333,      0.333333333333333,     -2.146500000000000]),
+        Atom('Cu', [      7.071531840490944,     -0.432387156877258,     -0.202086055768294]),
+        Atom('Cu', [      8.433333333333335,      0.333333333333333,     -2.146500000000000]),
+        Atom('Cu', [      0.321531840490810,      1.905881433340708,     -0.202086055768213]),
+        Atom('Cu', [      1.683333333333333,      2.671601923551318,     -2.146500000000000]),
+        Atom('Cu', [      3.021531840490771,      1.905881433340728,     -0.202086055768250]),
+        Atom('Cu', [      4.383333333333334,      2.671601923551318,     -2.146500000000000]),
+        Atom('Cu', [      5.721531840490857,      1.905881433340735,     -0.202086055768267]),
+        Atom('Cu', [      7.083333333333333,      2.671601923551318,     -2.146500000000000]),
+        Atom('Cu', [      8.421531840490820,      1.905881433340739,     -0.202086055768265]),
+        Atom('Cu', [      9.783333333333335,      2.671601923551318,     -2.146500000000000]),
+        Atom('Cu', [      1.671531840490742,      4.244150023558601,     -0.202086055768165]),
+        Atom('Cu', [      3.033333333333334,      5.009870513769302,     -2.146500000000000]),
+        Atom('Cu', [      4.371531840490840,      4.244150023558694,     -0.202086055768265]),
+        Atom('Cu', [      5.733333333333333,      5.009870513769302,     -2.146500000000000]),
+        Atom('Cu', [      7.071531840490880,      4.244150023558786,     -0.202086055768352]),
+        Atom('Cu', [      8.433333333333335,      5.009870513769302,     -2.146500000000000]),
+        Atom('Cu', [      9.771531840491031,      4.244150023558828,     -0.202086055768371]),
+        Atom('Cu', [     11.133333333333335,      5.009870513769302,     -2.146500000000000]),
+        Atom('Cu', [      3.021531840490714,      6.582418613776583,     -0.202086055768197]),
+        Atom('Cu', [      4.383333333333334,      7.348139103987287,     -2.146500000000000]),
+        Atom('Cu', [      5.721531840490814,      6.582418613776629,     -0.202086055768203]),
+        Atom('Cu', [      7.083333333333333,      7.348139103987287,     -2.146500000000000]),
+        Atom('Cu', [      8.421531840490985,      6.582418613776876,     -0.202086055768357]),
+        Atom('Cu', [      9.783333333333335,      7.348139103987287,     -2.146500000000000]),
+        Atom('Cu', [     11.121531840490929,      6.582418613776676,     -0.202086055768221]),
+        Atom('Cu', [     12.483333333333334,      7.348139103987287,     -2.146500000000000]),
+    ])
+    mask = [a.position[2] < -1 for a in slab]
+    slab.set_constraint(FixAtoms(mask=mask))
+
+    a = 2.70
+    c = 1.59 * a
+    h = 1.85
+    d = 1.10
+    x = slab.positions[0, 2] / (c / 2) * 100
+
+    molecule = Atoms('2N', positions=[(0., 0., h),
+                                      (0., 0., h + d)])
+    molecule.set_calculator(EMT())
+    slab.extend(molecule)
+    return slab
+
+def get_calculator():
+    return EMT()
+
+run_test(get_atoms_surf, get_calculator, name + '-surf', steps=200)
+run_test(get_atoms_adsorbate, get_calculator, name + '-N2', steps=200)
diff --git a/ase/optimize/test/__init__.py b/ase/optimize/test/__init__.py
new file mode 100644
index 0000000..c68fda7
--- /dev/null
+++ b/ase/optimize/test/__init__.py
@@ -0,0 +1,152 @@
+"""Define a helper function for running tests
+
+The skeleton for making a new setup is as follows:
+
+from ase.optimize.test import run_test
+
+def get_atoms():
+    return Atoms('H')
+
+def get_calculator():
+    return EMT()
+
+run_test(get_atoms, get_calculator, 'Hydrogen')
+"""
+import matplotlib
+matplotlib.rcParams['backend']="Agg"
+
+from ase.optimize.bfgs import BFGS
+from ase.optimize.lbfgs import LBFGS, LBFGSLineSearch
+from ase.optimize.fire import FIRE
+from ase.optimize.mdmin import MDMin
+from ase.optimize.sciopt import SciPyFminCG
+from ase.optimize.sciopt import SciPyFminBFGS
+from ase.optimize.bfgslinesearch import BFGSLineSearch
+
+from ase.parallel import rank, paropen
+
+import matplotlib.pyplot as pl
+import numpy as np
+
+import traceback
+
+optimizers = [
+    'BFGS',
+    'LBFGS',
+    'LBFGSLineSearch',
+    'FIRE',
+    'MDMin',
+    'SciPyFminCG',
+    'SciPyFminBFGS',
+    'BFGSLineSearch'
+]
+
+def get_optimizer(optimizer):
+    if optimizer == 'BFGS': return BFGS
+    elif optimizer == 'LBFGS': return LBFGS
+    elif optimizer == 'LBFGSLineSearch': return LBFGSLineSearch
+    elif optimizer == 'FIRE': return FIRE
+    elif optimizer == 'MDMin': return MDMin
+    elif optimizer == 'SciPyFminCG': return SciPyFminCG
+    elif optimizer == 'SciPyFminBFGS': return SciPyFminBFGS
+    elif optimizer == 'BFGSLineSearch': return BFGSLineSearch
+
+def run_test(get_atoms, get_calculator, name,
+             fmax=0.05, steps=100, plot=True):
+
+    plotter = Plotter(name, fmax)
+    csvwriter = CSVWriter(name)
+    for optimizer in optimizers:
+        note = ''
+        logname = name + '-' + optimizer
+
+        atoms = get_atoms()
+        atoms.set_calculator(get_calculator())
+        opt = get_optimizer(optimizer)
+        relax = opt(atoms, logfile=None)
+                    #logfile = logname + '.log',
+                    #trajectory = logname + '.traj')
+
+        obs = DataObserver(atoms)
+        relax.attach(obs)
+        try:
+            relax.run(fmax = fmax, steps = steps)
+            E = atoms.get_potential_energy()
+
+            if relax.get_number_of_steps() == steps:
+                note = 'Not converged in %i steps' % steps
+        except Exception:
+            traceback.print_exc()
+            note = 'An exception occurred'
+            E = np.nan
+
+        nsteps = relax.get_number_of_steps()
+        if hasattr(relax, 'force_calls'):
+            fc = relax.force_calls
+            if rank == 0:
+                print '%-15s %-15s %3i %8.3f (%3i) %s' % (name, optimizer, nsteps, E, fc, note)
+        else:
+            fc = nsteps
+            if rank == 0:
+                print '%-15s %-15s %3i %8.3f       %s' % (name, optimizer, nsteps, E, note)
+
+        plotter.plot(optimizer, obs.get_E(), obs.get_fmax())
+        csvwriter.write(optimizer, nsteps, E, fc, note)
+
+    plotter.save()
+    csvwriter.finalize()
+
+class Plotter:
+    def __init__(self, name, fmax):
+        self.name = name
+        self.fmax = fmax
+        if rank == 0: 
+            self.fig = pl.figure(figsize=[12.0, 9.0])
+            self.axes0 = self.fig.add_subplot(2, 1, 1)
+            self.axes1 = self.fig.add_subplot(2, 1, 2)
+
+    def plot(self, optimizer, E, fmax):
+        if rank == 0:
+            self.axes0.plot(E, label = optimizer)
+            self.axes1.plot(fmax)
+
+    def save(self, format='png'):
+        if rank == 0:
+            self.axes0.legend()
+            self.axes0.set_title(self.name)
+            self.axes0.set_ylabel('E [eV]')
+            #self.axes0.set_yscale('log')
+
+            self.axes1.set_xlabel('steps')
+            self.axes1.set_ylabel('fmax [eV/A]')
+            self.axes1.set_yscale('log')
+            self.axes1.axhline(self.fmax, color='k', linestyle='--')
+            self.fig.savefig(self.name + '.' + format)
+
+class CSVWriter:
+    def __init__(self, name):
+        self.f = paropen(name + '.csv', 'w')
+
+    def write(self, optimizer, nsteps, E, fc, note=''):
+        self.f.write(
+            '%s,%i,%i,%f,%s\n' % (optimizer, nsteps, fc, E, note)
+        )
+
+    def finalize(self):
+        self.f.close()
+
+class DataObserver:
+    def __init__(self, atoms):
+        self.atoms = atoms
+        self.E = []
+        self.fmax = []
+
+    def __call__(self):
+        self.E.append(self.atoms.get_potential_energy())
+        self.fmax.append(np.sqrt((self.atoms.get_forces()**2).sum(axis=1)).max())
+
+    def get_E(self):
+        return np.array(self.E)
+
+    def get_fmax(self):
+        return np.array(self.fmax)
diff --git a/ase/optimize/test/generate_rst.py b/ase/optimize/test/generate_rst.py
new file mode 100644
index 0000000..5b8d090
--- /dev/null
+++ b/ase/optimize/test/generate_rst.py
@@ -0,0 +1,45 @@
+import os
+import re
+dirlist = os.listdir('.')
+name = '.*\.csv'
+filterre = re.compile(name)
+dirlist = filter(filterre.search, dirlist)
+namelist = [d.strip('.csv') for d in dirlist]
+
+f = open('testoptimize.rst', 'w')
+f.write(
+""".. _optimizer_tests:
+
+===============
+Optimizer tests
+===============
+This page shows benchmarks of optimizations done with our different optimizers.
+Note that the iteration number (steps) is not the same as the number of force
+evaluations. This is because some of the optimizers uses internal line searches
+or similar.
+"""
+)
+
+for name in namelist:
+    lines = open(name + '.csv', 'r').read().split('\n')
+    firstline = lines.pop(0)
+    f.write(
+        '\n' + 
+        name + '\n' + \
+        '=' * len(name) + '\n'
+        'Calculator used: %s\n' % firstline.split(',')[-1] + \
+        '\n' + \
+        '=============== ===== ================= ========== ===============\n' + \
+        'Optimizer       Steps Force evaluations Energy     Note           \n' + \
+        '=============== ===== ================= ========== ===============\n'
+    )
+    for line in lines:
+        if len(line):
+            print line.split(',')
+            f.write(
+                '%-15s %5s %17s %10s %s\n' % tuple(line.split(','))
+            )
+    f.write(
+        '=============== ===== ================= ========== ===============\n'
+    )
+f.close()
diff --git a/ase/optimize/test/nanoparticle.py b/ase/optimize/test/nanoparticle.py
new file mode 100644
index 0000000..d1662fc
--- /dev/null
+++ b/ase/optimize/test/nanoparticle.py
@@ -0,0 +1,45 @@
+#/usr/bin/env python
+#PBS -l nodes=4:ppn=8
+#PBS -l walltime=02:15:00
+
+from ase import Atom, Atoms
+from ase.io import read
+from ase.constraints import FixAtoms
+from ase.optimize.test import run_test
+from gpaw import GPAW
+from gpaw import Mixer
+from gpaw.poisson import PoissonSolver
+
+name = 'nanoparticle'
+
+def get_atoms():
+    atoms = Atoms([
+        Atom('Pd', [5.078689759346383, 5.410678028467162, 4.000000000000000]),
+        Atom('Pd', [7.522055777772603, 4.000000000000000, 4.000000000000000]),
+        Atom('Pd', [7.522055777772603, 6.821356056934325, 4.000000000000000]),
+        Atom('Pd', [6.707600438297196, 5.410678028467162, 6.303627574066606]),
+        Atom('N',  [4.807604264052752, 5.728625577716107, 5.919407072553396]),
+        Atom('H',  [4.000000000000000, 5.965167390141987, 6.490469524180266]),
+    ])
+
+    constraint = FixAtoms(mask=[a.symbol == 'Pd' for a in atoms])
+    atoms.set_constraint(constraint)
+    atoms.center(vacuum=4.0)
+    atoms.set_pbc(False)
+    return atoms
+
+def get_calculator():
+    calc = GPAW(gpts=(64, 64, 64), #h=0.18, gives 64x60x60
+                mode='lcao', 
+                basis='szp(dzp)',
+                nbands=-5,
+                xc='LDA',
+                width=0.1,
+                mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0),
+                poissonsolver=PoissonSolver(nn='M', relax='GS'),
+                convergence={'energy': 1e-4, 'bands': -3},
+                stencils=(3, 3),
+                txt='nanoparticle.txt')
+    return calc
+
+run_test(get_atoms, get_calculator, name, fmax=0.05, steps=200)
diff --git a/ase/optimize/test/neb.py b/ase/optimize/test/neb.py
new file mode 100644
index 0000000..01b2c71
--- /dev/null
+++ b/ase/optimize/test/neb.py
@@ -0,0 +1,83 @@
+#/usr/bin/env python
+#PBS -l nodes=4:ppn=8
+#PBS -l walltime=13:00:00
+from ase.optimize import QuasiNewton
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.neb import NEB
+from ase.lattice.surface import fcc100, add_adsorbate
+from ase.optimize.test import run_test
+from gpaw import GPAW, Mixer, PoissonSolver
+import gpaw.mpi as mpi
+
+
+name = 'neb'
+
+def get_atoms():
+    # 2x2-Al(001) surface with 3 layers and an
+    # Au atom adsorbed in a hollow site:
+    slab = fcc100('Al', size=(2, 2, 3))
+    add_adsorbate(slab, 'Au', 1.7, 'hollow')
+    slab.center(axis=2, vacuum=4.0)
+
+    # Fix second and third layers:
+    mask = [atom.tag > 1 for atom in slab]
+    slab.set_constraint(FixAtoms(mask=mask))
+
+    # Use EMT potential:
+    slab.set_calculator(EMT())
+
+    # Initial state:
+    qn = QuasiNewton(slab, logfile=None)
+    qn.run(fmax=0.05)
+    initial = slab.copy()
+
+    # Final state:
+    slab[-1].x += slab.get_cell()[0, 0] / 2
+    qn = QuasiNewton(slab, logfile=None)
+    qn.run(fmax=0.05)
+    final = slab.copy()
+
+    # Setup a NEB calculation
+    constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial])
+
+    images = [initial]
+    for i in range(3):
+        image = initial.copy()
+        image.set_constraint(constraint)
+        images.append(image)
+
+    images.append(final)
+
+    neb = NEB(images, parallel=mpi.parallel)
+    neb.interpolate()
+
+    def set_calculator(calc):
+        i = 0
+        for image in neb.images[1:-1]:
+            if not mpi.parallel or mpi.rank // (mpi.size // 3) == i:
+                image.set_calculator(calc)
+            i += 1
+    neb.set_calculator = set_calculator
+
+    return neb
+
+def get_calculator_emt():
+    return EMT()
+
+def get_calculator_gpaw():
+    if mpi.parallel:
+        assert mpi.size % 3 == 0
+        s = mpi.size // 3
+        r0 = mpi.rank // s * s
+        comm = range(r0, r0 + s)
+    else:
+        comm = mpi.world
+    calc = GPAW(h=0.25,
+                kpts=(2, 2, 1),
+                communicator=comm,
+                txt='neb-%d.txt' % r0)
+    return calc
+
+run_test(get_atoms, get_calculator_emt, name + '-emt')
+run_test(get_atoms, get_calculator_gpaw, name + '-gpaw')
diff --git a/ase/optimize/test/test.py b/ase/optimize/test/test.py
new file mode 100644
index 0000000..cefe82b
--- /dev/null
+++ b/ase/optimize/test/test.py
@@ -0,0 +1,17 @@
+import os
+import ase
+
+tests = [
+    'N2Cu_relax.py',
+    'Cu_bulk.py',
+    'CO_Au111.py',
+    'H2.py',
+    'C5H12.py',
+    'nanoparticle.py', # SLOW
+#    'C2_Cu100.py', # Extremely slow
+]
+
+for test in tests:
+    filename = ase.__path__[0] + '/optimize/test/' + test
+    execfile(filename, {})
+
diff --git a/ase/parallel.py b/ase/parallel.py
new file mode 100644
index 0000000..a9a5628
--- /dev/null
+++ b/ase/parallel.py
@@ -0,0 +1,138 @@
+import sys
+import time
+import atexit
+
+import numpy as np
+
+
+def paropen(name, mode='r', buffering=0):
+    """MPI-safe version of open function.
+
+    In read mode, the file is opened on all nodes.  In write and
+    append mode, the file is opened on the master only, and /dev/null
+    is opened on all other nodes.
+    """
+    if rank > 0 and mode[0] != 'r':
+        name = '/dev/null'
+    return open(name, mode, buffering)
+
+
+def parprint(*args, **kwargs):
+    """MPI-safe print - prints only from master.
+
+    Tries to adopt python 3 behaviour.
+    """
+    if rank > 0:
+        return
+    defaults = {'end': '\n',
+                'file': sys.stdout }
+    for key in defaults:
+        if not key in kwargs:
+            kwargs[key] = defaults[key]
+
+    for arg in args[:-1]:
+        print >> kwargs['file'], arg,
+    if len(args):
+        last = args[-1]
+    else:
+        last = ''
+    if kwargs['end'] == '\n':
+        print >> kwargs['file'], last
+    else:
+        print >> kwargs['file'], last,
+
+
+class DummyMPI:
+    rank = 0
+    size = 1
+    def sum(self, a):
+        if isinstance(a, np.ndarray) and a.ndim > 0:
+            pass
+        else:
+            return a
+    
+    def barrier(self):
+        pass
+
+    def broadcast(self, a, rank):
+        pass
+
+
+class MPI4PY:
+    def __init__(self):
+        from mpi4py import MPI
+        self.comm = MPI.COMM_WORLD
+        self.rank = self.comm.rank
+        self.size = self.comm.size
+
+    def sum(self, a):
+        return self.comm.allreduce(a)
+    
+    def barrier(self):
+        self.comm.barrier()
+
+    def broadcast(self, a, rank):
+        a[:] = self.comm.bcast(a, rank)
+
+
+# Check for special MPI-enabled Python interpreters:
+if '_gpaw' in sys.modules:
+    # http://wiki.fysik.dtu.dk/gpaw
+    from gpaw.mpi import world
+elif 'asapparallel3' in sys.modules:
+    # http://wiki.fysik.dtu.dk/Asap
+    # We cannot import asap3.mpi here, as that creates an import deadlock
+    #from asap3.mpi import world
+    import asapparallel3
+    world = asapparallel3.Communicator()
+elif 'Scientific_mpi' in sys.modules:
+    from Scientific.MPI import world
+elif 'mpi4py' in sys.modules:
+    world = MPI4PY()
+else:
+    # This is a standard Python interpreter:
+    world = DummyMPI()
+
+rank = world.rank
+size = world.size
+barrier = world.barrier
+
+
+def register_parallel_cleanup_function():
+    """Call MPI_Abort if python crashes.
+
+    This will terminate the processes on the other nodes."""
+        
+    if size == 1:
+        return
+
+    def cleanup(sys=sys, time=time, world=world):
+        error = getattr(sys, 'last_type', None)
+        if error:
+            sys.stdout.flush()
+            sys.stderr.write(('ASE CLEANUP (node %d): %s occurred.  ' +
+                              'Calling MPI_Abort!\n') % (world.rank, error))
+            sys.stderr.flush()
+            # Give other nodes a moment to crash by themselves (perhaps
+            # producing helpful error messages):
+            time.sleep(3)
+            world.abort(42)
+
+    atexit.register(cleanup)
+
+def distribute_cpus(parsize_calculator, comm):
+    """Distribute cpus to tasks and calculators"""
+    
+    assert parsize_calculator <= comm.size
+    assert comm.size % parsize_calculator == 0
+
+    tasks_rank = comm.rank // parsize_calculator
+
+    r0 = tasks_rank * parsize_calculator
+    ranks = np.arange(r0, r0 + parsize_calculator)
+    calc_comm = comm.new_communicator(ranks)
+#    print 'comm.rank, ranks=', comm.rank, ranks
+
+    tasks_comm = np.arange(0, comm.size, parsize_calculator)
+
+    return calc_comm, tasks_comm, tasks_rank
diff --git a/ase/phonons.py b/ase/phonons.py
new file mode 100644
index 0000000..3940bda
--- /dev/null
+++ b/ase/phonons.py
@@ -0,0 +1,766 @@
+"""Module for calculating phonons of periodic systems."""
+
+import sys
+import pickle
+from math import sin, pi, sqrt
+from os import remove
+from os.path import isfile
+
+import numpy as np
+import numpy.linalg as la
+import numpy.fft as fft
+
+has_spglib = False
+try:
+    from pyspglib import spglib
+    has_spglib = True
+except ImportError:
+    pass
+
+import ase.units as units
+from ase.parallel import rank, barrier
+from ase.dft import monkhorst_pack
+from ase.io.trajectory import PickleTrajectory
+
+class Displacement:
+    """Abstract base class for phonon and el-ph supercell calculations.
+
+    Both phonons and the electron-phonon interaction in periodic systems can be
+    calculated with the so-called finite-displacement method where the
+    derivatives of the total energy and effective potential are obtained from
+    finite-difference approximations, i.e. by displacing the atoms. This class
+    provides the required functionality for carrying out the calculations for
+    the different displacements in its ``run`` member function.
+
+    Derived classes must overwrite the ``__call__`` member function which is
+    called for each atomic displacement.
+    
+    """
+
+    def __init__(self, atoms, calc=None, supercell=(1, 1, 1), name=None,
+                 delta=0.01, refcell=None):
+        """Init with an instance of class ``Atoms`` and a calculator.
+
+        Parameters
+        ----------
+        atoms: Atoms object
+            The atoms to work on.
+        calc: Calculator
+            Calculator for the supercell calculation.
+        supercell: tuple
+            Size of supercell given by the number of repetitions (l, m, n) of
+            the small unit cell in each direction.
+        name: str
+            Base name to use for files.
+        delta: float
+            Magnitude of displacement in Ang.
+        refcell: str
+            Reference cell in which the atoms will be displaced. If ``None``,
+            corner cell in supercell is used. If ``str``, cell in the center of
+            the supercell is used.
+
+        """
+
+        # Store atoms and calculator
+        self.atoms = atoms
+        self.calc = calc
+        
+        # Displace all atoms in the unit cell by default
+        self.indices = range(len(atoms))
+        self.name = name
+        self.delta = delta
+        self.N_c = supercell
+
+        # Reference cell offset
+        if refcell is None:
+            # Corner cell
+            self.offset = 0
+        else:
+            # Center cell
+            N_c = self.N_c
+            self.offset = N_c[0] // 2 * (N_c[1] * N_c[2]) + N_c[1] // \
+                          2 * N_c[2] + N_c[2] // 2        
+
+    def __call__(self, *args, **kwargs):
+        """Member function called in the ``run`` function."""
+
+        raise NotImplementedError("Implement in derived classes!.")
+    
+    def set_atoms(self, atoms):
+        """Set the atoms to vibrate.
+
+        Parameters
+        ----------
+        atoms: list
+            Can be either a list of strings, ints or ...
+            
+        """
+        
+        assert isinstance(atoms, list)
+        assert len(atoms) <= len(self.atoms)
+        
+        if isinstance(atoms[0], str):
+            assert np.all([isinstance(atom, str) for atom in atoms])
+            sym_a = self.atoms.get_chemical_symbols()
+            # List for atomic indices
+            indices = []
+            for type in atoms:
+                indices.extend([a for a, atom in enumerate(sym_a)
+                                if atom == type])
+        else:
+            assert np.all([isinstance(atom, int) for atom in atoms])
+            indices = atoms
+
+        self.indices = indices
+
+    def lattice_vectors(self):
+        """Return lattice vectors for cells in the supercell."""
+
+        # Lattice vectors relevative to the reference cell
+        R_cN = np.indices(self.N_c).reshape(3, -1)
+        N_c = np.array(self.N_c)[:, np.newaxis]
+        if self.offset == 0:
+            R_cN += N_c // 2
+            R_cN %= N_c
+        R_cN -= N_c // 2
+
+        return R_cN
+    
+    def run(self):
+        """Run the calculations for the required displacements.
+
+        This will do a calculation for 6 displacements per atom, +-x, +-y, and
+        +-z. Only those calculations that are not already done will be
+        started. Be aware that an interrupted calculation may produce an empty
+        file (ending with .pckl), which must be deleted before restarting the
+        job. Otherwise the calculation for that displacement will not be done.
+
+        """
+
+        # Atoms in the supercell -- repeated in the lattice vector directions
+        # beginning with the last
+        atoms_N = self.atoms * self.N_c
+        
+        # Set calculator if provided
+        assert self.calc is not None, "Provide calculator in __init__ method"
+        atoms_N.set_calculator(self.calc)
+        
+        # Do calculation on equilibrium structure
+        filename = self.name + '.eq.pckl'
+        
+        if not isfile(filename):
+            # Wait for all ranks to enter
+            barrier()
+            # Create file
+            if rank == 0:
+                fd = open(filename, 'w')
+                fd.close()
+
+            # Call derived class implementation of __call__
+            output = self.__call__(atoms_N)
+            # Write output to file
+            if rank == 0:
+                fd = open(filename, 'w')
+                pickle.dump(output, fd)
+                sys.stdout.write('Writing %s\n' % filename)
+                fd.close()
+            sys.stdout.flush()
+
+        # Positions of atoms to be displaced in the reference cell
+        natoms = len(self.atoms)
+        offset = natoms * self.offset
+        pos = atoms_N.positions[offset: offset + natoms].copy()
+        
+        # Loop over all displacements
+        for a in self.indices:
+            for i in range(3):
+                for sign in [-1, 1]:
+                    # Filename for atomic displacement
+                    filename = '%s.%d%s%s.pckl' % \
+                               (self.name, a, 'xyz'[i], ' +-'[sign])
+                    # Wait for ranks before checking for file
+                    # barrier()                    
+                    if isfile(filename):
+                        # Skip if already done
+                        continue
+                    # Wait for ranks
+                    barrier()
+                    if rank == 0:
+                        fd = open(filename, 'w')
+                        fd.close()
+
+                    # Update atomic positions
+                    atoms_N.positions[offset + a, i] = \
+                        pos[a, i] + sign * self.delta
+                    
+                    # Call derived class implementation of __call__
+                    output = self.__call__(atoms_N)
+                    # Write output to file    
+                    if rank == 0:
+                        fd = open(filename, 'w')
+                        pickle.dump(output, fd)
+                        sys.stdout.write('Writing %s\n' % filename)
+                        fd.close()
+                    sys.stdout.flush()
+                    # Return to initial positions
+                    atoms_N.positions[offset + a, i] = pos[a, i]
+        
+    def clean(self):
+        """Delete generated pickle files."""
+        
+        if isfile(self.name + '.eq.pckl'):
+            remove(self.name + '.eq.pckl')
+        
+        for a in self.indices:
+            for i in 'xyz':
+                for sign in '-+':
+                    name = '%s.%d%s%s.pckl' % (self.name, a, i, sign)
+                    if isfile(name):
+                        remove(name)
+
+
+class Phonons(Displacement):
+    """Class for calculating phonon modes using the finite displacement method.
+
+    The matrix of force constants is calculated from the finite difference
+    approximation to the first-order derivative of the atomic forces as::
+    
+                            2             nbj   nbj
+                nbj        d E           F-  - F+
+               C     = ------------ ~  -------------  ,
+                mai     dR   dR          2 * delta
+                          mai  nbj       
+
+    where F+/F- denotes the force in direction j on atom nb when atom ma is
+    displaced in direction +i/-i. The force constants are related by various
+    symmetry relations. From the definition of the force constants it must
+    be symmetric in the three indices mai::
+
+                nbj    mai         bj        ai
+               C    = C      ->   C  (R ) = C  (-R )  .
+                mai    nbj         ai  n     bj   n
+
+    As the force constants can only depend on the difference between the m and
+    n indices, this symmetry is more conveniently expressed as shown on the
+    right hand-side.
+
+    The acoustic sum-rule::
+
+                           _ _
+                aj         \    bj    
+               C  (R ) = -  )  C  (R )
+                ai  0      /__  ai  m
+                          (m, b)
+                            !=
+                          (0, a)
+                        
+    Ordering of the unit cells illustrated here for a 1-dimensional system (in
+    case ``refcell=None`` in constructor!):
+    
+    ::
+    
+               m = 0        m = 1        m = -2        m = -1
+           -----------------------------------------------------
+           |            |            |            |            |
+           |        * b |        *   |        *   |        *   |
+           |            |            |            |            |
+           |   * a      |   *        |   *        |   *        |
+           |            |            |            |            |
+           -----------------------------------------------------
+       
+    Example:
+
+    >>> from ase.structure import bulk
+    >>> from ase.phonons import Phonons
+    >>> from gpaw import GPAW, FermiDirac
+    >>> atoms = bulk('Si', 'diamond', a=5.4)
+    >>> calc = GPAW(kpts=(5, 5, 5),
+                    h=0.2,
+                    occupations=FermiDirac(0.))
+    >>> ph = Phonons(atoms, calc, supercell=(5, 5, 5))
+    >>> ph.run()
+    >>> ph.read(method='frederiksen', acoustic=True)
+
+    """
+
+    def __init__(self, *args, **kwargs):
+        """Initialize with base class args and kwargs."""
+
+        if 'name' not in kwargs.keys():
+            kwargs['name'] = "phonon"
+            
+        Displacement.__init__(self, *args, **kwargs)
+        
+        # Attributes for force constants and dynamical matrix in real space
+        self.C_N = None  # in units of eV / Ang**2 
+        self.D_N = None  # in units of eV / Ang**2 / amu
+        
+        # Attributes for born charges and static dielectric tensor
+        self.Z_avv = None
+        self.eps_vv = None
+   
+    def __call__(self, atoms_N):
+        """Calculate forces on atoms in supercell."""
+
+        # Calculate forces
+        forces = atoms_N.get_forces()
+
+        return forces
+
+    def check_eq_forces(self):
+        """Check maximum size of forces in the equilibrium structure."""
+
+        fname = '%s.eq.pckl' % self.name
+        feq_av = pickle.load(open(fname))
+
+        fmin = feq_av.max()
+        fmax = feq_av.min()
+        i_min = np.where(feq_av == fmin)
+        i_max = np.where(feq_av == fmax)
+
+        return fmin, fmax, i_min, i_max
+    
+    def read_born_charges(self, name=None, neutrality=True):
+        """Read Born charges and dieletric tensor from pickle file.
+
+        The charge neutrality sum-rule::
+    
+                   _ _
+                   \    a    
+                    )  Z   = 0
+                   /__  ij
+                    a
+                              
+        Parameters
+        ----------
+        neutrality: bool
+            Restore charge neutrality condition on calculated Born effective
+            charges. 
+
+        """
+
+        # Load file with Born charges and dielectric tensor for atoms in the
+        # unit cell
+        if name is None:
+            filename = '%s.born.pckl' % self.name
+        else:
+            filename = name
+            
+        fd = open(filename)
+        Z_avv, eps_vv = pickle.load(fd)
+        fd.close()
+
+        # Neutrality sum-rule
+        if neutrality:
+            Z_mean = Z_avv.sum(0) / len(Z_avv)
+            Z_avv -= Z_mean
+        
+        self.Z_avv = Z_avv[self.indices]
+        self.eps_vv = eps_vv
+        
+    def read(self, method='Frederiksen', symmetrize=3, acoustic=True,
+             cutoff=None, born=False, **kwargs):
+        """Read forces from pickle files and calculate force constants.
+
+        Extra keyword arguments will be passed to ``read_born_charges``.
+        
+        Parameters
+        ----------
+        method: str
+            Specify method for evaluating the atomic forces.
+        symmetrize: int
+            Symmetrize force constants (see doc string at top) when
+            ``symmetrize != 0`` (default: 3). Since restoring the acoustic sum
+            rule breaks the symmetry, the symmetrization must be repeated a few
+            times until the changes a insignificant. The integer gives the
+            number of iterations that will be carried out.
+        acoustic: bool
+            Restore the acoustic sum rule on the force constants.
+        cutoff: None or float
+            Zero elements in the dynamical matrix between atoms with an
+            interatomic distance larger than the cutoff.
+        born: bool
+            Read in Born effective charge tensor and high-frequency static
+            dielelctric tensor from file.
+            
+        """
+
+        method = method.lower()
+        assert method in ['standard', 'frederiksen']
+        if cutoff is not None:
+            cutoff = float(cutoff)
+            
+        # Read Born effective charges and optical dielectric tensor
+        if born:
+            self.read_born_charges(**kwargs)
+        
+        # Number of atoms
+        natoms = len(self.indices)
+        # Number of unit cells
+        N = np.prod(self.N_c)
+        # Matrix of force constants as a function of unit cell index in units
+        # of eV / Ang**2
+        C_xNav = np.empty((natoms * 3, N, natoms, 3), dtype=float)
+
+        # Loop over all atomic displacements and calculate force constants
+        for i, a in enumerate(self.indices):
+            for j, v in enumerate('xyz'):
+                # Atomic forces for a displacement of atom a in direction v
+                basename = '%s.%d%s' % (self.name, a, v)
+                fminus_av = pickle.load(open(basename + '-.pckl'))
+                fplus_av = pickle.load(open(basename + '+.pckl'))
+                
+                if method == 'frederiksen':
+                    fminus_av[a] -= fminus_av.sum(0)
+                    fplus_av[a]  -= fplus_av.sum(0)
+                    
+                # Finite difference derivative
+                C_av = fminus_av - fplus_av
+                C_av /= 2 * self.delta
+
+                # Slice out included atoms
+                C_Nav = C_av.reshape((N, len(self.atoms), 3))[:, self.indices]
+                index = 3*i + j                
+                C_xNav[index] = C_Nav
+
+        # Make unitcell index the first and reshape
+        C_N = C_xNav.swapaxes(0 ,1). reshape((N,) + (3 * natoms, 3 * natoms))
+
+        # Cut off before symmetry and acoustic sum rule are imposed
+        if cutoff is not None:
+            self.apply_cutoff(C_N, cutoff)
+            
+        # Symmetrize force constants
+        if symmetrize:
+            for i in range(symmetrize):
+                # Symmetrize
+                C_N = self.symmetrize(C_N)
+                # Restore acoustic sum-rule
+                if acoustic:
+                    self.acoustic(C_N)
+                else:
+                    break
+             
+        # Store force constants and dynamical matrix
+        self.C_N = C_N
+        self.D_N = C_N.copy()
+        
+        # Add mass prefactor
+        m_a = self.atoms.get_masses()
+        self.m_inv_x = np.repeat(m_a[self.indices]**-0.5, 3)
+        M_inv = np.outer(self.m_inv_x, self.m_inv_x)
+        for D in self.D_N:
+            D *= M_inv
+
+    def symmetrize(self, C_N):
+        """Symmetrize force constant matrix."""
+
+        # Number of atoms
+        natoms = len(self.indices)
+        # Number of unit cells
+        N = np.prod(self.N_c)
+
+        # Reshape force constants to (l, m, n) cell indices
+        C_lmn = C_N.reshape(self.N_c + (3 * natoms, 3 * natoms))
+
+        # Shift reference cell to center index 
+        if self.offset == 0:
+            C_lmn = fft.fftshift(C_lmn, axes=(0, 1, 2)).copy()
+        # Make force constants symmetric in indices -- in case of an even
+        # number of unit cells don't include the first
+        i, j, k = np.asarray(self.N_c) % 2 - 1
+        C_lmn[i:, j:, k:] *= 0.5
+        C_lmn[i:, j:, k:] += \
+                  C_lmn[i:, j:, k:][::-1, ::-1, ::-1].transpose(0, 1, 2, 4, 3).copy()
+        if self.offset == 0:
+            C_lmn = fft.ifftshift(C_lmn, axes=(0, 1, 2)).copy()
+
+        # Change to single unit cell index shape
+        C_N = C_lmn.reshape((N, 3 * natoms, 3 * natoms))
+
+        return C_N
+       
+    def acoustic(self, C_N):
+        """Restore acoustic sumrule on force constants."""
+
+        # Number of atoms
+        natoms = len(self.indices)
+        # Copy force constants
+        C_N_temp = C_N.copy()
+        
+        # Correct atomic diagonals of R_m = (0, 0, 0) matrix
+        for C in C_N_temp:
+            for a in range(natoms):
+                for a_ in range(natoms):
+                    C_N[self.offset, 3*a: 3*a + 3, 3*a: 3*a + 3] -= \
+                                     C[3*a: 3*a+3, 3*a_: 3*a_+3]
+                    
+    def apply_cutoff(self, D_N, r_c):
+        """Zero elements for interatomic distances larger than the cutoff.
+
+        Parameters
+        ----------
+        D_N: ndarray
+            Dynamical/force constant matrix.
+        r_c: float
+            Cutoff in Angstrom.
+
+        """
+
+        # Number of atoms and primitive cells
+        natoms = len(self.indices)
+        N = np.prod(self.N_c)
+        # Lattice vectors
+        R_cN = self.lattice_vectors()
+        # Reshape matrix to individual atomic and cartesian dimensions
+        D_Navav = D_N.reshape((N, natoms, 3, natoms, 3))
+
+        # Cell vectors
+        cell_vc = self.atoms.cell.transpose()
+        # Atomic positions in reference cell
+        pos_av = self.atoms.get_positions()
+        
+        # Zero elements with a distance to atoms in the reference cell
+        # larger than the cutoff
+        for n in range(N):
+            # Lattice vector to cell
+            R_v = np.dot(cell_vc, R_cN[:, n])
+            # Atomic positions in cell
+            posn_av = pos_av + R_v
+            # Loop over atoms and zero elements
+            for i, a in enumerate(self.indices):
+                dist_a = np.sqrt(np.sum((pos_av[a] - posn_av)**2, axis=-1))
+                # Atoms where the distance is larger than the cufoff
+                i_a = dist_a > r_c #np.where(dist_a > r_c)
+                # Zero elements
+                D_Navav[n, i, :, i_a, :] = 0.0
+            # print ""
+            
+    def get_force_constant(self):
+        """Return matrix of force constants."""
+
+        assert self.C_N is not None
+        
+        return self.C_N
+    
+    def band_structure(self, path_kc, modes=False, born=False, verbose=True):
+        """Calculate phonon dispersion along a path in the Brillouin zone.
+
+        The dynamical matrix at arbitrary q-vectors is obtained by Fourier
+        transforming the real-space force constants. In case of negative
+        eigenvalues (squared frequency), the corresponding negative frequency
+        is returned.
+
+        Eigenvalues and modes are in units of eV and Ang/sqrt(amu),
+        respectively.
+
+        Parameters
+        ----------
+        path_kc: ndarray
+            List of k-point coordinates (in units of the reciprocal lattice
+            vectors) specifying the path in the Brillouin zone for which the
+            dynamical matrix will be calculated.
+        modes: bool
+            Returns both frequencies and modes when True.
+        born: bool
+            Include non-analytic part given by the Born effective charges and
+            the static part of the high-frequency dielectric tensor. This
+            contribution to the force constant accounts for the splitting
+            between the LO and TO branches for q -> 0.
+        verbose: bool
+            Print warnings when imaginary frequncies are detected.
+        
+        """
+
+        assert self.D_N is not None
+        if born:
+            assert self.Z_avv is not None
+            assert self.eps_vv is not None
+
+        # Lattice vectors -- ordered as illustrated in class docstring
+        R_cN = self.lattice_vectors()
+
+        # Dynamical matrix in real-space
+        D_N = self.D_N
+        
+        # Lists for frequencies and modes along path
+        omega_kl = []
+        u_kl = []
+
+        # Reciprocal basis vectors for use in non-analytic contribution
+        reci_vc = 2 * pi * la.inv(self.atoms.cell)
+        # Unit cell volume in Bohr^3
+        vol = abs(la.det(self.atoms.cell)) / units.Bohr**3
+
+        for q_c in path_kc:
+           
+            # Add non-analytic part
+            if born:
+                # q-vector in cartesian coordinates
+                q_v = np.dot(reci_vc, q_c)
+                # Non-analytic contribution to force constants in atomic units
+                qdotZ_av = np.dot(q_v, self.Z_avv).ravel()
+                C_na = 4 * pi * np.outer(qdotZ_av, qdotZ_av) / \
+                       np.dot(q_v, np.dot(self.eps_vv, q_v)) / vol
+                self.C_na = C_na / units.Bohr**2 * units.Hartree                
+                # Add mass prefactor and convert to eV / (Ang^2 * amu)
+                M_inv = np.outer(self.m_inv_x, self.m_inv_x)                
+                D_na = C_na * M_inv / units.Bohr**2 * units.Hartree
+                self.D_na = D_na
+                D_N = self.D_N + D_na / np.prod(self.N_c) 
+
+            ## if np.prod(self.N_c) == 1:
+            ## 
+            ##     q_av = np.tile(q_v, len(self.indices))
+            ##     q_xx = np.vstack([q_av]*len(self.indices)*3)
+            ##     D_m += q_xx
+
+            # Evaluate fourier sum
+            phase_N = np.exp(-2.j * pi * np.dot(q_c, R_cN))
+            D_q = np.sum(phase_N[:, np.newaxis, np.newaxis] * D_N, axis=0)
+
+            if modes:
+                omega2_l, u_xl = la.eigh(D_q, UPLO='U')
+                # Sort eigenmodes according to eigenvalues (see below) and 
+                # multiply with mass prefactor
+                u_lx = (self.m_inv_x[:, np.newaxis] * 
+                        u_xl[:, omega2_l.argsort()]).T.copy()
+                u_kl.append(u_lx.reshape((-1, len(self.indices), 3)))
+            else:
+                omega2_l = la.eigvalsh(D_q, UPLO='U')
+
+            # Sort eigenvalues in increasing order
+            omega2_l.sort()
+            # Use dtype=complex to handle negative eigenvalues
+            omega_l = np.sqrt(omega2_l.astype(complex))
+
+            # Take care of imaginary frequencies
+            if not np.all(omega2_l >= 0.):
+                indices = np.where(omega2_l < 0)[0]
+
+                if verbose:
+                    print ("WARNING, %i imaginary frequencies at "
+                           "q = (% 5.2f, % 5.2f, % 5.2f) ; (omega_q =% 5.3e*i)"
+                           % (len(indices), q_c[0], q_c[1], q_c[2],
+                              omega_l[indices][0].imag))
+                
+                omega_l[indices] = -1 * np.sqrt(np.abs(omega2_l[indices].real))
+
+            omega_kl.append(omega_l.real)
+
+        # Conversion factor: sqrt(eV / Ang^2 / amu) -> eV
+        s = units._hbar * 1e10 / sqrt(units._e * units._amu)
+        omega_kl = s * np.asarray(omega_kl)
+        
+        if modes:
+            return omega_kl, np.asarray(u_kl)
+        
+        return omega_kl
+
+    def dos(self, kpts=(10, 10, 10), npts=1000, delta=1e-3, indices=None):
+        """Calculate phonon dos as a function of energy.
+
+        Parameters
+        ----------
+        qpts: tuple
+            Shape of Monkhorst-Pack grid for sampling the Brillouin zone.
+        npts: int
+            Number of energy points.
+        delta: float
+            Broadening of Lorentzian line-shape in eV.
+        indices: list
+            If indices is not None, the atomic-partial dos for the specified
+            atoms will be calculated.
+            
+        """
+
+        # Monkhorst-Pack grid
+        kpts_kc = monkhorst_pack(kpts)
+        N = np.prod(kpts)
+        # Get frequencies
+        omega_kl = self.band_structure(kpts_kc)
+        # Energy axis and dos
+        omega_e = np.linspace(0., np.amax(omega_kl) + 5e-3, num=npts)
+        dos_e = np.zeros_like(omega_e)
+       
+        # Sum up contribution from all q-points and branches
+        for omega_l in omega_kl:
+            diff_el = (omega_e[:, np.newaxis] - omega_l[np.newaxis, :])**2
+            dos_el = 1. / (diff_el + (0.5 * delta)**2)
+            dos_e += dos_el.sum(axis=1)
+
+        dos_e *= 1./(N * pi) * 0.5*delta
+        
+        return omega_e, dos_e
+    
+    def write_modes(self, q_c, branches=0, kT=units.kB*300, born=False,
+                    repeat=(1, 1, 1), nimages=30, center=False):
+        """Write modes to trajectory file.
+
+        Parameters
+        ----------
+        q_c: ndarray
+            q-vector of modes.
+        branches: int or list
+            Branch index of modes.
+        kT: float
+            Temperature in units of eV. Determines the amplitude of the atomic
+            displacements in the modes.
+        born: bool
+            Include non-analytic contribution to the force constants at q -> 0.
+        repeat: tuple
+            Repeat atoms (l, m, n) times in the directions of the lattice
+            vectors. Displacements of atoms in repeated cells carry a Bloch
+            phase factor given by the q-vector and the cell lattice vector R_m.
+        nimages: int
+            Number of images in an oscillation.
+        center: bool
+            Center atoms in unit cell if True (default: False).
+            
+        """
+
+        if isinstance(branches, int):
+            branch_l = [branches]
+        else:
+            branch_l = list(branches)
+
+        # Calculate modes
+        omega_l, u_l = self.band_structure([q_c], modes=True, born=born)
+        # Repeat atoms
+        atoms = self.atoms * repeat
+        # Center
+        if center:
+            atoms.center()
+        
+        # Here ``Na`` refers to a composite unit cell/atom dimension
+        pos_Nav = atoms.get_positions()
+        # Total number of unit cells
+        N = np.prod(repeat)
+
+        # Corresponding lattice vectors R_m
+        R_cN = np.indices(repeat).reshape(3, -1)
+        # Bloch phase
+        phase_N = np.exp(2.j * pi * np.dot(q_c, R_cN))
+        phase_Na = phase_N.repeat(len(self.atoms))
+
+        for l in branch_l:
+            
+            omega = omega_l[0, l]
+            u_av = u_l[0, l]
+
+            # Mean displacement of a classical oscillator at temperature T
+            u_av *= sqrt(kT) / abs(omega)
+
+            mode_av = np.zeros((len(self.atoms), 3), dtype=complex)
+            # Insert slice with atomic displacements for the included atoms
+            mode_av[self.indices] = u_av
+            # Repeat and multiply by Bloch phase factor
+            mode_Nav = (np.vstack(N * [mode_av]) * phase_Na[:, np.newaxis]).real
+            
+            traj = PickleTrajectory('%s.mode.%d.traj' % (self.name, l), 'w')
+            
+            for x in np.linspace(0, 2*pi, nimages, endpoint=False):
+                atoms.set_positions(pos_Nav + sin(x) * mode_Nav)
+                traj.write(atoms)
+                
+            traj.close()
diff --git a/ase/quaternions.py b/ase/quaternions.py
new file mode 100644
index 0000000..1e5d84e
--- /dev/null
+++ b/ase/quaternions.py
@@ -0,0 +1,122 @@
+import numpy as np
+from ase.atoms import Atoms
+
+class Quaternions(Atoms):
+    def __init__(self, *args, **kwargs):
+        quaternions = None
+        if 'quaternions' in kwargs:
+            quaternions = np.array(kwargs['quaternions'])
+            del kwargs['quaternions']
+        Atoms.__init__(self, *args, **kwargs)
+        if quaternions is not None:
+            self.set_array('quaternions', quaternions, shape=(4,))
+        # set default shapes
+        self.set_shapes(np.array([[5, 3, 1]] * len(self)))
+
+    def set_shapes(self, shapes):
+        self.set_array('shapes', shapes, shape=(3,))
+
+    def set_quaternions (self, quaternions):
+        self.set_array('quaternions', quaternions, quaternion=(4,))
+
+    def get_shapes(self):
+        return self.get_array('shapes')
+
+    def get_quaternions(self):
+        return self.get_array('quaternions')
+
+class Quaternion:
+    def __init__(self, qin=[1, 0, 0, 0]):
+        assert(len(qin) == 4)
+        self.q = np.array(qin)
+
+    def __str__(self):
+        return self.q.__str__()
+
+    def __mult__(self, other):
+        sw, sx, sy, sz = self.q[0], self.q[1], self.q[2], self.q[3]
+        ow, ox, oy, oz = other.q[0], other.q[1], other.q[2], other.q[3]
+        return Quaternion([sw*ow - sx*ox - sy*oy - sz*oz,
+                           sw*ox + sx*ow + sy*oz - sz*oy,
+                           sw*oy + sy*ow + sz*ox - sx*oz,
+                           sw*oz + sz*ow + sx*oy - sy*ox])
+
+    def conjugate(self):
+        return Quaternion(-self.q * np.array([-1., 1., 1., 1.]))
+
+    def rotate(self, vector):
+        """Apply the rotation matrix to a vector."""
+        qw, qx, qy, qz = self.q[0], self.q[1], self.q[2], self.q[3]
+        x, y, z = vector[0], vector[1], vector[2]
+	
+        ww = qw * qw
+        xx = qx * qx
+        yy = qy * qy
+        zz = qz * qz
+        wx = qw * qx
+        wy = qw * qy
+        wz = qw * qz
+        xy = qx * qy
+        xz = qx * qz
+        yz = qy * qz
+	
+        return np.array([
+                (ww + xx - yy - zz) * x + 2 * ((xy - wz) * y + (xz + wy) * z),
+                (ww - xx + yy - zz) * y + 2 * ((xy + wz) * x + (yz - wx) * z),
+                (ww - xx - yy + zz) * z + 2 * ((xz - wy) * x + (yz + wx) * y)
+                ])
+
+    def rotation_matrix(self):
+
+        qw, qx, qy, qz = self.q[0], self.q[1], self.q[2], self.q[3]
+	
+        ww =  qw * qw
+        xx =  qx * qx
+        yy =  qy * qy
+        zz =  qz * qz
+        wx =  qw * qx
+        wy =  qw * qy
+        wz =  qw * qz
+        xy =  qx * qy
+        xz =  qx * qz
+        yz =  qy * qz
+
+        return np.array([[ww + xx - yy - zz , 2 * (xy + wz), 2 * (xz - wy)],
+                         [2*(xy - wz), ww - xx + yy - zz, 2*(yz + wx)],
+                         [2*(xz + wy), 2*(yz - wx), ww - xx - yy + zz]
+                         ])
+ 
+    def rotate_byq(self, q, vector):
+        """Apply the rotation matrix to a vector."""
+        qw, qx, qy, qz = q[0], q[1], q[2], q[3]
+        x, y, z = vector[0], vector[1], vector[2]
+	
+        ww = qw * qw
+        xx = qx * qx
+        yy = qy * qy
+        zz = qz * qz
+        wx = qw * qx
+        wy = qw * qy
+        wz = qw * qz
+        xy = qx * qy
+        xz = qx * qz
+        yz = qy * qz
+	
+        return np.array([
+                (ww + xx - yy - zz) * x + 2 * ((xy - wz) * y + (xz + wy) * z),
+                (ww - xx + yy - zz) * y + 2 * ((xy + wz) * x + (yz - wx) * z),
+                (ww - xx - yy + zz) * z + 2 * ((xz - wy) * x + (yz + wx) * y)
+                ])
+   
+    def from_matrix(self, matrix):
+        """Build quaternion from rotation matrix."""
+        m = np.array(matrix)
+        assert m.shape == (3, 3)
+
+        qw = np.sqrt(1 + m[0, 0] + m[1, 1] + m[2, 2]) / 2.
+        qx = (m[2, 1] - m[1, 2]) / (4 * qw)
+        qy = (m[0, 2] - m[2, 0]) / (4 *qw)
+        qz = (m[1, 0] - m[0, 1]) / (4 *qw)
+
+        self.q = np.array([qw, qx, qy, qz])
+        return self
diff --git a/ase/structure.py b/ase/structure.py
new file mode 100644
index 0000000..c4ea282
--- /dev/null
+++ b/ase/structure.py
@@ -0,0 +1,433 @@
+"""Atomic structure.
+
+This mudule contains helper functions for setting up nanotubes and
+graphene nanoribbons."""
+
+import warnings
+from math import sqrt
+
+import numpy as np
+
+from ase.atoms import Atoms, string2symbols
+from ase.data import covalent_radii
+from ase.utils import gcd
+
+
+def nanotube(n, m, length=1, bond=1.42, symbol='C', verbose=False):
+    if n < m:
+        m, n = n, m
+        sign = -1
+    else:
+        sign = 1
+
+    nk = 6000
+    sq3 = sqrt(3.0)
+    a = sq3 * bond
+    l2 = n * n + m * m + n * m
+    l = sqrt(l2)
+    dt = a * l / np.pi
+
+    nd = gcd(n ,m)
+    if (n - m) % (3 * nd ) == 0:
+        ndr = 3 * nd
+    else:
+        ndr = nd
+
+    nr = (2 * m + n) / ndr
+    ns = -(2 * n + m) / ndr
+    nt2 = 3 * l2 / ndr / ndr
+    nt = np.floor(sqrt(nt2))
+    nn = 2 * l2 / ndr
+
+    ichk = 0
+    if nr == 0:
+        n60 = 1
+    else:
+        n60 = nr * 4
+
+    absn = abs(n60)
+    nnp = []
+    nnq = []
+    for i in range(-absn, absn + 1):
+        for j in range(-absn, absn + 1):
+            j2 = nr * j - ns * i
+            if j2 == 1:
+                j1 = m * i - n * j
+                if j1 > 0 and j1 < nn:
+                    ichk += 1
+                    nnp.append(i)
+                    nnq.append(j)
+
+    if ichk == 0:
+        raise RuntimeError('not found p, q strange!!')
+    if ichk >= 2:
+        raise RuntimeError('more than 1 pair p, q strange!!')
+
+    nnnp = nnp[0]
+    nnnq = nnq[0]
+
+    if verbose:
+        print 'the symmetry vector is', nnnp, nnnq
+
+    lp = nnnp * nnnp + nnnq * nnnq + nnnp * nnnq
+    r = a * sqrt(lp)
+    c = a * l
+    t = sq3 * c / ndr
+
+    if 2 * nn > nk:
+        raise RuntimeError('parameter nk is too small!')
+
+    rs = c / (2.0 * np.pi)
+
+    if verbose:
+        print 'radius=', rs, t
+
+    q1 = np.arctan((sq3 * m) / (2 * n + m))
+    q2 = np.arctan((sq3 * nnnq) / (2 * nnnp + nnnq))
+    q3 = q1 - q2
+
+    q4 = 2.0 * np.pi / nn
+    q5 = bond * np.cos((np.pi / 6.0) - q1) / c * 2.0 * np.pi
+
+    h1 = abs(t) / abs(np.sin(q3))
+    h2 = bond * np.sin((np.pi / 6.0) - q1)
+
+    ii = 0
+    x, y, z = [], [], []
+    for i in range(nn):
+        x1, y1, z1 = 0, 0, 0
+
+        k = np.floor(i * abs(r) / h1)
+        x1 = rs * np.cos(i * q4)
+        y1 = rs * np.sin(i * q4)
+        z1 = (i * abs(r) - k * h1) * np.sin(q3)
+        kk2 = abs(np.floor((z1 + 0.0001) / t))
+        if z1 >= t - 0.0001:
+            z1 -= t * kk2
+        elif z1 < 0:
+            z1 += t * kk2
+        ii += 1
+
+        x.append(x1)
+        y.append(y1)
+        z.append(z1)
+        z3 = (i * abs(r) - k * h1) * np.sin(q3) - h2
+        ii += 1
+
+        if z3 >= 0 and z3 < t:
+            x2 = rs * np.cos(i * q4 + q5)
+            y2 = rs * np.sin(i * q4 + q5)
+            z2 = (i * abs(r) - k * h1) * np.sin(q3) - h2
+            x.append(x2)
+            y.append(y2)
+            z.append(z2)
+        else:
+            x2 = rs * np.cos(i * q4 + q5)
+            y2 = rs * np.sin(i * q4 + q5)
+            z2 = (i * abs(r) - (k + 1) * h1) * np.sin(q3) - h2
+            kk = abs(np.floor(z2 / t))
+            if z2 >= t - 0.0001:
+                z2 -= t * kk
+            elif z2 < 0:
+                z2 += t * kk
+            x.append(x2)
+            y.append(y2)
+            z.append(z2)
+
+    ntotal = 2 * nn
+    X = []
+    for i in range(ntotal):
+        X.append([x[i], y[i], sign * z[i]])
+
+    if length > 1:
+        xx = X[:]
+        for mnp in range(2, length + 1):
+            for i in range(len(xx)):
+                X.append(xx[i][:2] + [xx[i][2] + (mnp - 1) * t])
+
+    TransVec = t
+    NumAtom = ntotal * length
+    Diameter = rs * 2
+    ChiralAngle = np.arctan((sq3 * n) / (2 * m + n)) / (np.pi * 180)
+
+    cell = [Diameter * 2, Diameter * 2, length * t]
+    atoms = Atoms(symbol + str(NumAtom), positions=X, cell=cell,
+                  pbc=[False, False, True])
+    atoms.center()
+    if verbose:
+        print 'translation vector =', TransVec
+        print 'diameter = ', Diameter
+        print 'chiral angle = ', ChiralAngle
+    return atoms
+
+def graphene_nanoribbon(n, m, type='zigzag', saturated=False, C_H=1.09,
+                        C_C=1.42, vacuum=2.5, magnetic=None, initial_mag=1.12,
+                        sheet=False, main_element='C', saturate_element='H',
+                        vacc=None):
+    """Create a graphene nanoribbon.
+
+    Creates a graphene nanoribbon in the x-z plane, with the nanoribbon
+    running along the z axis.
+
+    Parameters:
+
+    n: The width of the nanoribbon
+
+    m: The length of the nanoribbon.
+
+    type ('zigzag'): The orientation of the ribbon.  Must be either 'zigzag'
+    or 'armchair'.
+
+    saturated (Falsi):  If true, hydrogen atoms are placed along the edge.
+
+    C_H: Carbon-hydrogen bond length.  Default: 1.09 Angstrom
+
+    C_C: Carbon-carbon bond length.  Default: 1.42 Angstrom.
+
+    vacuum:  Amount of vacuum added to both sides.  Default 2.5 Angstrom.
+
+    magnetic:  Make the edges magnetic.
+
+    initial_mag: Magnitude of magnetic moment if magnetic=True.
+
+    sheet:  If true, make an infinite sheet instead of a ribbon.
+    """
+    #This function creates the coordinates for a graphene nanoribbon,
+    #n is width, m is length
+
+    if vacc is not None:
+        warnings.warn('Use vacuum=%f' % (0.5 * vacc))
+        vacuum = 0.5 * vacc
+
+    assert vacuum > 0
+    b = sqrt(3) * C_C / 4
+    arm_unit = Atoms(main_element+'4', pbc=(1,0,1),
+                     cell = [4 * b,  2 * vacuum,  3 * C_C])
+    arm_unit.positions = [[0, 0, 0],
+                          [b * 2, 0, C_C / 2.],
+                          [b * 2, 0, 3 * C_C / 2.],
+                          [0, 0, 2 * C_C]]
+    zz_unit = Atoms(main_element+'2', pbc=(1,0,1),
+                    cell = [3 * C_C /2., 2 * vacuum, b * 4])
+    zz_unit.positions = [[0, 0, 0],
+                         [C_C / 2., 0, b * 2]]
+    atoms = Atoms()
+    tol = 1e-4
+    if sheet:
+        vacuum2 = 0.0
+    else:
+        vacuum2 = vacuum
+    if type == 'zigzag':
+        edge_index0 = np.arange(m) * 2 + 1
+        edge_index1 = (n - 1) * m * 2 + np.arange(m) * 2
+        if magnetic:
+            mms = np.zeros(m * n * 2)
+            for i in edge_index0:
+                mms[i] = initial_mag
+            for i in edge_index1:
+                mms[i] = -initial_mag
+
+        for i in range(n):
+            layer = zz_unit.repeat((1, 1, m))
+            layer.positions[:, 0] -= 3 * C_C / 2 * i
+            if i % 2 == 1:
+                layer.positions[:, 2] += 2 * b
+                layer[-1].position[2] -= b * 4 * m
+            atoms += layer
+        if magnetic:
+            atoms.set_initial_magnetic_moments(mms)
+        if saturated:
+            H_atoms0 = Atoms(saturate_element + str(m))
+            H_atoms0.positions = atoms[edge_index0].positions
+            H_atoms0.positions[:, 0] += C_H
+            H_atoms1 = Atoms(saturate_element + str(m))
+            H_atoms1.positions = atoms[edge_index1].positions
+            H_atoms1.positions[:, 0] -= C_H
+            atoms += H_atoms0 + H_atoms1
+        atoms.cell = [n * 3 * C_C / 2 + 2 * vacuum2, 2 * vacuum, m * 4 * b]
+
+    elif type == 'armchair':
+        for i in range(n):
+            layer = arm_unit.repeat((1, 1, m))
+            layer.positions[:, 0] -= 4 * b * i
+            atoms += layer
+        atoms.cell = [b * 4 * n + 2 * vacuum2, 2 * vacuum, 3 * C_C * m]
+
+    atoms.center()
+    atoms.set_pbc([sheet, False, True])
+    return atoms
+
+def molecule(name, data=None, **kwargs):
+    """Create formula base on data. If data is None assume G2 set.
+    kwargs currently not used.  """
+    if data is None:
+        from ase.data.g2 import data
+    if name not in data.keys():
+        raise NotImplementedError('%s not in data.' % (name))
+    args = data[name].copy()
+    # accept only the following Atoms constructor arguments
+    # XXX: should we accept all Atoms arguments?
+    for k in args.keys():
+        if k not in [
+            'symbols', 'positions', 'numbers',
+            'tags', 'masses',
+            'magmoms', 'charges',
+            'info',
+            ]:
+            args.pop(k)
+    # kwargs overwrites data
+    args.update(kwargs)
+    return Atoms(**args)
+
+def bulk(name, crystalstructure, a=None, c=None, covera=None,
+         orthorhombic=False, cubic=False):
+    """Helper function for creating bulk systems.
+
+    name: str
+        Chemical symbol or symbols as in 'MgO' or 'NaCl'.
+    crystalstructure: str
+        Must be one of sc, fcc, bcc, hcp, diamond, zincblende or
+        rocksalt.
+    a: float
+        Lattice constant.
+    c: float
+        Lattice constant.
+    covera: float
+        c/a raitio used for hcp.  Defaults to ideal ratio.
+    orthorhombic: bool
+        Construct orthorhombic unit cell instead of primitive cell
+        which is the default.
+    cubic: bool
+        Construct cubic unit cell.
+    """
+
+    #warnings.warn('This function is deprecated.  Use the ' +
+    #              'ase.lattice.bulk.bulk() function instead.')
+
+    if a is not None:
+        a = float(a)
+    if c is not None:
+        c = float(c)
+
+    if covera is not None and  c is not None:
+        raise ValueError("Don't specify both c and c/a!")
+
+    if covera is None and c is None:
+        covera = sqrt(8.0 / 3.0)
+
+    if a is None:
+        a = estimate_lattice_constant(name, crystalstructure, covera)
+
+    if covera is None and c is not None:
+        covera = c / a
+
+    x = crystalstructure.lower()
+
+    if orthorhombic and x != 'sc':
+        return _orthorhombic_bulk(name, x, a, covera)
+
+    if cubic and x == 'bcc':
+        return _orthorhombic_bulk(name, x, a, covera)
+
+    if cubic and x != 'sc':
+        return _cubic_bulk(name, x, a)
+
+    if x == 'sc':
+        atoms = Atoms(name, cell=(a, a, a), pbc=True)
+    elif x == 'fcc':
+        b = a / 2
+        atoms = Atoms(name, cell=[(0, b, b), (b, 0, b), (b, b, 0)], pbc=True)
+    elif x == 'bcc':
+        b = a / 2
+        atoms = Atoms(name, cell=[(-b, b, b), (b, -b, b), (b, b, -b)],
+                      pbc=True)
+    elif x == 'hcp':
+        atoms = Atoms(2 * name,
+                      scaled_positions=[(0, 0, 0),
+                                        (1.0 / 3.0, 1.0 / 3.0, 0.5)],
+                      cell=[(a, 0, 0),
+                            (a / 2, a * sqrt(3) / 2, 0),
+                            (0, 0, covera * a)],
+                      pbc=True)
+    elif x == 'diamond':
+        atoms = bulk(2 * name, 'zincblende', a)
+    elif x == 'zincblende':
+        s1, s2 = string2symbols(name)
+        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
+        atoms.positions[1] += a / 4
+    elif x == 'rocksalt':
+        s1, s2 = string2symbols(name)
+        atoms = bulk(s1, 'fcc', a) + bulk(s2, 'fcc', a)
+        atoms.positions[1, 0] += a / 2
+    else:
+        raise ValueError('Unknown crystal structure: ' + crystalstructure)
+
+    return atoms
+
+def estimate_lattice_constant(name, crystalstructure, covera):
+    atoms = bulk(name, crystalstructure, 1.0, covera)
+    v0 = atoms.get_volume()
+    v = 0.0
+    for Z in atoms.get_atomic_numbers():
+        r = covalent_radii[Z]
+        v += 4 * np.pi / 3 * r**3 * 1.5
+    return (v / v0)**(1.0 / 3)
+
+def _orthorhombic_bulk(name, x, a, covera=None):
+    if x == 'fcc':
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
+    elif x == 'bcc':
+        atoms = Atoms(2 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5)])
+    elif x == 'hcp':
+        atoms = Atoms(4 * name,
+                      cell=(a, a * sqrt(3), covera * a),
+                      scaled_positions=[(0, 0, 0),
+                                        (0.5, 0.5, 0),
+                                        (0.5, 1.0 / 6.0, 0.5),
+                                        (0, 2.0 / 3.0, 0.5)],
+                      pbc=True)
+    elif x == 'diamond':
+        atoms = _orthorhombic_bulk(2 * name, 'zincblende', a)
+    elif x == 'zincblende':
+        s1, s2 = string2symbols(name)
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0, 0.25),
+                                        (0.5, 0.5, 0.5), (0, 0.5, 0.75)])
+    elif x == 'rocksalt':
+        s1, s2 = string2symbols(name)
+        b = a / sqrt(2)
+        atoms = Atoms(2 * name, cell=(b, b, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0.5, 0),
+                                        (0.5, 0.5, 0.5), (0, 0, 0.5)])
+    else:
+        raise RuntimeError
+
+    return atoms
+
+def _cubic_bulk(name, x, a):
+    if x == 'fcc':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0, 0.5, 0.5),
+                                        (0.5, 0, 0.5), (0.5, 0.5, 0)])
+    elif x == 'diamond':
+        atoms = _cubic_bulk(2 * name, 'zincblende', a)
+    elif x == 'zincblende':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.25, 0.25, 0.25),
+                                        (0, 0.5, 0.5), (0.25, 0.75, 0.75),
+                                        (0.5, 0, 0.5), (0.75, 0.25, 0.75),
+                                        (0.5, 0.5, 0), (0.75, 0.75, 0.25)])
+    elif x == 'rocksalt':
+        atoms = Atoms(4 * name, cell=(a, a, a), pbc=True,
+                      scaled_positions=[(0, 0, 0), (0.5, 0, 0),
+                                        (0, 0.5, 0.5), (0.5, 0.5, 0.5),
+                                        (0.5, 0, 0.5), (0, 0, 0.5),
+                                        (0.5, 0.5, 0), (0, 0.5, 0)])
+    else:
+        raise RuntimeError
+
+    return atoms
diff --git a/ase/svnversion.py b/ase/svnversion.py
new file mode 100644
index 0000000..23b0a86
--- /dev/null
+++ b/ase/svnversion.py
@@ -0,0 +1 @@
+svnversion = "exportado"
diff --git a/ase/svnversion_io.py b/ase/svnversion_io.py
new file mode 100644
index 0000000..500f183
--- /dev/null
+++ b/ase/svnversion_io.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2003  CAMP
+# Please see the accompanying LICENSE file for further information.
+
+from os import path
+try:
+    from subprocess import Popen, PIPE
+except ImportError:
+    from os import popen3
+else:
+    def popen3(cmd):
+        p = Popen(cmd, shell=True, close_fds=True,
+                  stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        return p.stdin, p.stdout, p.stderr
+
+def write_svnversion(svnversion, dir):
+    svnversionfile = path.join(dir, 'svnversion.py')
+    f = open(svnversionfile,'w')
+    f.write('svnversion = "%s"\n' % svnversion)
+    f.close()
+    print 'svnversion = ' +svnversion+' written to '+svnversionfile
+    # assert svn:ignore property if the installation is under svn control
+    # because svnversion.py has to be ignored by svn!
+    cmd = popen3('svn propset svn:ignore svnversion.py '+dir)[1]
+    output = cmd.read()
+    cmd.close()
+
+def get_svnversion_from_svn(dir):
+    # try to get the last svn version number from svnversion
+    cmd = popen3('svnversion -n '+dir)[1] # assert we are in the project dir
+    output = cmd.read().strip()
+    cmd.close()
+    if output.startswith('exported'):
+        # we build from exported source (e.g. rpmbuild)
+        output = None
+    return output
+
+svnversion = get_svnversion_from_svn(dir='ase')
+if svnversion:
+    write_svnversion(svnversion, dir='ase')
diff --git a/ase/tasks/__init__.py b/ase/tasks/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ase/tasks/bulk.py b/ase/tasks/bulk.py
new file mode 100644
index 0000000..b61ba13
--- /dev/null
+++ b/ase/tasks/bulk.py
@@ -0,0 +1,153 @@
+import optparse
+
+import numpy as np
+
+from ase.lattice import bulk
+from ase.tasks.task import OptimizeTask
+from ase.data import chemical_symbols, reference_states
+from ase.utils.eos import EquationOfState
+from ase.io.trajectory import PickleTrajectory
+import ase.units as units
+
+
+class BulkTask(OptimizeTask):
+    taskname = 'bulk'
+
+    def __init__(self, crystal_structure=None, lattice_constant=None,
+                 c_over_a=None, cubic=False, orthorhombic=False, fit=None,
+                 **kwargs):
+        """Bulk task."""
+
+        self.crystal_structure = crystal_structure
+        self.lattice_constant = lattice_constant
+        self.c_over_a = c_over_a
+        self.cubic = cubic
+        self.orthorhombic = orthorhombic
+        self.fit = fit
+
+        self.repeat = None
+
+        OptimizeTask.__init__(self, **kwargs)
+        
+        self.summary_header += [('V0', 'Ang^3'),
+                                ('B', 'GPa')]
+
+    def expand(self, names):
+        """Expand fcc, bcc, hcp and diamond.
+
+        The name fcc will be expanded to all the elements with the fcc
+        stucture and so on."""
+
+        names = OptimizeTask.expand(self, names)
+            
+        newnames = []
+        for name in names:
+            if name in ['fcc', 'bcc', 'hcp', 'diamond']:
+                for Z in range(1, 95):
+                    x = reference_states[Z]
+                    if x is not None and x['symmetry'] == name:
+                        newnames.append(chemical_symbols[Z])
+            else:
+                newnames.append(name)
+
+        return newnames
+
+    def build_system(self, name):
+        atoms = bulk(name, crystalstructure=self.crystal_structure,
+                     a=self.lattice_constant, covera=self.c_over_a,
+                     orthorhombic=self.orthorhombic, cubic=self.cubic)
+
+        M = {'Fe': 2.3, 'Co': 1.2, 'Ni': 0.6}.get(name)
+        if M is not None:
+            atoms.set_initial_magnetic_moments([M] * len(atoms))
+
+        if self.repeat is not None:
+            r = self.repeat.split(',')
+            if len(r) == 1:
+                r = 3 * r
+            atoms = atoms.repeat([int(c) for c in r])
+
+        return atoms
+    
+    def fit_volume(self, name, atoms):
+        N, x = self.fit
+        cell0 = atoms.get_cell()
+        strains = np.linspace(1 - x, 1 + x, N)
+        energies = []
+        traj = PickleTrajectory(self.get_filename(name, '-fit.traj'), 'w')
+        for s in strains:
+            atoms.set_cell(cell0 * s, scale_atoms=True)
+            energies.append(atoms.get_potential_energy())
+            traj.write(atoms)
+
+        traj.close()
+
+        assert N % 2 == 1
+        data = {'energy': energies[N // 2],
+                'strains': strains,
+                'energies': energies}
+
+        return data
+            
+    def calculate(self, name, atoms):
+        #????
+        if self.fit:
+            return self.fit_volume(name, atoms)
+        else:
+            return OptimizeTask.calculate(self, name, atoms)
+        
+    def analyse(self):
+        OptimizeTask.analyse(self)
+        for name, data in self.data.items():
+            if 'strains' in data:
+                atoms = self.create_system(name)
+                volumes = data['strains']**3 * atoms.get_volume()
+                energies = data['energies']
+                eos = EquationOfState(volumes, energies)
+                try:
+                    v, e, B = eos.fit()
+                except ValueError:
+                    self.results[name].extend([None, None])
+                else:
+                    self.results[name][1:] = [energies[2] - e, v,
+                                              B * 1e24 / units.kJ]
+            else:
+                self.results[name].extend([None, None])
+
+    def add_options(self, parser):
+        OptimizeTask.add_options(self, parser)
+
+        bulk = optparse.OptionGroup(parser, 'Bulk')
+        bulk.add_option('-F', '--fit', metavar='N,x',
+                        help='Find optimal volume and bulk modulus ' +
+                        'using N points and variations of the lattice ' +
+                        'constants from -x % to +x %.')
+        bulk.add_option('-x', '--crystal-structure',
+                        help='Crystal structure.',
+                        choices=['sc', 'fcc', 'bcc', 'diamond', 'hcp',
+                                 'rocksalt', 'zincblende'])
+        bulk.add_option('-a', '--lattice-constant', type='float',
+                        help='Lattice constant in Angstrom.')
+        bulk.add_option('--c-over-a', type='float',
+                        help='c/a ratio.')
+        bulk.add_option('-O', '--orthorhombic', action='store_true',
+                        help='Use orthorhombic unit cell.')
+        bulk.add_option('-C', '--cubic', action='store_true',
+                        help='Use cubic unit cell.')
+        bulk.add_option('-r', '--repeat',
+                        help='Repeat unit cell.  Use "-r 2" or "-r 2,3,1".')
+        parser.add_option_group(bulk)
+
+    def parse(self, opts, args):
+        OptimizeTask.parse(self, opts, args)
+
+        if opts.fit:
+            points, strain = opts.fit.split(',')
+            self.fit = (int(points), float(strain) * 0.01)
+
+        self.crystal_structure = opts.crystal_structure
+        self.lattice_constant = opts.lattice_constant
+        self.c_over_a = opts.c_over_a
+        self.orthorhombic = opts.orthorhombic
+        self.cubic = opts.cubic
+        self.repeat = opts.repeat
diff --git a/ase/tasks/calcfactory.py b/ase/tasks/calcfactory.py
new file mode 100644
index 0000000..7ea9612
--- /dev/null
+++ b/ase/tasks/calcfactory.py
@@ -0,0 +1,175 @@
+import optparse
+from math import sqrt, pi
+
+import numpy as np
+
+from ase.dft.kpoints import monkhorst_pack
+
+
+def str2dict(s, namespace={}):
+    """Convert comma-separated key=vale string to dictionary.
+
+    Example:
+
+    >>> str2dict('a=1.2,b=True,c=abc,d=1,2,3')
+    {'a': 1.2, 'c': 'abc', 'b': True, 'd': (1, 2, 3)}
+    """
+
+    dct = {}
+    s = (s + ',').split('=')
+    for i in range(len(s) - 1):
+        key = s[i]
+        m = s[i + 1].rfind(',')
+        value = s[i + 1][:m]
+        try:
+            value = eval(value, namespace)
+        except (NameError, SyntaxError):
+            pass
+        dct[key] = value
+        s[i + 1] = s[i + 1][m + 1:]
+    return dct
+
+
+class CalculatorFactory:
+    def __init__(self, Class, name, label='label',
+                 kpts=None, kptdensity=3.0,
+                 **kwargs):
+        """Calculator factory object.
+
+        Used to create calculators with specific parameters."""
+
+        self.Class = Class
+        self.name = name
+        self.label = label
+        self.kpts = kpts
+        self.kptdensity = kptdensity
+        self.kwargs = kwargs
+
+    def calculate_kpts(self, atoms):
+        """Estimate an appropriate number of k-points."""
+
+        if self.kpts is not None:
+            # Number of k-points was explicitely set:
+            return self.kpts
+
+        # Use kptdensity to make a good estimate:
+        recipcell = atoms.get_reciprocal_cell()
+        kpts = []
+        for i in range(3):
+            if atoms.pbc[i]:
+                k = 2 * pi * sqrt((recipcell[i]**2).sum()) * self.kptdensity
+                kpts.append(max(1, 2 * int(round(k / 2))))
+            else:
+                kpts.append(1)
+
+        return kpts
+
+    def __call__(self, name, atoms):
+        """Create calculator.
+
+        Put name in the filename of all created files."""
+
+        kpts = self.calculate_kpts(atoms)
+        if kpts != 'no k-points':
+            self.kwargs['kpts'] = kpts
+
+        if self.label is not None:
+            self.kwargs[self.label] = name
+
+        return self.Class(**self.kwargs)
+
+    def add_options(self, parser):
+        calc = optparse.OptionGroup(parser, 'Calculator')
+        calc.add_option('-k', '--monkhorst-pack',
+                        metavar='K1,K2,K3',
+                        help='Monkhorst-Pack sampling of BZ.  Example: ' +
+                        '"4,4,4": 4x4x4 k-points, "4,4,4g": same set of ' +
+                        'k-points shifted to include the Gamma point.')
+        calc.add_option('--k-point-density', type='float', default=3.0,
+                        help='Density of k-points in Angstrom.')
+        calc.add_option('-p', '--parameters', metavar='key=value,...',
+                        help='Comma-separated key=value pairs of ' +
+                        'calculator specific parameters.')
+        parser.add_option_group(calc)
+
+    def parse(self, opts, args):
+        mp = opts.monkhorst_pack
+        if mp is not None:
+            if mp[-1].lower() == 'g':
+                kpts = np.array([int(k) for k in mp[:-1].split(',')])
+                shift = 0.5 * ((kpts + 1) % 2) / kpts
+                self.kpts = monkhorst_pack(kpts) + shift
+            else:
+                self.kpts = [int(k) for k in mp.split(',')]
+
+        self.kptdensity = opts.k_point_density
+
+        if opts.parameters:
+            self.kwargs.update(str2dict(opts.parameters))
+
+
+# Recognized names of calculators sorted alphabetically:
+calcnames = ['abinit', 'aims', 'asap', 'castep', 'dftb', 'elk', 'emt',
+             'exciting', 'fleur', 'gpaw', 'hotbit', 'jacapo',
+             'lammps', 'lj', 'morse',
+             'nwchem', 'siesta', 'turbomole', 'vasp']
+
+classnames = {'asap': 'EMT',
+              'elk': 'ELK',
+              'emt': 'EMT',
+              'fleur': 'FLEUR',
+              'jacapo': 'Jacapo',
+              'lammps': 'LAMMPS',
+              'lj': 'LennardJones',
+              'morse': 'MorsePotential',
+              'nwchem': 'NWchem',
+              'vasp': 'Vasp'}
+
+
+def calculator_factory(name, **kwargs):
+    """Create an ASE calculator factory."""
+
+    if name == 'asap':
+        from asap3 import EMT
+        return CalculatorFactory(EMT, 'Asap', None, 'no k-points', **kwargs)
+
+    if name == 'elk':
+        from ase.calculators.elk import ELK
+        return CalculatorFactory(ELK, 'ELK', 'dir', **kwargs)
+
+    if name == 'fleur':
+        from ase.calculators.fleur import FLEUR
+        return CalculatorFactory(FLEUR, 'FLEUR', 'workdir', **kwargs)
+
+    if name == 'gpaw':
+        from gpaw.factory import GPAWFactory
+        return GPAWFactory(**kwargs)
+
+    if name == 'hotbit':
+        from hotbit import Calculator
+        return CalculatorFactory(Calculator, 'Hotbit', 'txt', 'no k-points',
+                                 **kwargs)
+
+    if name == 'jacapo':
+        from ase.calculators.jacapo import Jacapo
+        return CalculatorFactory(Jacapo, 'Jacapo', 'nc', **kwargs)
+
+    if name == 'vasp':
+        from ase.calculators.vasp import Vasp
+        return CalculatorFactory(Vasp, 'Vasp', None, **kwargs)
+
+    classname = classnames.get(name, name.title())
+    module = __import__('ase.calculators.' + name, {}, None, [classname])
+    Class = getattr(module, classname)
+
+    if name in ['emt', 'lammps', 'lj', 'morse']:
+        kpts = 'no k-points'
+    else:
+        kpts = None
+
+    if name in ['emt', 'lj', 'morse']:
+        label = None
+    else:
+        label = 'label'
+
+    return CalculatorFactory(Class, classname, label, kpts, **kwargs)
diff --git a/ase/tasks/io.py b/ase/tasks/io.py
new file mode 100644
index 0000000..c4c1808
--- /dev/null
+++ b/ase/tasks/io.py
@@ -0,0 +1,56 @@
+import numpy as np
+
+from ase.parallel import world
+
+try:
+    import json
+except ImportError:
+    json = None
+
+
+if json is None:
+    def dumps(obj):
+        if isinstance(obj, str):
+            return '"' + obj + '"'
+        if isinstance(obj, (int, float)):
+            return repr(obj)
+        if isinstance(obj, dict):
+            return '{' + ','.join(dumps(key) + ':' + dumps(value)
+                                  for key, value in obj.items()) + '}'
+        return '[' + ','.join(dumps(value) for value in obj) + ']'
+
+    loads = eval
+else:
+    class NDArrayEncoder(json.JSONEncoder):
+        def default(self, obj):
+            if isinstance(obj, np.ndarray):
+                return obj.tolist()
+            return json.JSONEncoder.default(self, obj)
+    
+    dumps = NDArrayEncoder().encode
+    loads = json.loads
+
+
+def numpyfy(obj):
+    if isinstance(obj, dict):
+        return dict((key, numpyfy(value)) for key, value in obj.items())
+    if isinstance(obj, list):
+        try:
+            obj = np.array(obj)
+        except ValueError:
+            obj = [numpyfy(value) for value in obj]
+    return obj
+
+
+def write_json(name, atoms, results):
+    if world.rank == 0:
+        fd = open(name + '.json', 'w')
+        fd.write(dumps(results))
+        fd.close()
+
+
+def read_json(name):
+    fd = open(name + '.json', 'r')
+    results = loads(fd.read())
+    fd.close()
+    return numpyfy(results)
diff --git a/ase/tasks/main.py b/ase/tasks/main.py
new file mode 100644
index 0000000..705d56c
--- /dev/null
+++ b/ase/tasks/main.py
@@ -0,0 +1,97 @@
+import os
+import sys
+import tempfile
+import textwrap
+import traceback
+
+from ase.tasks.task import Task
+from ase.tasks.bulk import BulkTask
+from ase.tasks.molecule import MoleculeTask
+from ase.tasks.calcfactory import calcnames
+
+
+usage = """\
+Usage: ase [calculator] [task] [options] system(s)
+
+%s
+task:       'molecule', 'bulk' or the name of Python script that instantiates
+            a Task object.  Default value is 'molecule'.
+systems:    chemical formulas or filenames of files containing the atomic
+            structure.
+
+Try "ase molecule --help" or "ase bulk --help".
+"""
+
+
+def run(args=sys.argv[1:], calcname='emt'):
+
+    if isinstance(args, str):
+        args = args.split(' ')
+
+    argsoriginal = args[:]
+
+    if len(args) > 0 and args[0] in calcnames:
+        calcname = args.pop(0)
+
+    taskname = 'molecule'
+    if (len(args) > 0 and
+        (args[0] in ['molecule', 'bulk'] or args[0].endswith('.py'))):
+        taskname = args.pop(0)
+
+    if len(args) == 0:
+        sys.stderr.write(
+            usage % textwrap.fill(', '.join(calcnames[:-1]) +
+                                  ' or ' + calcnames[-1] +
+                                  '.  Default value is emt.',
+                                  initial_indent='calculator: ',
+                                  subsequent_indent=' ' * 12))
+        return
+    
+    if taskname.endswith('.py'):
+        locals = {}
+        execfile(taskname, locals, locals)
+        tasks = [task for task in locals.values() if isinstance(task, Task)]
+        assert len(tasks) == 1
+        task = tasks[0]
+    elif taskname == 'bulk':
+        task = BulkTask()
+    else:
+        task = MoleculeTask()
+
+    task.set_calculator_factory(calcname)
+
+    args = task.parse_args(args)
+
+    if task.interactive_python_session:
+        if '-i' in argsoriginal:
+            argsoriginal.remove('-i')
+        if '--interactive-python-session' in argsoriginal:
+            argsoriginal.remove('--interactive-python-session')
+        file = tempfile.NamedTemporaryFile()
+        file.write('import os\n')
+        file.write('if "PYTHONSTARTUP" in os.environ:\n')
+        file.write('    execfile(os.environ["PYTHONSTARTUP"])\n')
+        file.write('from ase.tasks.main import run\n')
+        file.write('atoms, task = run(%r, %r)\n' % (argsoriginal, calcname))
+        file.flush()
+        os.system('python -i %s' % file.name)
+        return
+
+    atoms = task.run(args)
+
+    return atoms, task
+
+
+def main():
+    try:
+        run()
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except Exception:
+        traceback.print_exc()
+        sys.stderr.write("""
+An exception occurred!  Please report the issue to
+ase-developer at listserv.fysik.dtu.dk - thanks!  Please also report this
+if it was a user error, so that a better error message can be provided
+next time.""")
+        raise
diff --git a/ase/tasks/molecule.py b/ase/tasks/molecule.py
new file mode 100644
index 0000000..f7b71d9
--- /dev/null
+++ b/ase/tasks/molecule.py
@@ -0,0 +1,242 @@
+from math import sqrt
+import optparse
+
+import numpy as np
+
+from ase.atoms import Atoms, string2symbols
+from ase.structure import molecule
+from ase.tasks.task import OptimizeTask
+from ase.data import covalent_radii, atomic_numbers
+from ase.data import ground_state_magnetic_moments
+from ase.utils.eos import EquationOfState
+from ase.io.trajectory import PickleTrajectory
+import ase.units as units
+
+
+class MoleculeTask(OptimizeTask):
+    taskname = 'molecule'
+
+    def __init__(self, vacuum=3.0, cell=None, atomize=False,
+                 bond_length=None, fit=None,
+                 **kwargs):
+        """Molecule task.
+
+        This task can calculate bond lengths and vibration frequencies
+        of dimer molecules."""
+
+        self.vacuum = vacuum
+        self.unit_cell = cell
+        self.atomize = atomize
+        self.bond_length = bond_length
+        self.fit = fit
+
+        OptimizeTask.__init__(self, **kwargs)
+
+        self.summary_header += [('d0', 'Ang'),
+                                ('hnu', 'meV'),
+                                ('Ea', 'eV'),
+                                ('Ea0', 'eV')]
+
+    def run(self, names1):
+        names = []
+        atoms = set()
+        for name in names1:
+            if name.lower() == 'g2':
+                from ase.data.g2 import molecule_names
+                names.extend(molecule_names)
+                from ase.data.g2 import atom_names
+                if self.atomize:
+                    atoms.update(atom_names)
+            elif name.lower() == 'g2-1':
+                from ase.data.g2_1 import molecule_names
+                names.extend(molecule_names)
+                from ase.data.g2_1 import atom_names
+                if self.atomize:
+                    atoms.update(atom_names)
+            else:
+                names.append(name)
+                if self.atomize:
+                    atoms.update(self.build_system(name).get_chemical_symbols())
+        if self.atomize:
+            names.extend(atoms)
+
+        return OptimizeTask.run(self, names)
+
+    def build_system(self, name):
+        try:
+            # Known molecule or atom?
+            atoms = molecule(name)
+            if len(atoms) == 2 and self.bond_length is not None:
+                atoms.set_distance(0, 1, self.bond_length)
+        except NotImplementedError:
+            symbols = string2symbols(name)
+            if len(symbols) == 1:
+                magmom = ground_state_magnetic_moments[atomic_numbers[symbols[0]]]
+                atoms = Atoms(name, magmoms=[magmom])
+            elif len(symbols) == 2:
+                # Dimer
+                if self.bond_length is None:
+                    b = (covalent_radii[atomic_numbers[symbols[0]]] +
+                         covalent_radii[atomic_numbers[symbols[1]]])
+                else:
+                    b = self.bond_length
+                atoms = Atoms(name, positions=[(0, 0, 0),
+                                               (b, 0, 0)])
+            else:
+                raise ValueError('Unknown molecule: ' + name)
+
+        if self.unit_cell is None:
+            atoms.center(vacuum=self.vacuum)
+        else:
+            atoms.cell = self.unit_cell
+            atoms.center()
+
+        return atoms
+
+    def fit_bond_length(self, name, atoms):
+        N, x = self.fit
+        assert N % 2 == 1
+        d0 = atoms.get_distance(0, 1)
+        distances = np.linspace(d0 * (1 - x), d0 * (1 + x), N)
+        energies = []
+        traj = PickleTrajectory(self.get_filename(name, '-fit.traj'), 'w')
+        for d in distances:
+            atoms.set_distance(0, 1, d)
+            energies.append(atoms.get_potential_energy())
+            self.check_occupation_numbers(atoms)
+            traj.write(atoms)
+
+        traj.close()
+
+        data = {'energy': energies[N // 2],
+                'distances': distances,
+                'energies': energies}
+
+        return data
+
+    def calculate(self, name, atoms):
+        if self.fit and len(atoms) == 2:
+            return self.fit_bond_length(name, atoms)
+        else:
+            data = OptimizeTask.calculate(self, name, atoms)
+            self.check_occupation_numbers(atoms)
+            return data
+
+    def analyse(self):
+        OptimizeTask.analyse(self)
+
+        for name, data in self.data.items():
+            if 'distances' in data:
+                distances = data['distances']
+                energies = data['energies']
+                fit0 = np.poly1d(np.polyfit(1 / distances, energies, 3))
+                fit1 = np.polyder(fit0, 1)
+                fit2 = np.polyder(fit1, 1)
+
+                dmin = None
+                for t in np.roots(fit1):
+                    if t > 0 and fit2(t) > 0:
+                        dmin = 1 / t
+                        break
+
+                if dmin is None:
+                    raise ValueError('No minimum!')
+
+                if abs(dmin) < min(distances) or abs(dmin) > max(distances):
+                    raise ValueError('Fit outside of range! ' + \
+                                      str(abs(dmin)) + ' not in ' + \
+                                      str(distances))
+
+                emin = fit0(t)
+                k = fit2(t) * t**4
+                m1, m2 = self.create_system(name).get_masses()
+                m = m1 * m2 / (m1 + m2)
+                hnu = units._hbar * 1e10 * sqrt(k / units._e / units._amu / m)
+
+                data['minimum energy'] = emin
+                self.results[name][1:] = [energies[2] - emin, dmin, 1000 * hnu]
+            else:
+                self.results[name].extend([None, None])
+
+        for name, data in self.data.items():
+            atoms = self.create_system(name)
+            if len(atoms) == 1:
+                self.results[name].extend([None, None])
+                continue
+
+            eatoms = 0.0
+            for symbol in atoms.get_chemical_symbols():
+                if symbol in self.data and symbol != name:
+                    eatoms += self.data[symbol]['energy']
+                else:
+                    eatoms = None
+                    break
+            ea = None
+            ea0 = None
+            if eatoms is not None:
+                ea = eatoms - data['energy']
+                if 'minimum energy' in data:
+                    ea0 = eatoms - data['minimum energy']
+            self.results[name].extend([ea, ea0])
+
+    def add_options(self, parser):
+        OptimizeTask.add_options(self, parser)
+
+        mol = optparse.OptionGroup(parser, 'Molecule')
+        mol.add_option('-v', '--vacuum', type='float', default=3.0,
+                       help='Amount of vacuum to add around isolated systems '
+                       '(in Angstrom).')
+        mol.add_option('--unit-cell',
+                       help='Unit cell.  Examples: "10.0" or "9,10,11" ' +
+                       '(in Angstrom).')
+        mol.add_option('--bond-length', type='float',
+                       help='Bond length of dimer in Angstrom.')
+        mol.add_option('-F', '--fit', metavar='N,x',
+                       help='Find optimal bondlength and vibration ' +
+                       'frequency using N points and displacements from ' +
+                       '-x % to +x %.')
+        mol.add_option('--atomize', action='store_true',
+                       help='Calculate Atomization energies.')
+        parser.add_option_group(mol)
+
+    def parse(self, opts, args):
+        OptimizeTask.parse(self, opts, args)
+
+        self.vacuum = opts.vacuum
+        self.bond_length = opts.bond_length
+        self.atomize = opts.atomize
+
+        if opts.fit:
+            points, strain = opts.fit.split(',')
+            self.fit = (int(points), float(strain) * 0.01)
+
+        if opts.unit_cell:
+            if ',' in opts.unit_cell:
+                self.unit_cell = [float(x) for x in opts.unit_cell.split(',')]
+            else:
+                self.unit_cell = [float(opts.unit_cell)] * 3
+
+    def check_occupation_numbers(self, config):
+        """Check that occupation numbers are integers and sum
+        to desired magnetic moment.  """
+        if config.pbc.any():
+            return
+        calc = config.get_calculator()
+        try:
+            mref = abs(config.get_initial_magnetic_moments().sum())
+            nspins = calc.get_number_of_spins()
+            mcalc = 0.0
+            for s in range(nspins):
+                f = calc.get_occupation_numbers(spin=s)
+                if abs((f.round() - f).sum()) > 0.0001:
+                    raise RuntimeError('Fractional occupation numbers?! ' + \
+                                       str(f) + ' for spin ' + str(s))
+                mcalc += abs(f).sum() * (-1)**s
+            mcalc = abs(mcalc)
+            if mref > 0.0:
+                if  abs(mcalc - mref) > 0.0001:
+                    raise RuntimeError('Incorrect magnetic moment?! ' + \
+                                       str(mcalc) + ' vs ' + str(mref))
+
+        except AttributeError:
+            pass
diff --git a/ase/tasks/task.py b/ase/tasks/task.py
new file mode 100644
index 0000000..fe63943
--- /dev/null
+++ b/ase/tasks/task.py
@@ -0,0 +1,388 @@
+import sys
+import optparse
+import traceback
+from time import time
+
+import numpy as np
+
+from ase.parallel import world
+from ase.visualize import view
+from ase.io import read, write
+from ase.io import string2index
+from ase.constraints import FixAtoms
+from ase.optimize.lbfgs import LBFGS
+from ase.utils import opencew, devnull, prnt
+from ase.tasks.io import read_json, write_json
+from ase.data import chemical_symbols, atomic_numbers
+from ase.tasks.calcfactory import calculator_factory
+
+
+class Task:
+    taskname = 'generic-task'
+
+    def __init__(self, calcfactory='emt',
+                 tag=None, magmoms=None, gui=False,
+                 write_summary=False, use_lock_files=False,
+                 write_to_file=None, slice=slice(None),
+                 logfile='-'):
+
+        """Generic task object.
+
+        This task will do a single of the energy and forces for the
+        configurations that subcalsses define in their build_system()
+        methods.
+
+        calcfactory: CalculatorFactory object or str
+            A calculator factory or the name of a calculator.
+
+        For the meaning of the other arguments, see the add_options()
+        and parse_args() methods."""
+
+        self.set_calculator_factory(calcfactory)
+
+        self.tag = tag
+        self.magmoms = magmoms
+        self.gui = gui
+        self.write_summary = write_summary
+        self.use_lock_files = use_lock_files
+        self.write_to_file = write_to_file
+        self.slice = slice
+
+        if world.rank == 0:
+            if logfile is None:
+                logfile = devnull
+            elif isinstance(logfile, str):
+                if logfile == '-':
+                    logfile = sys.stdout
+                else:
+                    logfile = open(logfile, 'w')
+        else:
+            logfile = devnull
+        self.logfile = logfile
+        
+        self.write_funcs = [write_json]
+        self.read_func = read_json
+    
+        self.data = {}  # data read from json files
+        self.results = {}  # results from analysis of json files
+
+        self.summary_header = [('name', ''), ('E', 'eV')]
+
+        self.interactive_python_session = False
+        self.contains = None
+        self.modify = None
+
+    def set_calculator_factory(self, calcfactory):
+        if isinstance(calcfactory, str):
+            calcfactory = calculator_factory(calcfactory)
+
+        self.calcfactory = calcfactory
+        
+    def log(self, *args, **kwargs):
+        prnt(file=self.logfile, *args, **kwargs)
+
+    def get_filename(self, name=None, ext=''):
+        filename = self.taskname + '-' + self.calcfactory.name.lower()
+        if self.tag:
+            filename += '-' + self.tag
+        if name:
+            filename = name + '-' + filename
+        return filename + ext
+
+    def expand(self, names):
+        """Expand ranges like H-Li to H, He, Li."""
+        if isinstance(names, str):
+            names = [names]
+            
+        newnames = []
+        for name in names:
+            if name.count('-') == 1:
+                s1, s2 = name.split('-')
+                Z1 = atomic_numbers.get(s1)
+                Z2 = atomic_numbers.get(s2)
+                if Z1 is None or Z2 is None:
+                    newnames.append(name)
+                else:
+                    newnames.extend(chemical_symbols[Z1:Z2 + 1])
+            else:
+                newnames.append(name)
+
+        return newnames
+
+    def exclude(self, names):
+        newnames = []
+        for name in names:
+            atoms = self.create_system(name)
+            if (self.contains is None or
+                self.contains in atoms.get_chemical_symbols()):
+                newnames.append(name)
+        return newnames
+
+    def run(self, names):
+        """Run task far all names.
+
+        The task will be one of these four:
+
+        * Open ASE's GUI
+        * Write configuration to file
+        * Write summary
+        * Do the actual calculation
+        """
+
+        names = self.expand(names)
+        names = names[self.slice]
+        names = self.exclude(names)
+
+        if self.gui:
+            for name in names:
+                view(self.create_system(name))
+            return
+        
+        if self.write_to_file:
+            if self.write_to_file[0] == '.':
+                for name in names:
+                    filename = self.get_filename(name, self.write_to_file)
+                    write(filename, self.create_system(name))
+            else:
+                assert len(names) == 1
+                write(self.write_to_file, self.create_system(names[0]))
+            return
+
+        if self.write_summary:
+            self.read(names)
+            self.analyse()
+            self.summarize(names)
+            return
+
+        atoms = None
+        for name in names:
+            if self.use_lock_files:
+                lockfilename = self.get_filename(name, '.json')
+                fd = opencew(lockfilename)
+                if fd is None:
+                    self.log('Skipping', name)
+                    continue
+                fd.close()
+            atoms = self.run_single(name)
+        
+        return atoms
+
+    def run_single(self, name):
+        try:
+            atoms = self.create_system(name)
+        except Exception:
+            self.log(name, 'FAILED')
+            traceback.print_exc(file=self.logfile)
+            return
+            
+        atoms.calc = self.calcfactory(self.get_filename(name), atoms)
+
+        tstart = time()
+
+        try:
+            data = self.calculate(name, atoms)
+        except KeyboardInterrupt:
+            raise
+        except Exception:
+            self.log(name, 'FAILED')
+            traceback.print_exc(file=self.logfile)
+            return
+
+        tstop = time()
+        data['time'] = tstop - tstart
+
+        for write in self.write_funcs:
+            filenamebase = self.get_filename(name)
+            write(filenamebase, atoms, data)
+        
+        return atoms
+
+    def create_system(self, name):
+        if '.' in name:
+            system = read(name)
+        else:
+            system = self.build_system(name)
+
+        if self.magmoms is not None:
+            system.set_initial_magnetic_moments(
+                np.tile(self.magmoms, len(system) // len(self.magmoms)))
+
+        if self.modify:
+            exec self.modify
+
+        return system
+
+    def calculate(self, name, atoms):
+        e = atoms.get_potential_energy()
+        f = atoms.get_forces()
+        return {'energy': e, 'forces': f}
+
+    def read(self, names):
+        self.data = {}
+        for name in names:
+            filenamebase = self.get_filename(name)
+            try:
+                data = self.read_func(filenamebase)
+            except (IOError, SyntaxError, ValueError):
+                continue
+            self.data[name] = data
+
+    def analyse(self):
+        for name, data in self.data.items():
+            self.results[name] = [data['energy']]
+
+    def summarize(self, names):
+        self.log(' '.join('%10s' % x[0] for x in self.summary_header))
+        self.log(' '.join('%10s' % x[1] for x in self.summary_header))
+        for name in names:
+            data = self.results.get(name, [])
+            s = '%10s' % name
+            for x in data:
+                if x is None:
+                    s += '           '
+                else:
+                    s += '%11.3f' % x
+            self.log(s)
+
+    def create_parser(self):
+        calcname = self.calcfactory.name
+        parser = optparse.OptionParser(
+            usage='%prog [options] system(s)',
+            description='Run %s calculation.' % calcname)
+        self.add_options(parser)
+        return parser
+
+    def add_options(self, parser):
+        general = optparse.OptionGroup(parser, 'General')
+        general.add_option('-t', '--tag',
+                            help='String tag added to filenames.')
+        general.add_option('-M', '--magnetic-moment',
+                           metavar='M1,M2,...',
+                           help='Magnetic moment(s).  ' +
+                           'Use "-M 1" or "-M 2.3,-2.3".')
+        general.add_option('-G', '--gui', action='store_true',
+                            help="Pop up ASE's GUI.")
+        general.add_option('-s', '--write-summary', action='store_true',
+                            help='Write summary.')
+        general.add_option('--slice', metavar='start:stop:step',
+                            help='Select subset of calculations using ' +
+                            'Python slice syntax.  ' +
+                            'Use "::2" to do every second calculation and ' +
+                            '":-5" to do the last five.')
+        general.add_option('-w', '--write-to-file', metavar='FILENAME',
+                            help='Write configuration to file.')
+        general.add_option('-i', '--interactive-python-session',
+                            action='store_true',
+                            help='Run calculation inside interactive Python ' +
+                            'session.  A possible $PYTHONSTARTUP script ' +
+                            'will be imported and the "atoms" variable ' +
+                            'refers to the Atoms object.')
+        general.add_option('-l', '--use-lock-files', action='store_true',
+                            help='Skip calculations where the json ' +
+                           'lock-file or result file already exists.')
+        general.add_option('--contains', metavar='ELEMENT',
+                            help='Run only systems containing specific ' +
+                           'element.')
+        general.add_option('--modify', metavar='...',
+                            help='Modify system with Python statement.  ' +
+                           'Example: "system.positions[-1,2]+=0.1". ' +
+                           'Warning: no spaces allowed!')
+        parser.add_option_group(general)
+    
+    def parse_args(self, args=None):
+        if args is None:
+            args = sys.argv[1:]
+
+        parser = self.create_parser()
+        self.calcfactory.add_options(parser)
+        opts, args = parser.parse_args(args)
+
+        if len(args) == 0:
+            parser.error('incorrect number of arguments')
+
+        self.parse(opts, args)
+        self.calcfactory.parse(opts, args)
+
+        return args
+
+    def parse(self, opts, args):
+        if opts.tag:
+            self.tag = opts.tag
+            
+        if opts.magnetic_moment:
+            self.magmoms = np.array(
+                [float(m) for m in opts.magnetic_moment.split(',')])
+        
+        self.gui = opts.gui
+        self.write_summary = opts.write_summary
+        self.write_to_file = opts.write_to_file
+        self.use_lock_files = opts.use_lock_files
+        self.interactive_python_session = opts.interactive_python_session
+        self.contains = opts.contains
+        self.modify = opts.modify
+
+        if opts.slice:
+            self.slice = string2index(opts.slice)
+
+
+class OptimizeTask(Task):
+    taskname = 'opt'
+
+    def __init__(self, fmax=None, constrain_tags=[], **kwargs):
+        self.fmax = fmax
+        self.constrain_tags = constrain_tags
+
+        Task.__init__(self, **kwargs)
+        
+        self.summary_header.append(('E-E0', 'eV'))
+
+    def optimize(self, name, atoms):
+        mask = [t in self.constrain_tags for t in atoms.get_tags()]
+        if mask:
+            constrain = FixAtoms(mask=mask)
+            atoms.constraints = [constrain]
+
+        optimizer = LBFGS(atoms, trajectory=self.get_filename(name, '.traj'),
+                          logfile=None)
+        optimizer.run(self.fmax)
+        
+    def calculate(self, name, atoms):
+        data = Task.calculate(self, name, atoms)
+
+        if self.fmax is not None:
+            self.optimize(name, atoms)
+
+            data['minimum energy'] = atoms.get_potential_energy()
+            data['minimum forces'] = atoms.get_forces()
+        
+        return data
+
+    def analyse(self):
+        Task.analyse(self)
+        for name, data in self.data.items():
+            if 'minimum energy' in data:
+                self.results[name].append(data['energy'] -
+                                          data['minimum energy'])
+            else:
+                self.results[name].append(None)
+
+    def add_options(self, parser):
+        Task.add_options(self, parser)
+
+        optimize = optparse.OptionGroup(parser, 'Optimize')
+        optimize.add_option('-R', '--relax', type='float', metavar='FMAX',
+                            help='Relax internal coordinates using L-BFGS '
+                            'algorithm.')
+        optimize.add_option('--constrain-tags', type='str',
+                            metavar='T1,T2,...',
+                            help='Constrain atoms with tags T1, T2, ...')
+        parser.add_option_group(optimize)
+
+    def parse(self, opts, args):
+        Task.parse(self, opts, args)
+
+        self.fmax = opts.relax
+
+        if opts.constrain_tags:
+            self.constrain_tags = [int(t)
+                                   for t in opts.constrain_tags.split(',')]
diff --git a/ase/test/Ag-Cu100.py b/ase/test/Ag-Cu100.py
new file mode 100644
index 0000000..8fadd51
--- /dev/null
+++ b/ase/test/Ag-Cu100.py
@@ -0,0 +1,64 @@
+from math import sqrt
+from ase import Atom, Atoms
+from ase.neb import NEB
+from ase.constraints import FixAtoms
+from ase.vibrations import Vibrations
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton, BFGS
+
+# Distance between Cu atoms on a (100) surface:
+d = 3.6 / sqrt(2)
+initial = Atoms('Cu',
+                positions=[(0, 0, 0)],
+                cell=(d, d, 1.0),
+                pbc=(True, True, False))
+initial *= (2, 2, 1)  # 2x2 (100) surface-cell
+
+# Approximate height of Ag atom on Cu(100) surfece:
+h0 = 2.0
+initial += Atom('Ag', (d / 2, d / 2, h0))
+
+if 0:
+    view(initial)
+
+# Make band:
+images = [initial.copy() for i in range(6)]
+neb = NEB(images, climb=True)
+
+# Set constraints and calculator:
+constraint = FixAtoms(range(len(initial) - 1))
+for image in images:
+    image.set_calculator(EMT())
+    image.set_constraint(constraint)
+
+# Displace last image:
+images[-1].positions[-1] += (d, 0, 0)
+#images[-1].positions[-1] += (d, d, 0)
+
+# Relax height of Ag atom for initial and final states:
+dyn1 = QuasiNewton(images[0])
+dyn1.run(fmax=0.01)
+dyn2 = QuasiNewton(images[-1])
+dyn2.run(fmax=0.01)
+
+# Interpolate positions between initial and final states:
+neb.interpolate()
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+#dyn = MDMin(neb, dt=0.4)
+#dyn = FIRE(neb, dt=0.4)
+dyn = BFGS(neb, trajectory='mep.traj')
+dyn.run(fmax=0.05)
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+a = images[0]
+vib = Vibrations(a, [4])
+vib.run()
+print vib.get_frequencies()
+vib.summary()
+print vib.get_mode(-1)
+vib.write_mode(-1, nimages=20)
diff --git a/ase/test/CO2_Au111.py b/ase/test/CO2_Au111.py
new file mode 100644
index 0000000..7f95935
--- /dev/null
+++ b/ase/test/CO2_Au111.py
@@ -0,0 +1,31 @@
+from math import sqrt, pi
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.constraints import FixBondLengths
+from ase.optimize import BFGS, QuasiNewton
+from ase.neb import SingleCalculatorNEB
+from ase.lattice.surface import fcc111, add_adsorbate
+from math import sqrt, cos, sin
+
+
+zpos = cos(134.3/2.0*pi/180.0)*1.197
+xpos = sin(134.3/2.0*pi/180.0)*1.19
+co2 = Atoms('COO', positions=[(-xpos+1.2,0,-zpos),
+                              (-xpos+1.2,-1.1,-zpos),
+                              (-xpos+1.2,1.1,-zpos)])
+
+slab = fcc111('Au', size=(2, 2, 4), vacuum=2*5, orthogonal=True)
+slab.center()
+add_adsorbate(slab,co2,1.5,'bridge')
+slab.set_pbc((True,True,False))
+d0 = co2.get_distance(-3, -2)
+d1 = co2.get_distance(-3, -1)
+
+calc = EMT()
+slab.set_calculator(calc)
+constraint = FixBondLengths([[-3,-2],[-3,-1]])
+slab.set_constraint(constraint)
+dyn = BFGS(slab, trajectory='relax.traj')
+dyn.run(fmax=0.05)
+assert abs(co2.get_distance(-3, -2) - d0) < 1e-14
+assert abs(co2.get_distance(-3, -1) - d1) < 1e-14
diff --git a/ase/test/COCu111.py b/ase/test/COCu111.py
new file mode 100644
index 0000000..38dea91
--- /dev/null
+++ b/ase/test/COCu111.py
@@ -0,0 +1,79 @@
+from math import sqrt
+from ase import Atoms, Atom
+from ase.calculators.emt import EMT
+from ase.constraints import FixAtoms
+from ase.optimize import BFGS, QuasiNewton
+from ase.neb import NEB
+
+# Distance between Cu atoms on a (111) surface:
+a = 3.6
+d = a / sqrt(2)
+fcc111 = Atoms(symbols='Cu',
+               cell=[(d, 0, 0),
+                     (d / 2, d * sqrt(3) / 2, 0),
+                     (d / 2, d * sqrt(3) / 6, -a / sqrt(3))],
+               pbc=True)
+slab = fcc111 * (2, 2, 4)
+slab.set_cell([2 * d, d * sqrt(3), 1])
+slab.set_pbc((1, 1, 0))
+slab.calc = EMT()
+Z = slab.get_positions()[:, 2]
+indices = [i for i, z in enumerate(Z) if z < Z.mean()]
+constraint = FixAtoms(indices=indices)
+slab.set_constraint(constraint)
+dyn = QuasiNewton(slab)
+dyn.run(fmax=0.05)
+Z = slab.get_positions()[:, 2]
+print Z[0] - Z[1]
+print Z[1] - Z[2]
+print Z[2] - Z[3]
+
+b = 1.2
+h = 1.5
+slab += Atom('C', (d / 2, -b / 2, h))
+slab += Atom('O', (d / 2, +b / 2, h))
+s = slab.copy()
+dyn = QuasiNewton(slab)
+dyn.run(fmax=0.05)
+#view(slab)
+
+# Make band:
+images = [slab]
+for i in range(6):
+    image = slab.copy()
+    image.set_constraint(constraint)
+    image.calc = EMT()
+    images.append(image)
+image[-2].position = image[-1].position
+image[-1].x = d
+image[-1].y = d / sqrt(3)
+dyn = QuasiNewton(images[-1])
+dyn.run(fmax=0.05)
+neb = NEB(images, climb=not True)
+
+# Set constraints and calculator:
+
+# Displace last image:
+
+# Relax height of Ag atom for initial and final states:
+
+# Interpolate positions between initial and final states:
+neb.interpolate()
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+#dyn = MDMin(neb, dt=0.4)
+#dyn = FIRE(neb, dt=0.01)
+dyn = BFGS(neb, maxstep=0.04, trajectory='mep.traj')
+#from ase.optimize.oldqn import GoodOldQuasiNewton
+#dyn = GoodOldQuasiNewton(neb)
+dyn.run(fmax=0.05)
+
+for image in images:
+    print image.positions[-1], image.get_potential_energy()
+
+if locals().get('display'):
+    import os
+    error = os.system('ag mep.traj at -7:')
+    assert error == 0
diff --git a/ase/test/COCu111_2.py b/ase/test/COCu111_2.py
new file mode 100644
index 0000000..832699c
--- /dev/null
+++ b/ase/test/COCu111_2.py
@@ -0,0 +1,71 @@
+from math import sqrt
+from ase import Atoms, Atom
+from ase.constraints import FixAtoms
+from ase.optimize import FIRE, QuasiNewton, BFGS
+from ase.neb import SingleCalculatorNEB
+from ase.calculators.emt import EMT
+
+Optimizer = BFGS
+
+# Distance between Cu atoms on a (111) surface:
+a = 3.6
+d = a / sqrt(2)
+fcc111 = Atoms(symbols='Cu',
+               cell=[(d, 0, 0),
+                     (d / 2, d * sqrt(3) / 2, 0),
+                     (d / 2, d * sqrt(3) / 6, -a / sqrt(3))],
+               pbc=True)
+initial = fcc111 * (2, 2, 4)
+initial.set_cell([2 * d, d * sqrt(3), 1])
+initial.set_pbc((1, 1, 0))
+initial.set_calculator(EMT())
+Z = initial.get_positions()[:, 2]
+indices = [i for i, z in enumerate(Z) if z < Z.mean()]
+constraint = FixAtoms(indices=indices)
+initial.set_constraint(constraint)
+dyn = Optimizer(initial)
+dyn.run(fmax=0.05)
+Z = initial.get_positions()[:, 2]
+print Z[0] - Z[1]
+print Z[1] - Z[2]
+print Z[2] - Z[3]
+
+b = 1.2
+h = 1.5
+initial += Atom('C', (d / 2, -b / 2, h))
+initial += Atom('O', (d / 2, +b / 2, h))
+s = initial.copy()
+dyn = Optimizer(initial)
+dyn.run(fmax=0.05)
+#view(initial)
+
+# create final
+final = initial.copy()
+final.set_calculator(EMT())
+final.set_constraint(constraint)
+final[-2].position = final[-1].position
+final[-1].x = d
+final[-1].y = d / sqrt(3)
+dyn = Optimizer(final)
+dyn.run(fmax=0.1)
+#view(final)
+
+# create 2 intermediate step neb
+neb = SingleCalculatorNEB([initial, final])
+neb.refine(2)
+neb.set_calculators(EMT())
+assert neb.n() == 4
+
+dyn = Optimizer(neb, maxstep=0.04, trajectory='mep_2coarse.traj')
+dyn.run(fmax=0.1)
+#dyn.run(fmax=39.1)
+
+# read from the trajectory
+neb = SingleCalculatorNEB('mep_2coarse.traj at -4:')
+
+# refine in the important region
+neb.refine(2, 1, 3)
+neb.set_calculators(EMT())
+dyn = Optimizer(neb, maxstep=0.04, trajectory='mep_2fine.traj')
+dyn.run(fmax=0.1)
+assert len(neb.images) == 8
diff --git a/ase/test/__init__.py b/ase/test/__init__.py
new file mode 100644
index 0000000..36ad1bd
--- /dev/null
+++ b/ase/test/__init__.py
@@ -0,0 +1,173 @@
+import os
+
+import platform
+import sys
+import unittest
+from glob import glob
+
+import numpy as np
+
+
+class NotAvailable(SystemExit):
+    def __init__(self, msg, code=0):
+        SystemExit.__init__(self, (msg,code,))
+        self.msg = msg
+        self.code = code
+
+# -------------------------------------------------------------------
+
+# Custom test case/suite for embedding unittests in the test scripts
+
+if sys.version_info < (2, 4, 0, 'final', 0):
+    class CustomTestCase(unittest.TestCase):
+        assertTrue = unittest.TestCase.failUnless
+        assertFalse = unittest.TestCase.failIf
+else:
+    from unittest import TestCase as CustomTestCase
+
+from ase.parallel import paropen
+
+class CustomTextTestRunner(unittest.TextTestRunner):
+    def __init__(self, logname, descriptions=1, verbosity=1):
+        self.f = paropen(logname, 'w')
+        unittest.TextTestRunner.__init__(self, self.f, descriptions, verbosity)
+
+    def run(self, test):
+        stderr_old = sys.stderr
+        try:
+            sys.stderr = self.f
+            testresult = unittest.TextTestRunner.run(self, test)
+        finally:
+            sys.stderr = stderr_old
+        return testresult
+
+# -------------------------------------------------------------------
+
+class ScriptTestCase(unittest.TestCase):
+    def __init__(self, methodname='testfile', filename=None, display=True):
+        unittest.TestCase.__init__(self, methodname)
+        self.filename = filename
+        self.display = display
+
+    def testfile(self):
+        try:
+            execfile(self.filename, {'display': self.display})
+        except KeyboardInterrupt:
+            raise RuntimeError('Keyboard interrupt')
+        except NotAvailable, err:
+            # Only non-zero error codes are failures
+            if err.code:
+                raise
+
+    def id(self):
+        return self.filename
+
+    def __str__(self):
+        return '%s (ScriptTestCase)' % self.filename.split('/')[-1]
+
+    def __repr__(self):
+        return "ScriptTestCase(filename='%s')" % self.filename
+
+
+def test(verbosity=1, dir=None, display=True, stream=sys.stdout):
+    ts = unittest.TestSuite()
+    if dir is None:
+        # ase/test (__path__[0])
+        testdir = __path__[0]
+    else:
+        if os.path.isdir(dir):
+            # absolute path
+            testdir = dir
+        else:
+            # relative to ase/test (__path__[0])
+            testdir = os.path.join(__path__[0], dir)
+    files = glob(testdir + '/*')
+    sdirtests = [] # tests from subdirectories: only one level assumed
+    tests = []
+    for f in files:
+        if os.path.isdir(f):
+            # add test subdirectories (like calculators)
+            sdirtests.extend(glob(os.path.join(testdir, f) + '/*.py'))
+        else:
+            # add py files in testdir
+            if f.endswith('.py'):
+                tests.append(f)
+    tests.sort()
+    sdirtests.sort()
+    tests.extend(sdirtests) # run test subdirectories at the end
+    lasttest = None # is COCu111.py in the current set
+    for test in tests:
+        if test.endswith('__init__.py'):
+            continue
+        if test.endswith('COCu111.py'):
+            lasttest = test
+            continue
+        ts.addTest(ScriptTestCase(filename=test, display=display))
+    if lasttest:
+        ts.addTest(ScriptTestCase(filename=lasttest, display=display))
+
+    operating_system = platform.system() + ' ' + platform.machine()
+    operating_system += ' ' + ' '.join(platform.dist())
+    python = platform.python_version() + ' ' + platform.python_compiler()
+    python += ' ' + ' '.join(platform.architecture())
+    print 'python %s on %s' % (python, operating_system)
+
+    from ase.utils import devnull
+    sys.stdout = devnull
+
+    ttr = unittest.TextTestRunner(verbosity=verbosity, stream=stream)
+    results = ttr.run(ts)
+
+    sys.stdout = sys.__stdout__
+
+    return results
+
+
+class World:
+    """Class for testing parallelization with MPI"""
+    def __init__(self, size):
+        self.size = size
+        self.data = {}
+
+    def get_rank(self, rank):
+        return CPU(self, rank)
+
+class CPU:
+    def __init__(self, world, rank):
+        self.world = world
+        self.rank = rank
+        self.size = world.size
+
+    def send(self, x, rank):
+        while (self.rank, rank) in self.world.data:
+            pass
+        self.world.data[(self.rank, rank)] = x
+
+    def receive(self, x, rank):
+        while (rank, self.rank) not in self.world.data:
+            pass
+        x[:] = self.world.data.pop((rank, self.rank))
+
+    def sum(self, x):
+        if not isinstance(x, np.ndarray):
+            x = np.array([x])
+            self.sum(x)
+            return x[0]
+
+        if self.rank == 0:
+            y = np.empty_like(x)
+            for rank in range(1, self.size):
+                self.receive(y, rank)
+                x += y
+        else:
+            self.send(x, 0)
+
+        self.broadcast(x, 0)
+
+    def broadcast(self, x, root):
+        if self.rank == root:
+            for rank in range(self.size):
+                if rank != root:
+                    self.send(x, rank)
+        else:
+            self.receive(x, root)
diff --git a/ase/test/abinit/abinit_cmdline.py b/ase/test/abinit/abinit_cmdline.py
new file mode 100644
index 0000000..76fa671
--- /dev/null
+++ b/ase/test/abinit/abinit_cmdline.py
@@ -0,0 +1,17 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    abinit_pp_path = os.getenv('ABINIT_PP_PATH')
+    if abinit_pp_path == None:
+        raise NotAvailable('ABINIT_PP_PATH not defined')
+except NotAvailable:
+    raise NotAvailable('Abinit required')
+
+import numpy as np
+
+from ase.tasks.main import run
+
+atoms, task = run("abinit bulk Al -x fcc -a 4.04 --k-point-density=3.0 -p xc='PBE',ecut=340")
+atoms, task = run('abinit bulk Al -s')
diff --git a/ase/test/ag.py b/ase/test/ag.py
new file mode 100644
index 0000000..5f2aa00
--- /dev/null
+++ b/ase/test/ag.py
@@ -0,0 +1,21 @@
+'Some ag tests.'
+
+import os
+import sys
+
+# Make sure ag can run in terminal mode without $DISPLAY and gtk:
+sys.argv = ['ag', '--terminal']
+display = os.environ.pop('DISPLAY', None)
+error = False
+try:
+    from ase.gui.ag import main
+    main()
+    assert 'gtk' not in sys.modules
+except:
+    error = True
+
+if display is not None:
+    os.environ['DISPLAY'] = display
+
+if error:
+    raise
diff --git a/ase/test/atom.py b/ase/test/atom.py
new file mode 100644
index 0000000..24ffdfa
--- /dev/null
+++ b/ase/test/atom.py
@@ -0,0 +1,30 @@
+from ase import Atom, Atoms
+
+m = Atoms('H2')
+a = m[0]
+b = Atom('H')
+for c in [a, b]:
+    assert c.x == 0
+    c.z = 24.0
+    assert c.position[2] == 24.0
+    assert c.symbol == 'H'
+    c.number = 92
+    assert c.symbol == 'U'
+    c.symbol = 'Fe'
+    assert c.number == 26
+    c.tag = 42
+    assert c.tag == 42
+    c.momentum = (1,2,3)
+assert m[0].tag == 42
+momenta = m.get_momenta()
+m = Atoms('LiH')
+for a in m:
+    print a.symbol
+for a in m:  
+    if a.symbol == 'H':
+        a.z = 0.75
+assert m.get_distance(0, 1) == 0.75
+a = m.pop()
+m += a
+del m[:1]
+print m
diff --git a/ase/test/bader.py b/ase/test/bader.py
new file mode 100644
index 0000000..95fd947
--- /dev/null
+++ b/ase/test/bader.py
@@ -0,0 +1,28 @@
+import os
+from ase.structure import molecule
+from ase.io.bader import attach_charges
+
+fname = 'ACF.dat'
+f = open(fname, 'w')
+print >> f, """
+   #         X           Y           Z        CHARGE     MIN DIST
+ ----------------------------------------------------------------
+   1      7.0865      8.5038      9.0672      9.0852      1.3250
+   2      7.0865      9.9461      7.9403      0.4574      0.3159
+   3      7.0865      7.0615      7.9403      0.4574      0.3159
+ ----------------------------------------------------------------
+  NUMBER OF ELECTRONS:        9.99999
+"""
+f.close()
+
+atoms = molecule('H2O')
+atoms.set_cell([7.5, 9, 9])
+atoms.center()
+
+attach_charges(atoms)
+attach_charges(atoms, fname)
+os.remove(fname)
+
+for atom in atoms:
+    print 'Atom', atom.symbol, 'Bader charge', atom.charge 
+
diff --git a/ase/test/basin.py b/ase/test/basin.py
new file mode 100644
index 0000000..802647a
--- /dev/null
+++ b/ase/test/basin.py
@@ -0,0 +1,35 @@
+import numpy as np
+from math import pi, sqrt
+from ase import Atoms
+from ase.calculators.lj import LennardJones
+from ase.optimize.basin import BasinHopping
+from ase.io import PickleTrajectory, read
+from ase.units import kB
+
+N = 7
+R = N**(1./3.)
+pos = np.random.uniform(-R, R, (N, 3))
+s = Atoms('He' + str(N),
+          positions = pos)
+s.set_calculator(LennardJones())
+
+ftraj = 'lowest.traj'
+traj = PickleTrajectory(ftraj, 'w', s)
+bh = BasinHopping(s, 
+                  temperature=100 * kB, dr=0.5, 
+                  optimizer_logfile=None)
+bh.attach(traj)
+bh.run(10)
+
+Emin, smin = bh.get_minimum()
+
+# recalc energy
+smin.set_calculator(LennardJones())
+E = smin.get_potential_energy()
+assert abs(E - Emin) < 1e-15
+traj.close()
+smim = read(ftraj)
+E = smin.get_potential_energy()
+assert abs(E - Emin) < 1e-15
+
+#view(smin)
diff --git a/ase/test/build.py b/ase/test/build.py
new file mode 100644
index 0000000..44b1058
--- /dev/null
+++ b/ase/test/build.py
@@ -0,0 +1,21 @@
+import numpy as np
+from ase import Atoms, Atom
+
+a = Atoms([Atom('Cu')])
+a.positions[:] += 1.0
+print a.get_positions(), a.positions
+a=a+a
+a+=a
+a.append(Atom('C'))
+a += Atoms([])
+a += Atom('H', magmom=1)
+print a.get_initial_magnetic_moments()
+print a[0].number
+print a[[0,1]].get_atomic_numbers()
+print a[np.array([1,1,0,0,1], bool)].get_atomic_numbers()
+print a[::2].get_atomic_numbers()
+print a.get_chemical_symbols()
+del a[2]
+print a.get_chemical_symbols()
+del a[-2:]
+print a.get_chemical_symbols()
diff --git a/ase/test/castep/castep_interface.py b/ase/test/castep/castep_interface.py
new file mode 100644
index 0000000..911a5bc
--- /dev/null
+++ b/ase/test/castep/castep_interface.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+"""Simple shallow test of the CASTEP interface"""
+
+import os
+import shutil
+import tempfile
+import traceback
+
+from ase.test import NotAvailable
+
+# check if CASTEP_COMMAND is set a environment variable
+
+if not os.environ.has_key('CASTEP_COMMAND'):
+    print("WARNING: Environment variable CASTEP_COMMAND is not set")
+    print("Will set CASTEP_COMMAND  = castep for the sake of this test")
+    print("Please change it if this does not run castep in your environment")
+    os.environ['CASTEP_COMMAND'] = 'castep'
+
+
+if not (os.system('which %s' % os.environ['CASTEP_COMMAND']) == 0):
+    raise NotAvailable("""Could not find CASTEP. If you have it
+                          installed make sure, you set the CASTEP_COMMAND
+                          environment variable correctly""")
+
+
+# check if we can import everything
+ase_castep_dir = "ase"
+
+try:
+    castep_calc = __import__(ase_castep_dir + ".calculators.castep", globals(), locals(), ["Castep", "CastepParam", "create_castep_keywords"])
+    Castep = castep_calc.Castep
+    CastepParam = castep_calc.CastepParam
+    create_castep_keywords = castep_calc.create_castep_keywords
+
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert False, 'Castep calculator module could not be loaded'
+
+try:
+    __import__(ase_castep_dir + ".io.castep")
+except Exception, e:
+    assert False, 'Castep io module could not be loaded'
+
+
+tmp_dir = tempfile.mkdtemp()
+cwd = os.getcwd()
+
+from ase.calculators.castep import Castep
+
+try:
+    c = Castep(directory=tmp_dir, label='test_label')
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert False, 'Could not instantiate castep calculator'
+
+
+try:
+    c.xc_functional = 'PBE'
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert False, 'Setting xc_functional  failed'
+
+import ase.lattice.cubic
+lattice = ase.lattice.cubic.BodyCenteredCubic('Li' )
+
+print('For the sake of evaluating this test, warnings')
+print('about auto-generating pseudo-potentials are')
+print('normal behavior and can be safely ignored')
+
+try:
+    lattice.set_calculator(c)
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert False, 'Setting the calculator %s failed' % c
+
+
+
+try:
+    create_castep_keywords(
+        castep_command=os.environ['CASTEP_COMMAND'],
+        path=tmp_dir,
+        fetch_only=20)
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert  False, "Cannot create castep_keywords, this usually means a  bug"\
+    + " in the interface or the castep binary cannot be called"
+
+
+param_fn = os.path.join(tmp_dir, 'myParam.param')
+param = open(param_fn,'w')
+param.write('XC_FUNCTIONAL : PBE #comment\n')
+param.write('XC_FUNCTIONAL : PBE #comment\n')
+param.write('#comment\n')
+param.write('CUT_OFF_ENERGY : 450.\n')
+param.close()
+try:
+    c.merge_param(param_fn)
+except Exception, e:
+    traceback.print_exc()
+    print(e)
+    assert False,"Error in merge_param_filename, go figure"
+
+
+# check if the CastepOpt, CastepCell comparison mechanism works
+
+p1 = CastepParam()
+p2 = CastepParam()
+assert p1._options == p2._options, "Print two newly created CastepParams are not the same"
+
+p1._options['xc_functional'].value = 'PBE'
+p1.xc_functional = 'PBE'
+
+assert not p1._options == p2._options, "Changed one CastepParam, but the still look the same"
+
+assert c.calculation_required(lattice), 'Calculator does not fetch that a calculation is required'
+
+if not c.dryrun_ok():
+    print(c._error)
+    assert False, "Dryrun_ok does not work, where it should"
+else:
+    print("Dryrun is ok")
+
+c.prepare_input_files(lattice)
+
+os.chdir(cwd)
+shutil.rmtree(tmp_dir)
+
+
+print("Test finished without errors")
\ No newline at end of file
diff --git a/ase/test/center.py b/ase/test/center.py
new file mode 100644
index 0000000..19c2e68
--- /dev/null
+++ b/ase/test/center.py
@@ -0,0 +1,82 @@
+"Test that atoms.center() works when adding vacuum ()"
+
+import numpy as np
+from math import pi, sqrt, cos
+from ase import data
+from ase.lattice.cubic import FaceCenteredCubic
+
+def checkang(a, b, phi):
+    "Check the angle between two vectors."
+    cosphi = np.dot(a,b) / sqrt(np.dot(a,a) * np.dot(b,b))
+    assert np.abs(cosphi - cos(phi)) < 1e-10
+
+symb = "Cu"
+Z = data.atomic_numbers[symb]
+a0 = data.reference_states[Z]['a']
+
+# (100) oriented block
+atoms = FaceCenteredCubic(size=(5,5,5), symbol="Cu", pbc=(1,1,0))
+assert len(atoms) == 5*5*5*4
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/2)
+checkang(c[1], c[2], pi/2)
+assert np.abs(5 * a0 - c[2,2]) < 1e-10
+
+# Add vacuum in one direction
+vac = 10.0
+atoms.center(axis=2, vacuum=vac)
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/2)
+checkang(c[1], c[2], pi/2)
+assert np.abs(4.5 * a0 + 2* vac - c[2,2]) < 1e-10
+
+# Add vacuum in all directions
+vac = 4.0
+atoms.center(vacuum=vac)
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/2)
+checkang(c[1], c[2], pi/2)
+assert np.abs(4.5 * a0 + 2* vac - c[0,0]) < 1e-10
+assert np.abs(4.5 * a0 + 2* vac - c[1,1]) < 1e-10
+assert np.abs(4.5 * a0 + 2* vac - c[2,2]) < 1e-10
+
+# Now a general unit cell
+atoms = FaceCenteredCubic(size=(5,5,5), directions=[[1,0,0], [0,1,0], [1,0,1]],
+                          symbol="Cu", pbc=(1,1,0))
+assert len(atoms) == 5*5*5*2
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/4)
+checkang(c[1], c[2], pi/2)
+assert np.abs(2.5 * a0 - c[2,2]) < 1e-10
+
+# Add vacuum in one direction
+vac = 10.0
+atoms.center(axis=2, vacuum=vac)
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/4)
+checkang(c[1], c[2], pi/2)
+assert np.abs(2 * a0 + 2* vac - c[2,2]) < 1e-10
+
+# Recenter without specifying vacuum
+atoms.center()
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/4)
+checkang(c[1], c[2], pi/2)
+assert np.abs(2 * a0 + 2* vac - c[2,2]) < 1e-10
+
+# Add vacuum in all directions
+vac = 4.0
+atoms.center(vacuum=vac)
+c = atoms.get_cell()
+checkang(c[0], c[1], pi/2)
+checkang(c[0], c[2], pi/4)
+checkang(c[1], c[2], pi/2)
+assert np.abs(4.5 * a0 + 2* vac - c[1,1]) < 1e-10
+assert np.abs(2 * a0 + 2* vac - c[2,2]) < 1e-10
+
diff --git a/ase/test/cmdline.py b/ase/test/cmdline.py
new file mode 100644
index 0000000..c7b60e5
--- /dev/null
+++ b/ase/test/cmdline.py
@@ -0,0 +1,13 @@
+import numpy as np
+from ase.tasks.main import run
+atoms, task = run('H2 --bond-length=0.78 -R 0.01 -F 5,2 --atomize')
+atoms, task = run('H2 H -s')
+results = np.array(task.results['H2'])
+assert abs(results -
+           [1.071, 0.000, 0.779, 862.780, 5.349, 5.349]).max() < 0.001
+
+atoms, task = run('bulk Cu -F 5,2')
+atoms, task = run('bulk Cu -s')
+results = np.array(task.results['Cu'])
+assert abs(results -
+           [-0.0057, 0.0014, 11.5654, 134.4389]).max() < 0.001
diff --git a/ase/test/cmr/__init__.py b/ase/test/cmr/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ase/test/cmr/ase_rw.py b/ase/test/cmr/ase_rw.py
new file mode 100644
index 0000000..2f5988d
--- /dev/null
+++ b/ase/test/cmr/ase_rw.py
@@ -0,0 +1,34 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from ase.calculators.emt import EMT
+from ase.io import read, write
+from ase.structure import molecule
+
+cmr_params = {"db_keywords":["O", "ase"], # keyword
+              "molecule":"O2"} #field
+
+m1 = molecule('O2')
+m1.set_calculator(EMT())
+e1 = m1.get_potential_energy()
+write("O2.db", m1, cmr_params = cmr_params)
+
+reread = read("O2.db")
+e2 = reread.get_potential_energy()
+assert abs(e1 - e2) < 1.e-6, str(e1) + ' ' + str(e2)
+
+db_read = cmr.read("O2.db")
+assert "O" in db_read["db_keywords"]
+assert "ase" in db_read["db_keywords"]
+assert db_read["molecule"] == "O2"
+
+# clean
+filename = "O2.db"
+if os.path.exists(filename): os.unlink(filename)
+
diff --git a/ase/test/cmr/cmr_rw.py b/ase/test/cmr/cmr_rw.py
new file mode 100644
index 0000000..a14a21f
--- /dev/null
+++ b/ase/test/cmr/cmr_rw.py
@@ -0,0 +1,30 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from ase.calculators.emt import EMT
+from ase.structure import molecule
+
+m1 = molecule('O2')
+m1.set_calculator(EMT())
+e1 = m1.get_potential_energy()
+
+data = cmr.atoms2cmr(m1)
+data.set_user_variable("molecule", "O2")
+data.set_user_variable("potential", "EMT")
+data.set_user_variable("db_keywords", ["O2", "EMT"])
+
+data.write("O2.db")
+
+reread = cmr.read("O2.db")
+e2 = reread["ase_potential_energy"]
+assert abs(e1-e2) < 1.e-6, str(e1) + ' ' + str(e2)
+
+# clean
+filename = "O2.db"
+if os.path.exists(filename): os.unlink(filename)
diff --git a/ase/test/cmr/reactions.py b/ase/test/cmr/reactions.py
new file mode 100644
index 0000000..9e7505d
--- /dev/null
+++ b/ase/test/cmr/reactions.py
@@ -0,0 +1,17 @@
+# Define the list of reactions here:
+# [('N2', -1), ('N', 2), ('reaction_energy', None), ('reaction_id', 1)] denotes:
+# reaction_energy = -1 * N2 + 2 *N
+# reaction_id is an integer or string to identify the reaction
+
+reactions = [  [('N2', -1), ('N', 2), ('reaction_id', 1)],
+               [('H2O', -1), ('H2', 1), ('O2', 0.5), ('reaction_id', 'H2O decomposition')],
+               [('H2O', -1), ('H', 2), ('O', 1), ('reaction_id', 3)],
+            ]
+
+# reference reaction energies
+reference = {
+    # 'reaction_id' : reaction energy
+    reactions[0][-1][1]: 9.93722251591,
+    reactions[1][-1][1]: -0.496408928568,
+    reactions[2][-1][1]: 9.14067431054,
+    }
diff --git a/ase/test/cmr/reactions_run.py b/ase/test/cmr/reactions_run.py
new file mode 100644
index 0000000..53889de
--- /dev/null
+++ b/ase/test/cmr/reactions_run.py
@@ -0,0 +1,43 @@
+from ase.test import NotAvailable
+
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from ase.calculators.emt import EMT
+
+from ase.structure import molecule
+
+from ase.io import write
+from ase.optimize import QuasiNewton
+
+# see the module for the required format of reactions definition
+from ase.test.cmr.reactions import reactions
+
+# assure that all reactions define a reaction_id
+for r in reactions:
+    assert r[-1][0] == 'reaction_id'
+
+optimize = True
+
+calculator = EMT()
+
+# find names of compounds
+# (in one of the most obscure ways - python list flattening)
+compounds = [c[0] for c in sum([r[:-1] for r in reactions], [])]
+# unique
+compounds = list(set(compounds))
+
+for formula in compounds:
+    m = molecule(formula)
+    m.set_calculator(calculator)
+    if optimize:
+        dyn = QuasiNewton(m,
+                          logfile=('%s.log' % formula),
+                          trajectory=('%s.traj' % formula),
+                          )
+        dyn.run()
+    else:
+        e = m.get_potential_energy()
+        write(filename=('%s.traj' % formula), images=m, format='traj')
diff --git a/ase/test/cmr/reactions_test.py b/ase/test/cmr/reactions_test.py
new file mode 100644
index 0000000..eee2f9c
--- /dev/null
+++ b/ase/test/cmr/reactions_test.py
@@ -0,0 +1,84 @@
+from ase.test import NotAvailable
+
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from cmr.ui import DirectoryReader
+
+from cmr.test.examples.ase_reaction_energy import ASEReactionEnergy
+
+# see the module for the required format of reactions definition
+from ase.test.cmr.reactions import reactions
+from ase.test.cmr.reactions import reference
+
+# assure that all reactions define a reaction_id
+for r in reactions:
+    assert r[-1][0] == 'reaction_id'
+
+# project id: must uniquely identify the project!
+project_id = 'EMT' + ' reaction energies'
+
+# if True, then results are uploaded to the database
+database = False
+
+# create assisting class for project with project_id,
+# that allows one to convert trajectory files into
+# db-files and perform analysis
+re = ASEReactionEnergy(project_id, reactions, prefix='', verbose=False)
+
+# compounds names
+compounds = re.get_compounds()
+
+# put additional fields here:
+cmr_params = {'calculator': 'EMT'}
+# convert all traj files in this directory to db-files
+re.create_db_files(cmr_params)
+
+# calculate the reaction energies and write the results to db-files
+# named 'reaction_id.index.db'
+# Each db-file defines a group (group of all compounds belonging to
+# the given reaction).
+
+# reaction energies on initial, unoptimized geometries
+cmr_params = {'geometries': 'initial'}
+re.make_reaction_groups(database=False, index=0, cmr_params=cmr_params)
+
+# print
+re.print_result(database=False)
+
+# reaction energies on final, optimized geometries
+cmr_params = {'geometries': 'final'}
+re.make_reaction_groups(database=False, index= -1, cmr_params=cmr_params)
+
+# print
+re.print_result(database=False)
+
+
+reader = DirectoryReader('.')
+
+# retrieve all reactions (groups) with project_id and optimized geometries from the current directory
+all = reader.find(name_value_list=[('db_calculator', 'group'),
+                                   ('geometries', 'final')
+                                  ],
+                  keyword_list=[project_id, 'reaction'])
+
+print 'reaction_id, calc, ref, calc - ref'
+# compare with the reference
+for r in reactions:
+    reaction_id = r[-1][1]
+    res = all.get('reaction_id', reaction_id)
+    if res is None:
+        print "Could not find reaction_id %s in reference"%str(reaction_id)
+    else:
+        calc = res['reaction_energy']
+        ref = reference[reaction_id]
+        print reaction_id, calc, ref, calc - ref
+        assert abs(calc - ref) < 1e-5
+
+
+
+# upload the created groups to the database
+if database:
+    re.upload_to_database()
diff --git a/ase/test/cmr/reactions_xsimple.py b/ase/test/cmr/reactions_xsimple.py
new file mode 100644
index 0000000..8b5028a
--- /dev/null
+++ b/ase/test/cmr/reactions_xsimple.py
@@ -0,0 +1,71 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from ase.calculators.emt import EMT
+
+from ase.structure import molecule
+
+from ase.io import write
+
+# project id: must uniquely identify the project!
+project_id = 'simple reaction energies'
+
+reaction = [('N2', -1), ('N', 2)]
+
+calculator = EMT()
+
+for (formula, coef) in reaction:
+    m = molecule(formula)
+    m.set_calculator(calculator)
+    m.get_potential_energy()
+    cmr_params = {
+        "db_keywords": [project_id],
+        # add project_id also as a field to support search across projects
+        "project_id": project_id,
+        "formula": formula,
+        "calculator": calculator.get_name(),
+        }
+    write(filename=('reactions_xsimple.%s.db' % formula),
+          images=m, format='db', cmr_params=cmr_params)
+
+# analyse the results with CMR
+
+from cmr.ui import DirectoryReader
+reader = DirectoryReader('.')
+
+# read all compounds in the project calculated with EMT
+all = reader.find(name_value_list=[('calculator', 'EMT')],
+                  keyword_list=[project_id])
+
+all.print_table(0, columns=["formula", "ase_potential_energy"])
+print
+
+group = cmr.create_group()
+group_vars = {"reaction":reaction, "output":"group.db"}
+sum = 0.0
+for (formula, coef) in reaction:
+	data = all.get("formula", formula)
+        if data is None:
+           print "%s is missing"%formula
+           sum = None
+           break
+        sum += coef*data["ase_potential_energy"]
+        group.add(data["db_hash"])
+
+group_vars["result"] = sum
+group.write(group_vars)
+print "Energy: ",sum
+group.dump()
+
+# clean
+for (formula, coef) in reaction:
+    filename=('reactions_xsimple.%s.db' % formula)
+    if os.path.exists(filename): os.unlink(filename)
+filename = "group.db"
+if os.path.exists(filename): os.unlink(filename)
diff --git a/ase/test/cmr/rw.py b/ase/test/cmr/rw.py
new file mode 100644
index 0000000..4358d32
--- /dev/null
+++ b/ase/test/cmr/rw.py
@@ -0,0 +1,44 @@
+import os
+
+import numpy as np
+
+def array_almost_equal(a1, a2, tol=np.finfo(type(1.0)).eps):
+    """Replacement for old numpy.testing.utils.array_almost_equal."""
+    return (np.abs(a1 - a2) < tol).all()
+
+from ase.test import NotAvailable
+# this test should be run with cmr!
+try:
+    import cmr
+except ImportError:
+    raise NotAvailable('CMR is required')
+
+from ase.calculators.emt import EMT
+
+from ase.io import read, write
+
+from ase.structure import molecule
+
+m1 = molecule('O2')
+m1.center(2.0)
+
+write("O2.db", images=m1)
+
+m1.set_calculator(EMT())
+e1 = m1.get_potential_energy()
+f1 = m1.get_forces()
+
+m2 = read("O2.db")
+
+m2.set_calculator(EMT())
+e2 = m2.get_potential_energy()
+f2 = m1.get_forces()
+
+# assume atoms definitions are the same if energy/forces are the same: can we do better?
+assert abs(e1-e2) < 1.e-6, str(e1) + ' ' + str(e2)
+assert array_almost_equal(f1, f2, tol=1.e-6)
+
+# clean
+filename = "O2.db"
+if os.path.exists(filename): os.unlink(filename)
+
diff --git a/ase/test/com.py b/ase/test/com.py
new file mode 100644
index 0000000..39232cf
--- /dev/null
+++ b/ase/test/com.py
@@ -0,0 +1,14 @@
+"""Test that atoms.get_center_of_mass(scaled=True) works"""
+
+import numpy as np
+from ase import Atoms
+
+d = 1.142
+a = Atoms('CO', positions=[(2, 0, 0), (2, -d, 0)], pbc=True)
+a.set_cell(np.array(((4, -4, 0), (0, 5.657, 0), (0, 0, 10))))
+
+def array_almost_equal(a1, a2, tol=np.finfo(type(1.0)).eps):
+    return (np.abs(a1 - a2) < tol).all()
+
+scaledref = np.array((0.5, 0.23823499, 0.))
+assert array_almost_equal(a.get_center_of_mass(scaled=True), scaledref, tol=1e-8)
diff --git a/ase/test/coverage.py b/ase/test/coverage.py
new file mode 100644
index 0000000..4aff8c9
--- /dev/null
+++ b/ase/test/coverage.py
@@ -0,0 +1,4 @@
+from ase import Atoms
+print Atoms()
+print Atoms('H2O')
+#...
diff --git a/ase/test/crystal.py b/ase/test/crystal.py
new file mode 100644
index 0000000..437430b
--- /dev/null
+++ b/ase/test/crystal.py
@@ -0,0 +1,63 @@
+import numpy as np
+
+from ase.lattice.spacegroup import crystal
+
+
+# A diamond unit cell
+diamond = crystal('C', [(0,0,0)], spacegroup=227, 
+                  cellpar=[3.57, 3.57, 3.57, 90, 90, 90])
+
+assert len(diamond) == 8
+correct_pos = np.array([[ 0.  ,  0.  ,  0.  ],
+                        [ 0.  ,  0.5 ,  0.5 ],
+                        [ 0.5 ,  0.5 ,  0.  ],
+                        [ 0.5 ,  0.  ,  0.5 ],
+                        [ 0.75,  0.25,  0.75],
+                        [ 0.25,  0.25,  0.25],
+                        [ 0.25,  0.75,  0.75],
+                        [ 0.75,  0.75,  0.25]])
+assert np.allclose(diamond.get_scaled_positions(), correct_pos)
+
+
+
+# A CoSb3 skutterudite unit cell containing 32 atoms
+skutterudite = crystal(('Co', 'Sb'), 
+    basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)], 
+    spacegroup=204, cellpar=[9.04, 9.04, 9.04, 90, 90, 90])
+
+assert len(skutterudite) == 32
+
+correct_pos = np.array([[ 0.25 ,  0.25 ,  0.25 ],
+                        [ 0.75 ,  0.75 ,  0.25 ],
+                        [ 0.75 ,  0.25 ,  0.75 ],
+                        [ 0.25 ,  0.75 ,  0.75 ],
+                        [ 0.75 ,  0.75 ,  0.75 ],
+                        [ 0.25 ,  0.25 ,  0.75 ],
+                        [ 0.25 ,  0.75 ,  0.25 ],
+                        [ 0.75 ,  0.25 ,  0.25 ],
+                        [ 0.   ,  0.335,  0.158],
+                        [ 0.   ,  0.665,  0.158],
+                        [ 0.   ,  0.335,  0.842],
+                        [ 0.   ,  0.665,  0.842],
+                        [ 0.158,  0.   ,  0.335],
+                        [ 0.158,  0.   ,  0.665],
+                        [ 0.842,  0.   ,  0.335],
+                        [ 0.842,  0.   ,  0.665],
+                        [ 0.335,  0.158,  0.   ],
+                        [ 0.665,  0.158,  0.   ],
+                        [ 0.335,  0.842,  0.   ],
+                        [ 0.665,  0.842,  0.   ],
+                        [ 0.5  ,  0.835,  0.658],
+                        [ 0.5  ,  0.165,  0.658],
+                        [ 0.5  ,  0.835,  0.342],
+                        [ 0.5  ,  0.165,  0.342],
+                        [ 0.658,  0.5  ,  0.835],
+                        [ 0.658,  0.5  ,  0.165],
+                        [ 0.342,  0.5  ,  0.835],
+                        [ 0.342,  0.5  ,  0.165],
+                        [ 0.835,  0.658,  0.5  ],
+                        [ 0.165,  0.658,  0.5  ],
+                        [ 0.835,  0.342,  0.5  ],
+                        [ 0.165,  0.342,  0.5  ]])
+
+assert np.allclose(skutterudite.get_scaled_positions(), correct_pos)
diff --git a/ase/test/dihedralconstraint.py b/ase/test/dihedralconstraint.py
new file mode 100644
index 0000000..00f3a96
--- /dev/null
+++ b/ase/test/dihedralconstraint.py
@@ -0,0 +1,52 @@
+from ase.structure import molecule
+from ase.calculators.emt import EMT
+from ase.constraints import FixInternals
+from ase.optimize.bfgs import BFGS
+
+system = molecule('CH3CH2OH')
+system.center(vacuum=5.0)
+system.rattle(stdev=0.3)
+
+indices = [6, 0, 1, 2]
+indices2 = [6, 0, 1]
+#system.set_dihedral(indices, pi/20, mask=[0,1,1,1,1,1,0,0,0])
+
+#Angles, Bonds, Dihedrals are built up with  pairs of constraint 
+#value and indices defining the constraint
+
+angle = [system.get_angle(indices2), indices2]
+dihedral = [system.get_dihedral(indices), indices] 
+
+constraint = FixInternals(system, bonds=[], angles=[angle], dihedrals=[dihedral])
+
+print constraint
+
+calc = EMT()
+
+opt = BFGS(system, trajectory='opt.traj', logfile='opt.log')
+
+previous_angle = system.get_angle(indices2)
+previous_dihedral = system.get_dihedral(indices)
+
+print 'angle before', previous_angle
+print 'dihedral before', previous_dihedral
+
+system.set_calculator(calc)
+system.set_constraint(constraint)
+print '-----Optimization-----'
+opt.run(fmax=0.01)
+
+new_angle = system.get_angle(indices2)
+new_dihedral = system.get_dihedral(indices)
+
+print 'angle after', new_angle
+print 'dihedral after', new_dihedral
+
+err1 = new_angle - previous_angle
+err2 = new_dihedral - previous_dihedral
+
+print 'error in angle', repr(err1)
+print 'error in dihedral', repr(err2)
+
+assert err1 < 1e-12
+assert err2 < 1e-12
diff --git a/ase/test/dimer.py b/ase/test/dimer.py
new file mode 100644
index 0000000..afc6cfc
--- /dev/null
+++ b/ase/test/dimer.py
@@ -0,0 +1,19 @@
+from ase import Atom, Atoms
+from ase.calculators.lj import LennardJones
+from ase.constraints import FixBondLength
+
+dimer = Atoms([Atom('X', (0, 0, 0)),
+               Atom('X', (0, 0, 1))],
+              calculator=LennardJones(),
+              constraint=FixBondLength(0, 1))
+print dimer.get_forces()
+print dimer.positions
+dimer.positions[:] += 0.1
+print dimer.positions
+dimer.positions[:, 2] += 5.1
+print dimer.positions
+dimer.positions[:] = [(1,2,3),(4,5,6)]
+print dimer.positions
+dimer.set_positions([(1,2,3),(4,5,6.2)])
+print dimer.positions
+
diff --git a/ase/test/dimer_method.py b/ase/test/dimer_method.py
new file mode 100755
index 0000000..9a37a2a
--- /dev/null
+++ b/ase/test/dimer_method.py
@@ -0,0 +1,39 @@
+from ase.lattice.surface import fcc100, add_adsorbate
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.dimer import DimerControl, MinModeAtoms, MinModeTranslate
+
+# Set up a small "slab" with an adatoms
+atoms = fcc100('Pt', size = (2, 2, 1), vacuum = 10.0)
+add_adsorbate(atoms, 'Pt', 1.611, 'hollow')
+
+# Freeze the "slab"
+mask = [atom.tag > 0 for atom in atoms]
+atoms.set_constraint(FixAtoms(mask = mask))
+
+# Calculate using EMT
+atoms.set_calculator(EMT())
+relaxed_energy = atoms.get_potential_energy()
+
+# Set up the dimer
+d_control = DimerControl(initial_eigenmode_method = 'displacement', \
+                         displacement_method = 'vector', logfile = None, \
+                         mask = [0, 0, 0, 0, 1])
+d_atoms = MinModeAtoms(atoms, d_control)
+
+# Dispalce the atoms
+displacement_vector = [[0.0]*3]*5
+displacement_vector[-1][1] = -0.1
+d_atoms.displace(displacement_vector = displacement_vector)
+
+# Converge to a saddle point
+dim_rlx = MinModeTranslate(d_atoms, trajectory = 'dimer_method.traj', \
+                           logfile = None)
+dim_rlx.run(fmax = 0.001)
+
+# Test the results
+tolerance = 1e-3
+assert(d_atoms.get_barrier_energy() - 1.03733136918 < tolerance)
+assert(abs(d_atoms.get_curvature() + 0.900467048707) < tolerance)
+assert(d_atoms.get_eigenmode()[-1][1] < -0.99)
+assert(abs(d_atoms.get_positions()[-1][1]) < tolerance)
diff --git a/ase/test/distmom.py b/ase/test/distmom.py
new file mode 100644
index 0000000..e855c17
--- /dev/null
+++ b/ase/test/distmom.py
@@ -0,0 +1,20 @@
+from ase.dft import get_distribution_moment
+import numpy as np
+
+precision = 1E-8
+
+x = np.linspace(-50., 50., 1000)
+y = np.exp(-x**2 / 2.)
+area, center, mom2 = get_distribution_moment(x, y, (0, 1, 2))
+assert sum((abs(area - np.sqrt(2. * np.pi)), abs(center), abs(mom2 - 1.))) < precision
+
+x = np.linspace(-1., 1., 100000)
+for order in range(0, 9):
+    y = x**order
+    area = get_distribution_moment(x, y)
+    assert abs(area - (1. - (-1.)**(order + 1)) / (order + 1.)) < precision
+
+x = np.linspace(-50., 50., 100)
+y = np.exp(-2. * (x - 7.)**2 / 10.) + np.exp(-2. * (x + 5.)**2 / 10.)
+center=get_distribution_moment(x, y, 1)
+assert abs(center - 1.) < precision 
diff --git a/ase/test/elk/elk_cmdline.py b/ase/test/elk/elk_cmdline.py
new file mode 100644
index 0000000..51544ce
--- /dev/null
+++ b/ase/test/elk/elk_cmdline.py
@@ -0,0 +1,17 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    elk_species_path = os.getenv('ELK_SPECIES_PATH')
+    if elk_species_path == None:
+        raise NotAvailable('ELK_SPECIES_PATH not defined')
+except NotAvailable:
+    raise NotAvailable('ELK required')
+
+import numpy as np
+
+from ase.tasks.main import run
+
+atoms, task = run("elk bulk Al -x fcc -a 4.04 --k-point-density=3.0 -p xc='PBE',rgkmax=5.0,tforce=True")
+atoms, task = run('elk bulk Al -s')
diff --git a/ase/test/emt.py b/ase/test/emt.py
new file mode 100644
index 0000000..60e664c
--- /dev/null
+++ b/ase/test/emt.py
@@ -0,0 +1,64 @@
+import numpy as np
+from ase.calculators.emt import EMT
+from ase import Atoms
+
+a = 3.60
+b = a / 2
+cu = Atoms('Cu',
+           positions=[(0, 0, 0)],
+           cell=[(0, b, b),
+                 (b, 0, b),
+                 (b, b, 0)],
+           pbc=1,
+           calculator=EMT())
+e0 = cu.get_potential_energy()
+print e0
+
+cu.set_cell(cu.get_cell() * 1.001, scale_atoms=True)
+e1 = cu.get_potential_energy()
+V = a**3 / 4
+B = 2 * (e1 - e0) / 0.003**2 / V * 160.2
+print B
+
+for i in range(4):
+    x = 0.001 * i
+    A = np.array([(x, b, b+x),
+                  (b, 0, b),
+                  (b, b, 0)])
+    cu.set_cell(A, scale_atoms=True)
+    e = cu.get_potential_energy() - e0
+    if i == 0:
+        print i, e
+    else:
+        print i, e, e / x**2
+
+A = np.array([(0, b, b),
+              (b, 0, b),
+              (6*b, 6*b, 0)])
+R = np.zeros((2, 3))
+for i in range(1, 2):
+    R[i] = i * A[2] / 6
+print (Atoms('Cu2', positions=R,
+             pbc=1, cell=A,
+             calculator=EMT()).get_potential_energy() - 2 * e0) / 2
+
+A = np.array([(0, b, b),
+              (b, 0, b),
+              (10*b, 10*b, 0)])
+R = np.zeros((3, 3))
+for i in range(1, 3):
+    R[i] = i * A[2] / 10
+print (Atoms('Cu3', positions=R,
+             pbc=1, cell=A,
+             calculator=EMT()).get_potential_energy() - 3 * e0) / 2
+
+A = np.array([(0, b, b),
+              (b, 0, b),
+              (b, b, 0)])
+R = np.zeros((3, 3))
+for i in range(1, 3):
+    R[i] = i * A[2]
+print (Atoms('Cu3', positions=R,
+             pbc=(1, 1, 0), cell=A,
+             calculator=EMT()).get_potential_energy() - 3 * e0) / 2
+
diff --git a/ase/test/emt1.py b/ase/test/emt1.py
new file mode 100644
index 0000000..da38197
--- /dev/null
+++ b/ase/test/emt1.py
@@ -0,0 +1,25 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.constraints import FixBondLength
+from ase.io import PickleTrajectory
+from ase.optimize import BFGS
+
+a = 3.6
+b = a / 2
+cu = Atoms('Cu2Ag',
+           positions=[(0, 0, 0),
+                      (b, b, 0),
+                      (a, a, b)],
+           calculator=EMT())
+e0 = cu.get_potential_energy()
+print e0
+
+d0 = cu.get_distance(0, 1)
+cu.set_constraint(FixBondLength(0, 1))
+t = PickleTrajectory('cu2ag.traj', 'w', cu)
+qn = BFGS(cu)
+qn.attach(t.write)
+def f(): print cu.get_distance(0,1)
+qn.attach(f)
+qn.run(fmax=0.01)
+assert abs(cu.get_distance(0, 1) - d0) < 1e-14
diff --git a/ase/test/emt2.py b/ase/test/emt2.py
new file mode 100644
index 0000000..fad16a0
--- /dev/null
+++ b/ase/test/emt2.py
@@ -0,0 +1,13 @@
+from ase.calculators.emt import EMT
+from ase import Atoms
+from ase.structure import molecule
+a1 = Atoms('Au', calculator=EMT())
+e1 = a1.get_potential_energy()
+a2 = molecule('C6H6', calculator=EMT())
+e2 = a2.get_potential_energy()
+a1.translate((0, 0, 50))
+a3 = a1 + a2
+a3.calc = EMT()
+e3 = a3.get_potential_energy()
+print e1, e2, e3, e3 - e1 - e2
+assert abs(e3 - e1 - e2) < 1e-13
diff --git a/ase/test/example.py b/ase/test/example.py
new file mode 100644
index 0000000..feb9b93
--- /dev/null
+++ b/ase/test/example.py
@@ -0,0 +1,27 @@
+from ase.all import *
+
+atoms = Atoms('H7',
+              positions=[(0, 0, 0),
+                         (1, 0, 0),
+                         (0, 1, 0),
+                         (1, 1, 0),
+                         (0, 2, 0),
+                         (1, 2, 0),
+                         (0.5, 0.5, 1)],
+              constraint=[FixAtoms(range(6))],
+              calculator=LennardJones())
+
+traj = PickleTrajectory('H.traj', 'w', atoms)
+dyn = QuasiNewton(atoms, maxstep=0.2)
+dyn.attach(traj.write)
+dyn.run(fmax=0.01, steps=100)
+
+print atoms
+del atoms[-1]
+print atoms
+del atoms[5]
+print atoms
+assert len(atoms.constraints[0].index) == 5
+
+
+
diff --git a/ase/test/exciting/exciting.py b/ase/test/exciting/exciting.py
new file mode 100644
index 0000000..849ae38
--- /dev/null
+++ b/ase/test/exciting/exciting.py
@@ -0,0 +1,31 @@
+import os
+from ase import Atoms
+from ase.io import read, write
+from ase.calculators.exciting import Exciting
+from ase.units import Bohr, Hartree
+from ase.test import NotAvailable
+
+try:
+    import lxml
+except ImportError:
+    raise NotAvailable('This test need lxml module.')
+
+a = Atoms('N3O',
+          [(0, 0, 0), (1, 0, 0), (0, 0, 1), (0.5, 0.5, 0.5)],
+          pbc=True)
+
+raise NotAvailable('Problem with lxml module.')
+
+write('geo.exi', a)
+b = read('geo.exi')
+
+print a
+print a.get_positions()
+print b
+print b.get_positions()
+
+calculator = Exciting(dir='excitingtestfiles',
+                      kpts=(4, 4, 3),
+                      maxscl=3,
+                      #bin='/fshome/chm/git/exciting/bin/excitingser'
+                      )
diff --git a/ase/test/fio/abinit.py b/ase/test/fio/abinit.py
new file mode 100644
index 0000000..e691215
--- /dev/null
+++ b/ase/test/fio/abinit.py
@@ -0,0 +1,31 @@
+import numpy as np
+
+def array_almost_equal(a1, a2, tol=np.finfo(type(1.0)).eps):
+    """Replacement for old numpy.testing.utils.array_almost_equal."""
+    return (np.abs(a1 - a2) < tol).all()
+
+# this test should be run with abinit!
+from ase.calculators.emt import EMT
+
+from ase.io import read, write
+
+from ase.structure import molecule
+
+m1 = molecule('O2')
+m1.center(2.0)
+
+write('abinit_save.in', images=m1, format='abinit')
+
+m1.set_calculator(EMT())
+e1 = m1.get_potential_energy()
+f1 = m1.get_forces()
+
+m2 = read('abinit_save.in', format='abinit')
+
+m2.set_calculator(EMT())
+e2 = m2.get_potential_energy()
+f2 = m1.get_forces()
+
+# assume atoms definitions are the same if energy/forces are the same: can we do better?
+assert abs(e1-e2) < 1.e-6, str(e1) + ' ' + str(e2)
+assert array_almost_equal(f1, f2, tol=1.e-6)
diff --git a/ase/test/fio/info.py b/ase/test/fio/info.py
new file mode 100644
index 0000000..8c5424c
--- /dev/null
+++ b/ase/test/fio/info.py
@@ -0,0 +1,37 @@
+from ase import Atoms
+from ase.io import PickleTrajectory
+
+class Foo(object):
+    def __init__(self, value):
+        self.value = value
+    def __cmp__(self, other):
+        return int(self.value - other.value)
+
+
+if __name__ == '__main__':
+    import info  # import ourselves to make info.Foo reachable
+
+    # Create a molecule with an info attribute
+    info = dict(creation_date='2011-06-27', 
+                chemical_name='Hydrogen',
+                # custom classes also works provided that it is
+                # imported and pickleable...
+                foo=info.Foo(7),  
+                )
+    molecule = Atoms('H2', positions=[(0., 0., 0.), (0., 0., 1.1)], info=info)
+    assert molecule.info == info
+
+    # Copy molecule
+    atoms = molecule.copy()
+    assert atoms.info == info
+
+    # Save molecule to trajectory
+    traj = PickleTrajectory('info.traj', 'w', atoms=molecule)
+    traj.write()
+    del traj
+
+    # Load molecule from trajectory 
+    t = PickleTrajectory('info.traj')
+    atoms = t[-1]
+    assert atoms.info == info
+
diff --git a/ase/test/fio/magmom.py b/ase/test/fio/magmom.py
new file mode 100644
index 0000000..ca16c45
--- /dev/null
+++ b/ase/test/fio/magmom.py
@@ -0,0 +1,9 @@
+from ase import Atoms
+from ase.io import read, write
+
+atoms = Atoms('HH', [[.0,.0,.0], [.0,.0,.74]], pbc=True, cell=[5, 5, 5])
+atoms.set_initial_magnetic_moments([1, -1])
+moms = atoms.get_initial_magnetic_moments()
+write('test.traj',atoms)
+atoms = read('test.traj')
+assert (atoms.get_initial_magnetic_moments() == moms).all()
diff --git a/ase/test/fio/netcdf.py b/ase/test/fio/netcdf.py
new file mode 100644
index 0000000..af0c6b0
--- /dev/null
+++ b/ase/test/fio/netcdf.py
@@ -0,0 +1,23 @@
+import numpy as np
+from ase.io.pupynere import NetCDFFile
+
+# Write array
+a1 = np.random.rand(5, 5)
+a2 = a1 * 2 - 5
+nc = NetCDFFile('test.nc', 'w')
+nc.createDimension('dimx', a1.shape[0])
+nc.createDimension('dimy', a1.shape[1])
+nc.createVariable('matrix1', 'd', ('dimx', 'dimy'))[:] = a1
+nc.createVariable('matrix2', 'd', ('dimx', 'dimy'))[:] = a2
+nc.sync()
+nc.close()
+
+# Read array
+nc = NetCDFFile('test.nc', 'r')
+b1 = nc.variables['matrix1'][:]
+b2 = nc.variables['matrix2'][:]
+
+assert np.all(a1 == b1) and np.all(a2 == b2)
+
+import os
+os.remove('test.nc')
diff --git a/ase/test/fio/oi.py b/ase/test/fio/oi.py
new file mode 100644
index 0000000..70aeca4
--- /dev/null
+++ b/ase/test/fio/oi.py
@@ -0,0 +1,63 @@
+import numpy as np
+from ase import Atoms
+from ase.io import write, read
+from ase.test import NotAvailable
+
+a = 5.0
+d = 1.9
+c = a / 2
+atoms = Atoms('AuH',
+              positions=[(c, c, 0), (c, c, d)],
+              cell=(a, a, 2 * d),
+              pbc=(0, 0, 1))
+extra = np.array([ 2.3, 4.2 ])
+atoms.set_array('extra', extra)
+atoms *= (1, 1, 2)
+images = [atoms.copy(), atoms.copy()]
+r = ['xyz', 'traj', 'cube', 'pdb', 'cfg', 'struct', 'cif', 'gen']
+try:
+    import Scientific
+    version = Scientific.__version__.split('.')
+    print 'Found ScientificPython version: ',Scientific.__version__
+    if map(int,version) < [2,8]:
+        print 'ScientificPython 2.8 or greater required for numpy support in NetCDF'
+        #raise NotAvailable('ScientificPython version 2.8 or greater is required')
+except (ImportError, NotAvailable):
+    print 'No Scientific python found. Check your PYTHONPATH'
+    #raise NotAvailable('ScientificPython version 2.8 or greater is required')
+else:
+    r += ['etsf']
+w = r + ['xsf', 'findsym']
+try:
+    import matplotlib
+except ImportError:
+    pass
+else:
+    w += ['png', 'eps']
+
+for format in w:
+    print format, 'O',
+    fname1 = 'io-test.1.' + format
+    fname2 = 'io-test.2.' + format
+    write(fname1, atoms, format=format)
+    if format not in ['cube', 'png', 'eps', 'cfg', 'struct', 'etsf', 'gen']:
+        write(fname2, images, format=format)
+
+    if format in r:
+        print 'I'
+        a1 = read(fname1)
+        assert np.all(np.abs(a1.get_positions() -
+                             atoms.get_positions()) < 1e-6)
+        if format in ['traj', 'cube', 'cfg', 'struct', 'gen']:
+            assert np.all(np.abs(a1.get_cell() - atoms.get_cell()) < 1e-6)
+        if format in ['cfg']:
+            assert np.all(np.abs(a1.get_array('extra') -
+                                 atoms.get_array('extra')) < 1e-6)
+        if format not in ['cube', 'png', 'eps', 'cfg', 'struct', 'etsf',
+                          'gen']:
+            a2 = read(fname2)
+            a3 = read(fname2, index=0)
+            a4 = read(fname2, index=slice(None))
+            assert len(a4) == 2
+    else:
+        print
diff --git a/ase/test/fio/oldtraj.py b/ase/test/fio/oldtraj.py
new file mode 100644
index 0000000..230a2e8
--- /dev/null
+++ b/ase/test/fio/oldtraj.py
@@ -0,0 +1,70 @@
+"""Check that we can read old version 1 PickleTrajectories."""
+import cPickle as pickle
+from StringIO import StringIO
+
+import numpy as np
+
+from ase.io.trajectory import PickleTrajectory
+from ase.constraints import FixAtoms
+from ase import Atoms
+
+
+a = Atoms('FOO')
+
+def v1(a):
+    """Create old version-1 trajectory."""
+    fd = StringIO()
+    fd.write('PickleTrajectory')
+    d = {'pbc': a.pbc,
+         'numbers': a.numbers,
+         'tags': None,
+         'masses': None,
+         'constraints': a.constraints}
+    pickle.dump(d, fd, protocol=-1)
+    d = {'positions': a.positions,
+         'cell': a.cell,
+         'momenta': None}
+    pickle.dump(d, fd, protocol=-1)
+    return StringIO(fd.getvalue())
+
+
+def v2(a):
+    """Create new version-2 trajectory."""
+    fd = StringIO()
+    t = PickleTrajectory(fd, 'w')
+    t.write(a)
+    return StringIO(fd.getvalue())
+
+
+class MyFixAtoms(FixAtoms):
+    pass
+
+
+a.constraints = FixAtoms(indices=[2])
+t1 = v1(a)
+t2 = v2(a)
+
+# Read old trajectory:
+c1 = PickleTrajectory(t1)[0].constraints
+assert c1[0].index[0] == 2
+
+# Read new trajectory:
+c2 = PickleTrajectory(t2)[0].constraints
+assert c2[0].index[0] == 2
+
+a.constraints = MyFixAtoms(indices=[1])
+t3 = v2(a)
+
+# Read new trajectory with missing constraint class.  This should
+# ignore the constraint and issue a warning:
+del MyFixAtoms, a
+import warnings
+warnings.filterwarnings('error')
+try:
+    c3 = PickleTrajectory(t3)[0].constraints
+except UserWarning:
+    pass
+else:
+    assert False
+
+#assert len(c3) == 0
diff --git a/ase/test/fio/trajectory.py b/ase/test/fio/trajectory.py
new file mode 100644
index 0000000..1d08c78
--- /dev/null
+++ b/ase/test/fio/trajectory.py
@@ -0,0 +1,65 @@
+import os
+from ase import Atom, Atoms
+from ase.io import PickleTrajectory
+
+co = Atoms([Atom('C', (0, 0, 0)),
+            Atom('O', (0, 0, 1.2))])
+traj = PickleTrajectory('1.traj', 'w', co)
+for i in range(5):
+    co.positions[:, 2] += 0.1
+    traj.write()
+del traj
+traj = PickleTrajectory('1.traj', 'a')
+co = traj[-1]
+print co.positions
+co.positions[:] += 1
+traj.write(co)
+del traj
+t = PickleTrajectory('1.traj', 'a')
+print t[-1].positions
+print '.--------'
+for a in t:
+    print 1, a.positions[-1,2]
+co.positions[:] += 1
+t.write(co)
+for a in t:
+    print 2, a.positions[-1,2]
+assert len(t) == 7
+
+co[0].number = 1
+try:
+    t.write(co)
+except ValueError:
+    pass
+else:
+    assert False
+
+co[0].number = 6
+co.pbc = True
+try:
+    t.write(co)
+except ValueError:
+    pass
+else:
+    assert False
+
+co.pbc = False
+o = co.pop(1)
+try:
+    t.write(co)
+except ValueError:
+    pass
+else:
+    assert False
+
+co.append(o)
+t.write(co)
+
+# append to a nonexisting file
+fname = '2.traj'
+if os.path.isfile(fname):
+    os.remove(fname)
+t = PickleTrajectory(fname, 'a', co)
+del t
+os.remove(fname)
+
diff --git a/ase/test/fio/v_sim.py b/ase/test/fio/v_sim.py
new file mode 100644
index 0000000..74f812d
--- /dev/null
+++ b/ase/test/fio/v_sim.py
@@ -0,0 +1,17 @@
+import urllib2
+import urllib
+
+from ase.test import NotAvailable
+
+dest = 'demo.ascii'
+src = 'http://inac.cea.fr/L_Sim/V_Sim/files/' + dest
+
+try:
+    e = urllib2.urlopen(src)
+    urllib.urlretrieve(src, filename=dest)
+except urllib2.URLError:
+    raise NotAvailable('Retrieval of ' + src + ' failed')
+
+from ase.io import read
+
+a = read(dest, format='v_sim')
diff --git a/ase/test/fleur/fleur_cmdline.py b/ase/test/fleur/fleur_cmdline.py
new file mode 100644
index 0000000..904c8ee
--- /dev/null
+++ b/ase/test/fleur/fleur_cmdline.py
@@ -0,0 +1,17 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    fleur = os.getenv('FLEUR')
+    if fleur == None:
+        raise NotAvailable('FLEUR not defined')
+except NotAvailable:
+    raise NotAvailable('Fleur required')
+
+import numpy as np
+
+from ase.tasks.main import run
+
+atoms, task = run("fleur bulk Al -x fcc -a 4.04 --k-point-density=3.0 -p xc=PBE")
+atoms, task = run('fleur bulk Al -s')
diff --git a/ase/test/geometry.py b/ase/test/geometry.py
new file mode 100644
index 0000000..e03eeac
--- /dev/null
+++ b/ase/test/geometry.py
@@ -0,0 +1,99 @@
+"Test the ase.utils.geometry module"
+
+import numpy as np
+
+from ase.lattice.spacegroup import crystal
+from ase.utils.geometry import get_layers, cut, stack
+
+np.set_printoptions(suppress=True)
+
+al = crystal('Al', [(0,0,0)], spacegroup=225, cellpar=4.05)
+
+
+# Cut out slab of 5 Al(001) layers
+al001 = cut(al, nlayers=5)
+correct_pos = np.array([[ 0. ,  0. ,  0. ],
+                        [ 0. ,  0.5,  0.2],
+                        [ 0.5,  0. ,  0.2],
+                        [ 0.5,  0.5,  0. ],
+                        [ 0. ,  0. ,  0.4],
+                        [ 0. ,  0.5,  0.6],
+                        [ 0.5,  0. ,  0.6],
+                        [ 0.5,  0.5,  0.4],
+                        [ 0. ,  0. ,  0.8],
+                        [ 0.5,  0.5,  0.8]])
+assert np.allclose(correct_pos, al001.get_scaled_positions())
+
+# Check layers along 001
+tags, levels = get_layers(al001, (0, 0, 1))
+assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4])
+assert np.allclose(levels, [ 0., 2.025, 4.05, 6.075, 8.1])
+
+# Check layers along 101
+tags, levels = get_layers(al001, (1, 0, 1))
+assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9])
+assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256,
+                            2.632, 3.008, 3.384, 4.136, 4.888], atol=0.001)
+
+# Check layers along 111
+tags, levels = get_layers(al001, (1, 1, 1))
+assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7])
+assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 
+                            2.756, 3.031, 3.858, 4.960], atol=0.001)
+
+
+# Cut out slab of three Al(111) layers
+al111 = cut(al, (1,-1,0), (0,1,-1), nlayers=3)
+correct_pos = np.array([[ 0.5       ,  0.        ,  0.        ],
+                        [ 0.        ,  0.5       ,  0.        ],
+                        [ 0.5       ,  0.5       ,  0.        ],
+                        [ 0.        ,  0.        ,  0.        ],
+                        [ 1/6.      ,  1/3.      ,  1/3.      ],
+                        [ 1/6.      ,  5/6.      ,  1/3.      ],
+                        [ 2/3.      ,  5/6.      ,  1/3.      ],
+                        [ 2/3.      ,  1/3.      ,  1/3.      ],
+                        [ 1/3.      ,  1/6.      ,  2/3.      ],
+                        [ 5/6.      ,  1/6.      ,  2/3.      ],
+                        [ 5/6.      ,  2/3.      ,  2/3.      ],
+                        [ 1/3.      ,  2/3.      ,  2/3.      ]])
+assert np.allclose(correct_pos, al111.get_scaled_positions())
+
+# Cut out cell including all corner and edge atoms (non-periodic structure)
+al = cut(al, extend=1.1)
+correct_pos = np.array([[ 0.   ,  0.   ,  0.   ],
+                        [ 0.   ,  2.025,  2.025],
+                        [ 2.025,  0.   ,  2.025],
+                        [ 2.025,  2.025,  0.   ],
+                        [ 0.   ,  0.   ,  4.05 ],
+                        [ 2.025,  2.025,  4.05 ],
+                        [ 0.   ,  4.05 ,  0.   ],
+                        [ 2.025,  4.05 ,  2.025],
+                        [ 0.   ,  4.05 ,  4.05 ],
+                        [ 4.05 ,  0.   ,  0.   ],
+                        [ 4.05 ,  2.025,  2.025],
+                        [ 4.05 ,  0.   ,  4.05 ],
+                        [ 4.05 ,  4.05 ,  0.   ],
+                        [ 4.05 ,  4.05 ,  4.05 ]])
+assert np.allclose(correct_pos, al.positions)
+
+# Create an Ag(111)/Si(111) interface
+ag = crystal(['Ag'], basis=[(0,0,0)], spacegroup=225, cellpar=4.09)
+si = crystal(['Si'], basis=[(0,0,0)], spacegroup=227, cellpar=5.43)
+
+ag111 = cut(ag, a=(4, -4, 0), b=(4, 4, -8), nlayers=5)
+si111 = cut(si, a=(3, -3, 0), b=(3, 3, -6), nlayers=5)
+#
+#interface = stack(ag111, si111)
+#assert len(interface) == 1000
+#assert np.allclose(interface.positions[::100],
+#                   [[  4.08125   ,  -2.040625  ,   -2.040625  ],
+#                    [  8.1625    ,   6.121875  ,  -14.284375  ],
+#                    [ 10.211875  ,   0.00875   ,    2.049375  ],
+#                    [ 24.49041667,  -4.07833333,  -16.32208333],
+#                    [ 18.37145833,  14.29020833,  -24.48166667],
+#                    [ 24.49916667,  12.25541667,  -20.39458333],
+#                    [ 18.36854167,  16.32791667,  -30.60645833],
+#                    [ 19.0575    ,   0.01166667,    5.45333333],
+#                    [ 23.13388889,   6.80888889,    1.36722222],
+#                    [ 35.3825    ,   5.45333333,  -16.31333333]])
+#
diff --git a/ase/test/h2.py b/ase/test/h2.py
new file mode 100644
index 0000000..3d6e13a
--- /dev/null
+++ b/ase/test/h2.py
@@ -0,0 +1,8 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+
+h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1.1)],
+           calculator=EMT())
+print h2.calc.get_numeric_forces(h2)
+print h2.get_forces()
+
diff --git a/ase/test/hcp.py b/ase/test/hcp.py
new file mode 100644
index 0000000..e704879
--- /dev/null
+++ b/ase/test/hcp.py
@@ -0,0 +1,36 @@
+try:
+    import scipy
+except ImportError:
+    from ase.test import NotAvailable
+    raise NotAvailable('This test needs scipy module.')
+
+import numpy as np
+from ase.io import read, PickleTrajectory
+from ase.lattice import bulk
+from ase.calculators.emt import EMT
+
+a0 = 3.52 / np.sqrt(2)
+c0 = np.sqrt(8 / 3.0) * a0
+print '%.4f %.3f' % (a0, c0 / a0)
+for i in range(3):
+    traj = PickleTrajectory('Ni.traj', 'w')
+    eps = 0.01
+    for a in a0 * np.linspace(1 - eps, 1 + eps, 4):
+        for c in c0 * np.linspace(1 - eps, 1 + eps, 4):
+            ni = bulk('Ni', 'hcp', a=a, covera=c / a)
+            ni.set_calculator(EMT())
+            ni.get_potential_energy()
+            traj.write(ni)
+
+    configs = read('Ni.traj@:')
+    energies = [config.get_potential_energy() for config in configs]
+    ac = [(config.cell[0, 0], config.cell[2, 2]) for config in configs]
+    from ase.optimize import polyfit
+    p = polyfit(ac, energies)
+    from scipy.optimize import fmin_bfgs
+    a0, c0 = fmin_bfgs(p, (a0, c0))
+    print '%.4f %.3f' % (a0, c0 / a0)
+assert abs(a0 - 2.466) < 0.001
+assert abs(c0 / a0 - 1.632) < 0.005
+
+
diff --git a/ase/test/jacapo/jacapo.py b/ase/test/jacapo/jacapo.py
new file mode 100644
index 0000000..1af4dfb
--- /dev/null
+++ b/ase/test/jacapo/jacapo.py
@@ -0,0 +1,40 @@
+# do some tests here before we import
+# Right version of Scientific?
+from ase.test import NotAvailable
+import os
+try:
+    import Scientific
+    version = Scientific.__version__.split(".")
+    print 'Found ScientificPython version: ',Scientific.__version__
+    if map(int,version) < [2,8]:
+        print 'ScientificPython 2.8 or greater required for numpy support in NetCDF'
+        raise NotAvailable('ScientificPython version 2.8 or greater is required')
+except (ImportError, NotAvailable):
+    print "No Scientific python found. Check your PYTHONPATH"
+    raise NotAvailable('ScientificPython version 2.8 or greater is required')
+
+if not (os.system('which dacapo.run') == 0):
+    print "No Dacapo Fortran executable (dacapo.run) found. Check your path settings."
+    raise NotAvailable('dacapo.run is not installed on this machine or not in the path')
+
+# Now Scientific 2.8 and dacapo.run should both be available
+
+from ase import Atoms, Atom
+from ase.calculators.jacapo import Jacapo
+
+atoms = Atoms([Atom('H',[0,0,0])],
+            cell=(2,2,2))
+
+calc = Jacapo('Jacapo-test.nc',
+              pw=200,
+              nbands=2,
+              kpts=(1,1,1),
+              spinpol=False,
+              dipole=False,
+              symmetry=False,
+              ft=0.01)
+
+atoms.set_calculator(calc)
+
+print atoms.get_potential_energy()
+os.system('rm -f Jacapo-test.nc Jacapo-test.txt')
diff --git a/ase/test/maxwellboltzmann.py b/ase/test/maxwellboltzmann.py
new file mode 100644
index 0000000..933f792
--- /dev/null
+++ b/ase/test/maxwellboltzmann.py
@@ -0,0 +1,10 @@
+from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
+from ase.lattice.cubic import FaceCenteredCubic
+
+atoms = FaceCenteredCubic(size=(50,50,50), symbol="Cu", pbc=False)
+print "Number of atoms:", len(atoms)
+MaxwellBoltzmannDistribution(atoms, 0.1)
+temp = atoms.get_kinetic_energy() / (1.5 * len(atoms))
+
+print "Temperature", temp, " (should be 0.1)"
+assert abs(temp - 0.1) < 1e-3
diff --git a/ase/test/md.py b/ase/test/md.py
new file mode 100644
index 0000000..c2d9ab2
--- /dev/null
+++ b/ase/test/md.py
@@ -0,0 +1,21 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.md import VelocityVerlet
+from ase.io import PickleTrajectory
+
+a = 3.6
+b = a / 2
+fcc = Atoms('Cu', positions=[(0, 0, 0)],
+            cell=[(0, b, b), (b, 0, b), (b, b, 0)],
+            pbc=1)
+fcc *= (2, 1, 1)
+fcc.set_calculator(EMT())
+fcc.set_momenta([(0.9, 0.0, 0.0), (-0.9, 0, 0)])
+md = VelocityVerlet(fcc, dt=0.1)
+def f():
+    print fcc.get_potential_energy(), fcc.get_total_energy()
+md.attach(f)
+md.attach(PickleTrajectory('Cu2.traj', 'w', fcc).write, interval=3)
+md.run(steps=20)
+fcc2 = PickleTrajectory('Cu2.traj', 'r')[-1]
+
diff --git a/ase/test/n2.py b/ase/test/n2.py
new file mode 100644
index 0000000..2d2a9ac
--- /dev/null
+++ b/ase/test/n2.py
@@ -0,0 +1,8 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+
+n2 = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)],
+           calculator=EMT())
+QuasiNewton(n2).run(0.01)
+print n2.get_distance(0, 1), n2.get_potential_energy()
diff --git a/ase/test/neb.py b/ase/test/neb.py
new file mode 100644
index 0000000..7949434
--- /dev/null
+++ b/ase/test/neb.py
@@ -0,0 +1,61 @@
+import threading
+
+from ase.test import World
+from ase.io import PickleTrajectory
+from ase.neb import NEB
+from ase.calculators.lj import LennardJones as Calculator
+from ase.optimize import BFGS
+
+fmax = 0.05
+
+nimages = 3
+
+print [a.get_potential_energy() for a in PickleTrajectory('H.traj')]
+images = [PickleTrajectory('H.traj')[-1]]
+for i in range(nimages):
+    images.append(images[0].copy())
+images[-1].positions[6, 1] = 2 - images[0].positions[6, 1]
+neb = NEB(images)
+neb.interpolate()
+
+for image in images[1:]:
+    image.set_calculator(Calculator())
+
+dyn = BFGS(neb, trajectory='mep.traj')
+
+dyn.run(fmax=fmax)
+
+for a in neb.images:
+    print a.positions[-1], a.get_potential_energy()
+
+results = [images[2].get_potential_energy()]
+
+def run_neb_calculation(cpu):
+    images = [PickleTrajectory('H.traj')[-1]]
+    for i in range(nimages):
+        images.append(images[0].copy())
+    images[-1].positions[6, 1] = 2 - images[0].positions[6, 1]
+    neb = NEB(images, parallel=True, world=cpu)
+    neb.interpolate()
+
+    images[cpu.rank + 1].set_calculator(Calculator())
+
+    dyn = BFGS(neb)
+    dyn.run(fmax=fmax)
+
+    if cpu.rank == 1:
+        results.append(images[2].get_potential_energy())
+    
+w = World(nimages - 1)
+ranks = [w.get_rank(r) for r in range(w.size)]
+threads = [threading.Thread(target=run_neb_calculation, args=(rank,))
+           for rank in ranks]
+for t in threads:
+    t.start()
+for t in threads:
+    t.join()
+
+print results
+assert abs(results[0] - results[1]) < 1e-13
+
+
diff --git a/ase/test/neighbor.py b/ase/test/neighbor.py
new file mode 100644
index 0000000..d348bbe
--- /dev/null
+++ b/ase/test/neighbor.py
@@ -0,0 +1,75 @@
+import numpy.random as random
+import numpy as np
+from ase import Atoms
+from ase.calculators.neighborlist import NeighborList
+from ase.lattice import bulk
+
+atoms = Atoms(numbers=range(10),
+              cell=[(0.2, 1.2, 1.4),
+                    (1.4, 0.1, 1.6),
+                    (1.3, 2.0, -0.1)])
+atoms.set_scaled_positions(3 * random.random((10, 3)) - 1)
+
+def count(nl, atoms):
+    c = np.zeros(len(atoms), int)
+    R = atoms.get_positions()
+    cell = atoms.get_cell()
+    d = 0.0
+    for a in range(len(atoms)):
+        i, offsets = nl.get_neighbors(a)
+        for j in i:
+            c[j] += 1
+        c[a] += len(i)
+        d += (((R[i] + np.dot(offsets, cell) - R[a])**2).sum(1)**0.5).sum()
+    return d, c
+
+for sorted in [False, True]:
+    for p1 in range(2):
+        for p2 in range(2):
+            for p3 in range(2):
+                print p1, p2, p3
+                atoms.set_pbc((p1, p2, p3))
+                nl = NeighborList(atoms.numbers * 0.2 + 0.5,
+                                  skin=0.0, sorted=sorted)
+                nl.update(atoms)
+                d, c = count(nl, atoms)
+                atoms2 = atoms.repeat((p1 + 1, p2 + 1, p3 + 1))
+                nl2 = NeighborList(atoms2.numbers * 0.2 + 0.5,
+                                   skin=0.0, sorted=sorted)
+                nl2.update(atoms2)
+                d2, c2 = count(nl2, atoms2)
+                c2.shape = (-1, 10)
+                dd = d * (p1 + 1) * (p2 + 1) * (p3 + 1) - d2
+                print dd
+                print c2 - c
+                assert abs(dd) < 1e-10
+                assert not (c2 - c).any()
+
+h2 = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1)])
+nl = NeighborList([0.5, 0.5], skin=0.1, sorted=True, self_interaction=False)
+assert nl.update(h2)
+assert not nl.update(h2)
+assert (nl.get_neighbors(0)[0] == [1]).all()
+
+h2[1].z += 0.09
+assert not nl.update(h2)
+assert (nl.get_neighbors(0)[0] == [1]).all()
+
+h2[1].z += 0.09
+assert nl.update(h2)
+assert (nl.get_neighbors(0)[0] == []).all()
+assert nl.nupdates == 2
+
+x = bulk('X', 'fcc', a=2**0.5)
+print x
+
+nl = NeighborList([0.5], skin=0.01, bothways=True, self_interaction=False)
+nl.update(x)
+assert len(nl.get_neighbors(0)[0]) == 12
+
+nl = NeighborList([0.5] * 27, skin=0.01, bothways=True, self_interaction=False)
+nl.update(x * (3, 3, 3))
+for a in range(27):
+    assert len(nl.get_neighbors(a)[0]) == 12
+assert not np.any(nl.get_neighbors(13)[1])
+
diff --git a/ase/test/noncollinear.py b/ase/test/noncollinear.py
new file mode 100644
index 0000000..f01f74e
--- /dev/null
+++ b/ase/test/noncollinear.py
@@ -0,0 +1,21 @@
+from ase import Atom, Atoms
+a = Atoms('H2')
+
+a[0].magmom = 1
+m = a.get_initial_magnetic_moments()
+assert m.shape == (2,) and (m == [1, 0]).all()
+
+a[1].magmom = -1
+m = a.get_initial_magnetic_moments()
+assert m.shape == (2,) and (m == [1, -1]).all()
+assert a[1].magmom == -1
+
+a.set_initial_magnetic_moments()
+a[0].magmom = (0, 1, 0)
+m = a.get_initial_magnetic_moments()
+assert m.shape == (2, 3) and (m == [(0, 1, 0), (0, 0, 0)]).all()
+
+a[1].magmom = (1, 0, 0)
+m = a.get_initial_magnetic_moments()
+assert m.shape == (2, 3) and (m == [(0, 1, 0), (1, 0, 0)]).all()
+assert (a[1].magmom == (1, 0, 0)).all()
diff --git a/ase/test/nwchem/nwchem_cmdline.py b/ase/test/nwchem/nwchem_cmdline.py
new file mode 100644
index 0000000..740be54
--- /dev/null
+++ b/ase/test/nwchem/nwchem_cmdline.py
@@ -0,0 +1,19 @@
+import os
+
+from ase.test import NotAvailable
+
+try:
+    nwchem_command = os.getenv('NWCHEM_COMMAND')
+    if nwchem_command == None:
+        raise NotAvailable('NWCHEM_COMMAND not defined')
+except NotAvailable:
+    raise NotAvailable('Nwchem required')
+
+import numpy as np
+
+from ase.tasks.main import run
+
+atoms, task = run("nwchem molecule O2 O -l -p task=gradient")
+atoms, task = run('nwchem molecule O2 O -s')
+ae = 2 * np.array(task.results['O'])[0] - np.array(task.results['O2'])[0]
+assert abs(ae - 6.605) < 1e-3
diff --git a/ase/test/properties.py b/ase/test/properties.py
new file mode 100644
index 0000000..8dd3520
--- /dev/null
+++ b/ase/test/properties.py
@@ -0,0 +1,13 @@
+from ase import Atoms
+a = Atoms('H2', positions=[(0, 0, 0), (0, 0, 1.1)])
+
+a.pbc[0] = 1
+assert a.pbc.any()
+assert not a.pbc.all()
+a.pbc = 1
+assert a.pbc.all()
+
+a.cell = (1, 2, 3)
+a.cell *= 2
+a.cell[0, 0] = 3
+assert not (a.cell.diagonal() - (3, 4, 6)).any()
diff --git a/ase/test/pull.py b/ase/test/pull.py
new file mode 100644
index 0000000..ac59430
--- /dev/null
+++ b/ase/test/pull.py
@@ -0,0 +1,10 @@
+import numpy as np
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.io import PickleTrajectory
+
+Cu = Atoms('Cu', pbc=(1, 0, 0), calculator=EMT())
+traj = PickleTrajectory('Cu.traj', 'w')
+for a in np.linspace(2.0, 4.0, 20):
+    Cu.set_cell([a, 1, 1], scale_atoms=True)
+    traj.write(Cu)
diff --git a/ase/test/repeat_FixAtoms.py b/ase/test/repeat_FixAtoms.py
new file mode 100644
index 0000000..865b39e
--- /dev/null
+++ b/ase/test/repeat_FixAtoms.py
@@ -0,0 +1,39 @@
+from ase.structure import molecule
+from ase.constraints import FixAtoms
+
+N = 2
+
+atoms = molecule('CO2')
+atoms.set_cell((15.,15.,15.))
+
+print('indices method')
+atomsi = atoms.copy()
+atomsi.set_constraint(FixAtoms(indices=[0,]))
+atomsi = atomsi.repeat((N,1,1))
+
+atomsiref = atoms.copy().repeat((N,1,1))
+atomsiref.set_constraint(FixAtoms(indices=[0, N + 1]))
+
+lcatomsi = list(atomsi.constraints[0].index)
+lcatomsiref = list(atomsiref.constraints[0].index)
+
+assert lcatomsi == lcatomsiref
+
+print('mask method')
+atomsm = atoms.copy()
+atomsm.set_constraint(FixAtoms(mask=[True, False, False]))
+atomsm = atomsm.repeat((N,1,1))
+
+atomsmref = atoms.copy().repeat((N,1,1))
+atomsmref.set_constraint(FixAtoms(mask=[True, False, False] * N))
+
+lcatomsm = list(atomsm.constraints[0].index)
+lcatomsmref = list(atomsmref.constraints[0].index)
+
+assert lcatomsm == lcatomsmref
+
+# http://stackoverflow.com/questions/3873361/finding-multiple-occurrences-of-a-string-within-a-string-in-python
+
+lcatomsm2i = [n for (n, e) in enumerate(lcatomsm) if e == True]
+
+assert lcatomsm2i == lcatomsi
diff --git a/ase/test/replay.py b/ase/test/replay.py
new file mode 100644
index 0000000..6efd310
--- /dev/null
+++ b/ase/test/replay.py
@@ -0,0 +1,35 @@
+from math import sqrt
+from ase import Atoms, Atom
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+from ase.io import read
+from ase.vibrations import Vibrations
+
+# Distance between Cu atoms on a (100) surface:
+d = 3.6 / sqrt(2)
+a = Atoms('Cu',
+          positions=[(0, 0, 0)],
+          cell=(d, d, 1.0),
+          pbc=(True, True, False))
+a *= (2, 2, 1)  # 2x2 (100) surface-cell
+
+# Approximate height of Ag atom on Cu(100) surfece:
+h0 = 2.0
+a += Atom('Ag', (d / 2, d / 2, h0))
+
+if 0:
+    view(a)
+
+constraint = FixAtoms(range(len(a) - 1))
+a.set_calculator(EMT())
+a.set_constraint(constraint)
+dyn1 = QuasiNewton(a, trajectory='AgCu1.traj', logfile='AgCu1.log')
+dyn1.run(fmax=0.1)
+
+a = read('AgCu1.traj')
+a.set_calculator(EMT())
+print a.constraints
+dyn2 = QuasiNewton(a, trajectory='AgCu2.traj', logfile='AgCu2.log')
+dyn2.replay_trajectory('AgCu1.traj')
+dyn2.run(fmax=0.01)
diff --git a/ase/test/rotate.py b/ase/test/rotate.py
new file mode 100644
index 0000000..d6bc8cf
--- /dev/null
+++ b/ase/test/rotate.py
@@ -0,0 +1,17 @@
+from ase.utils import rotate, irotate
+
+def test(xyz):
+    a = rotate(xyz)
+    ixyz = '%sx,%sy,%sz' % irotate(a)
+    a2 = rotate(ixyz)
+    print xyz
+    print ixyz
+    #print np.around(a-a2, 5)
+    assert abs(a-a2).max() < 1e-10
+
+test('10z')
+test('155x,43y,190z')
+test('55x,90y,190z')
+test('180x,-90y,45z')
+test('-180y')
+test('40z,50x')
diff --git a/ase/test/rotate_euler.py b/ase/test/rotate_euler.py
new file mode 100644
index 0000000..4c3a202
--- /dev/null
+++ b/ase/test/rotate_euler.py
@@ -0,0 +1,12 @@
+from math import pi, sqrt
+from ase import Atoms, Atom
+
+d = 1.14
+a = Atoms([Atom('C', (0, 0, 0)), Atom('O', (d, 0, 0))])
+a.rotate_euler(phi=pi/2, theta=pi/4, psi=pi)
+for p in a[0].position:
+    assert p == 0.0
+assert abs(a[1].position[0]) < 1e-15
+d2 = d / sqrt(2)
+assert abs(a[1].position[1] - d2) < 1e-15
+assert abs(a[1].position[2] - d2) < 1e-15
diff --git a/ase/test/scaled_positions.py b/ase/test/scaled_positions.py
new file mode 100644
index 0000000..55a647c
--- /dev/null
+++ b/ase/test/scaled_positions.py
@@ -0,0 +1,2 @@
+from ase import Atoms
+assert Atoms('X', [(-1e-35, 0, 0)], pbc=True).get_scaled_positions()[0, 0] < 1
diff --git a/ase/test/stm.py b/ase/test/stm.py
new file mode 100644
index 0000000..90bfa68
--- /dev/null
+++ b/ase/test/stm.py
@@ -0,0 +1,7 @@
+from ase.calculators.test import make_test_dft_calculation
+from ase.dft.stm import STM
+
+atoms = make_test_dft_calculation()
+stm = STM(atoms, [0, 1, 2])
+c = stm.get_averaged_current(4.5)
+h = stm.scan(c)
diff --git a/ase/test/strain.py b/ase/test/strain.py
new file mode 100644
index 0000000..65b3811
--- /dev/null
+++ b/ase/test/strain.py
@@ -0,0 +1,34 @@
+from math import sqrt
+from ase import Atoms
+from ase.constraints import StrainFilter
+from ase.optimize.mdmin import MDMin
+from ase.io import PickleTrajectory
+try:
+    from asap3 import EMT
+except ImportError:
+    pass
+else:
+    a = 3.6
+    b = a / 2
+    cu = Atoms('Cu', cell=[(0,b,b),(b,0,b),(b,b,0)], pbc=1) * (6, 6, 6)
+
+    cu.set_calculator(EMT())
+    f = StrainFilter(cu, [1, 1, 1, 0, 0, 0])
+    opt = MDMin(f, dt=0.01)
+    t = PickleTrajectory('Cu.traj', 'w', cu)
+    opt.attach(t)
+    opt.run(0.001)
+
+# HCP:
+    from ase.lattice.surface import hcp0001
+    cu = hcp0001('Cu', (1, 1, 2), a=a / sqrt(2))
+    cu.cell[1,0] += 0.05
+    cu *= (6, 6, 3)
+
+    cu.set_calculator(EMT())
+    f = StrainFilter(cu)
+    opt = MDMin(f, dt=0.01)
+    t = PickleTrajectory('Cu.traj', 'w', cu)
+    opt.attach(t)
+    opt.run(0.01)
+
diff --git a/ase/test/strain_emt.py b/ase/test/strain_emt.py
new file mode 100644
index 0000000..d453664
--- /dev/null
+++ b/ase/test/strain_emt.py
@@ -0,0 +1,14 @@
+"""This test checks that the StrainFilter works using the default
+built-in EMT calculator."""
+
+from ase.constraints import StrainFilter
+from ase.optimize.mdmin import MDMin
+from ase.calculators.emt import EMT
+from ase.structure import bulk
+
+cu = bulk('Cu', 'fcc', a=3.6)
+
+cu.set_calculator(EMT(fakestress=True))
+f = StrainFilter(cu)
+opt = MDMin(f, dt=0.01)
+opt.run(0.1, steps=2)
diff --git a/ase/test/things.py b/ase/test/things.py
new file mode 100644
index 0000000..d0405a6
--- /dev/null
+++ b/ase/test/things.py
@@ -0,0 +1,14 @@
+from ase.dft import monkhorst_pack
+
+assert [0, 0, 0] in  monkhorst_pack((1, 3, 5)).tolist()
+assert [0, 0, 0] not in  monkhorst_pack((1, 3, 6)).tolist()
+assert len(monkhorst_pack((3, 4, 6))) == 3 * 4 * 6
+
+from ase.units import Hartree, Bohr, kJ, mol, kcal, kB, fs
+print Hartree, Bohr, kJ/mol, kcal/mol, kB*300, fs, 1/fs
+
+from ase.lattice import bulk
+hcp = bulk('X', 'hcp', a=1) * (2, 2, 1)
+assert abs(hcp.get_distance(0, 3, mic=True) - 1) < 1e-12
+assert abs(hcp.get_distance(0, 4, mic=True) - 1) < 1e-12
+assert abs(hcp.get_distance(2, 5, mic=True) - 1) < 1e-12
diff --git a/ase/test/unitcellfilter.py b/ase/test/unitcellfilter.py
new file mode 100644
index 0000000..9119d69
--- /dev/null
+++ b/ase/test/unitcellfilter.py
@@ -0,0 +1,36 @@
+from math import sqrt
+from ase import Atoms
+from ase.optimize.lbfgs import LBFGS
+from ase.constraints import StrainFilter, UnitCellFilter
+from ase.io import PickleTrajectory
+from ase.optimize.lbfgs import LBFGS
+from ase.optimize.mdmin import MDMin
+try:
+    from asap3 import EMT
+except ImportError:
+    pass
+else:
+    a = 3.6
+    b = a / 2
+    cu = Atoms('Cu', cell=[(0,b,b),(b,0,b),(b,b,0)], pbc=1) * (6, 6, 6)
+    cu.set_calculator(EMT())
+    f = UnitCellFilter(cu, [1, 1, 1, 0, 0, 0])
+    opt = LBFGS(f)
+    t = PickleTrajectory('Cu-fcc.traj', 'w', cu)
+    opt.attach(t)
+    opt.run(5.0)
+
+    # HCP:
+    from ase.lattice.surface import hcp0001
+    cu = hcp0001('Cu', (1, 1, 2), a=a / sqrt(2))
+    cu.cell[1,0] += 0.05
+    cu *= (6, 6, 3)
+    cu.set_calculator(EMT())
+    print cu.get_forces()
+    print cu.get_stress()
+    f = UnitCellFilter(cu)
+    opt = MDMin(f,dt=0.01)
+    t = PickleTrajectory('Cu-hcp.traj', 'w', cu)
+    opt.attach(t)
+    opt.run(0.2)
+
diff --git a/ase/test/vacancy.py b/ase/test/vacancy.py
new file mode 100644
index 0000000..e305479
--- /dev/null
+++ b/ase/test/vacancy.py
@@ -0,0 +1,40 @@
+from ase import Atoms
+from ase.optimize import QuasiNewton
+from ase.neb import NEB
+from ase.optimize.mdmin import MDMin
+try:
+    from asap3 import EMT
+except ImportError:
+    pass
+else:
+
+    a = 3.6
+    b = a / 2
+    initial = Atoms('Cu4',
+                    positions=[(0, 0, 0),
+                               (0, b, b),
+                               (b, 0, b),
+                               (b, b, 0)],
+                    cell=(a, a, a),
+                    pbc=True)
+    initial *= (4, 4, 4)
+    del initial[0]
+    images = [initial] + [initial.copy() for i in range(6)]
+    images[-1].positions[0] = (0, 0, 0)
+    for image in images:
+        image.set_calculator(EMT())
+        #image.set_calculator(ASAP())
+
+    for image in [images[0], images[-1]]:
+        QuasiNewton(image).run(fmax=0.01)
+    neb = NEB(images)
+    neb.interpolate()
+
+    for a in images:
+        print a.positions[0], a.get_potential_energy()
+
+    dyn = MDMin(neb, dt=0.1, trajectory='mep1.traj')
+    #dyn = QuasiNewton(neb)
+    print dyn.run(fmax=0.01, steps=25)
+    for a in images:
+        print a.positions[0], a.get_potential_energy()
diff --git a/ase/test/vasp/vasp_Al_volrelax.py b/ase/test/vasp/vasp_Al_volrelax.py
new file mode 100644
index 0000000..5052907
--- /dev/null
+++ b/ase/test/vasp/vasp_Al_volrelax.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+
+"""
+Run VASP tests to ensure that relaxation with the VASP calculator works.
+This is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT
+environment variables.
+
+"""
+
+from ase.test import NotAvailable
+import os
+
+vcmd = os.getenv('VASP_COMMAND')
+vscr = os.getenv('VASP_SCRIPT')
+if vcmd == None and vscr == None:
+    raise NotAvailable('Neither VASP_COMMAND nor VASP_SCRIPT defined')
+
+import numpy as np
+from ase import io
+# QuasiNewton nowadays is an alias for BFGSLineSearch, which is
+# broken. Use BFGS instead.
+from ase.optimize import BFGS as QuasiNewton
+from ase.lattice import bulk
+from ase.calculators.vasp import Vasp
+
+# -- Perform Volume relaxation within Vasp
+def vasp_vol_relax():
+    Al = bulk('Al', 'fcc', a=4.5, cubic=True)
+    calc = Vasp(xc='LDA', isif=7, nsw=5,
+                ibrion=1, ediffg=-1e-3, lwave=False, lcharg=False)
+    calc.calculate(Al)
+
+    # Explicitly parse atomic position output file from Vasp
+    CONTCAR_Al = io.read('CONTCAR', format='vasp')
+
+    print 'Stress after relaxation:\n', calc.read_stress()
+
+    print 'Al cell post relaxation from calc:\n', calc.get_atoms().get_cell()
+    print 'Al cell post relaxation from atoms:\n', Al.get_cell()
+    print 'Al cell post relaxation from CONTCAR:\n', CONTCAR_Al.get_cell()
+
+    # All the cells should be the same.
+    assert (calc.get_atoms().get_cell() == CONTCAR_Al.get_cell()).all()
+    assert (Al.get_cell() == CONTCAR_Al.get_cell()).all()
+
+    return Al
+
+# -- Perform Volume relaxation using ASE with Vasp as force/stress calculator
+def ase_vol_relax():
+    Al = bulk('Al', 'fcc', a=4.5, cubic=True)
+    calc = Vasp(xc='LDA')
+    Al.set_calculator(calc)
+
+    from ase.constraints import StrainFilter
+    sf = StrainFilter(Al)
+    qn = QuasiNewton(sf, logfile='relaxation.log')
+    qn.run(fmax=0.1, steps=5)
+
+    print 'Stress:\n', calc.read_stress()
+    print 'Al post ASE volume relaxation\n', calc.get_atoms().get_cell()
+
+    return Al
+
+# Test function for comparing two cells
+def cells_almost_equal(cellA, cellB, tol=0.01):
+    return  (np.abs(cellA - cellB) < tol).all()
+
+# Correct LDA relaxed cell
+a_rel = 4.18
+LDA_cell = np.diag([a_rel, a_rel, a_rel])
+
+Al_vasp = vasp_vol_relax()
+Al_ase = ase_vol_relax()
+
+assert cells_almost_equal(LDA_cell, Al_vasp.get_cell())
+assert cells_almost_equal(LDA_cell, Al_ase.get_cell())
+
+# Cleanup
+Al_ase.get_calculator().clean()
diff --git a/ase/test/vasp/vasp_co.py b/ase/test/vasp/vasp_co.py
new file mode 100644
index 0000000..c651141
--- /dev/null
+++ b/ase/test/vasp/vasp_co.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+"""
+Run some VASP tests to ensure that the VASP calculator works. This
+is conditional on the existence of the VASP_COMMAND or VASP_SCRIPT
+environment variables
+
+"""
+
+from ase.test import NotAvailable
+import os
+
+vcmd = os.getenv('VASP_COMMAND')
+vscr = os.getenv('VASP_SCRIPT')
+if vcmd == None and vscr == None:
+    raise NotAvailable('Neither VASP_COMMAND nor VASP_SCRIPT defined')
+
+from ase import Atoms
+from ase.calculators.vasp import Vasp
+import numpy as np
+
+def array_almost_equal(a1, a2, tol=np.finfo(type(1.0)).eps):
+    """Replacement for old numpy.testing.utils.array_almost_equal."""
+    return (np.abs(a1 - a2) < tol).all()
+
+d = 1.14
+co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)],
+              pbc=True)
+co.center(vacuum=5.)
+
+calc = Vasp(
+            xc = 'PBE',
+            prec = 'Low',
+            algo = 'Fast',
+            ismear= 0,
+            sigma = 1.,
+            istart = 0,
+            lwave = False,
+            lcharg = False)
+
+co.set_calculator(calc)
+en = co.get_potential_energy()
+assert abs(en + 14.918933) < 1e-4
+
+# Secondly, check that restart from the previously created VASP output works
+
+calc2 = Vasp(restart=True)
+co2 = calc2.get_atoms()
+
+# Need tolerance of 1e-14 because VASP itself changes coordinates
+# slightly between reading POSCAR and writing CONTCAR even if no ionic
+# steps are made.
+assert array_almost_equal(co.positions, co2.positions, 1e-14)
+
+assert en - co2.get_potential_energy() == 0.
+assert array_almost_equal(calc.get_stress(co), calc2.get_stress(co2))
+assert array_almost_equal(calc.get_forces(co), calc2.get_forces(co2))
+assert array_almost_equal(calc.get_eigenvalues(), calc2.get_eigenvalues())
+assert calc.get_number_of_bands() == calc2.get_number_of_bands()
+assert calc.get_xc_functional() == calc2.get_xc_functional()
+
+# Cleanup
+calc.clean()
diff --git a/ase/test/verlet.py b/ase/test/verlet.py
new file mode 100644
index 0000000..8364180
--- /dev/null
+++ b/ase/test/verlet.py
@@ -0,0 +1,29 @@
+import numpy as np
+from ase import Atoms
+from ase.units import fs
+from ase.calculators.test import TestPotential
+from ase.calculators.emt import EMT
+from ase.md import VelocityVerlet
+from ase.io import PickleTrajectory, read
+from ase.optimize import QuasiNewton
+
+np.seterr(all='raise')
+a = Atoms('4X',
+          masses=[1, 2, 3, 4],
+          positions=[(0, 0, 0),
+                     (1, 0, 0),
+                     (0, 1, 0),
+                     (0.1, 0.2, 0.7)],
+          calculator=TestPotential())
+print a.get_forces()
+md = VelocityVerlet(a, dt=0.5 * fs, logfile='-', loginterval=500)
+traj = PickleTrajectory('4N.traj', 'w', a)
+md.attach(traj.write, 100)
+e0 = a.get_total_energy()
+md.run(steps=10000)
+del traj
+assert abs(read('4N.traj').get_total_energy() - e0) < 0.0001
+
+qn = QuasiNewton(a)
+qn.run(0.001)
+assert abs(a.get_potential_energy() - 1.0) < 0.000002
diff --git a/ase/test/vib.py b/ase/test/vib.py
new file mode 100644
index 0000000..46e14f4
--- /dev/null
+++ b/ase/test/vib.py
@@ -0,0 +1,21 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+from ase.vibrations import Vibrations
+from ase.thermochemistry import IdealGasThermo
+
+n2 = Atoms('N2',
+           positions=[(0, 0, 0), (0, 0, 1.1)],
+           calculator=EMT())
+QuasiNewton(n2).run(fmax=0.01)
+vib = Vibrations(n2)
+vib.run()
+print vib.get_frequencies()
+vib.summary()
+print vib.get_mode(-1)
+vib.write_mode(-1, nimages=20)
+vib_energies = vib.get_energies()
+
+thermo = IdealGasThermo(vib_energies=vib_energies, geometry='linear', 
+                        atoms=n2, symmetrynumber=2, spin=0)
+thermo.get_free_energy(temperature=298.15, pressure=2*101325.)
diff --git a/ase/test/vtk_data.py b/ase/test/vtk_data.py
new file mode 100755
index 0000000..5e590ad
--- /dev/null
+++ b/ase/test/vtk_data.py
@@ -0,0 +1,481 @@
+#!/usr/bin/env python
+
+from ase.visualize.vtk import requirevtk, probe_vtk_kilobyte
+
+requirevtk()
+vtk_kilobyte = probe_vtk_kilobyte(1024)
+
+import numpy as np
+
+import sys, unittest, gc
+from ase.test import CustomTestCase, CustomTextTestRunner
+from ase.utils.memory import MemoryStatistics, MemorySingleton, shapeopt
+
+from vtk import vtkDataArray
+from ase.visualize.vtk.data import vtkFloatArrayFromNumPyArray, \
+                                   vtkDoubleArrayFromNumPyArray, \
+                                   vtkFloatArrayFromNumPyMultiArray, \
+                                   vtkDoubleArrayFromNumPyMultiArray
+
+# -------------------------------------------------------------------
+
+class UTConversionDataArrayNumPy(CustomTestCase):
+    """
+    Abstract class with test cases for VTK/NumPy data conversion.
+
+    Leak tests the six possible permutations of deletion order for the
+    objects involved in conversion between VTK and NumPy data formats.
+
+    Objects:
+
+    conv: instance of vtkDataArrayFromNumPyBuffer of subclass thereof
+        The object in charge of the conversion
+    data: NumPy array
+        NumPy data with a specific memory footprint
+    vtk_da: instance of vtkDataArray of subclass thereof
+        VTK data array with a similar memory footprint
+
+    Permutations:
+
+    Case A: 012 i.e. deletion order is conv, data, vtk_da
+    Case B: 021 i.e. deletion order is conv, vtk_da, data
+    Case C: 102 i.e. deletion order is data, conv, vtk_da
+    Case D: 120 i.e. deletion order is data, vtk_da, conv
+    Case E: 201 i.e. deletion order is vtk_da, conv, data
+    Case F: 210 i.e. deletion order is vtk_da, data, conv
+    """
+    footprint = 100*1024**2
+    dtype = None
+    verbose = 0
+    gc_threshold = (300,5,5) #default is (700,10,10)
+    gc_flags = gc.DEBUG_LEAK # | gc.DEBUG_STATS
+    ctol = -7 #10MB
+    etol = -7 #10MB
+
+    def setUp(self):
+        self.mem_ini = MemorySingleton(self.verbose-1)
+        self.mem_ref = MemoryStatistics(self.verbose-1)
+        self.mem_cur = self.mem_ref.copy()
+
+        self.gc_threshold_old = gc.get_threshold()
+        self.gc_flags_old = gc.get_debug()
+        gc.set_threshold(*self.gc_threshold)
+        gc.set_debug(self.gc_flags)
+
+        # Try to obtain a clean slate
+        gc.collect()
+        self.gc_count = len(gc.garbage)
+        del gc.garbage[:]
+
+    def tearDown(self):
+        gc.collect()
+        self.assertEqual(len(gc.garbage), self.gc_count)
+        if len(gc.garbage)>0:
+            if self.verbose>1: print gc.get_objects()
+            #TODO be pedantic and fail?
+        del gc.garbage[:]
+        gc.set_threshold(*self.gc_threshold_old)
+        gc.set_debug(self.gc_flags_old)
+
+    def assertAlmostConsumed(self, bytes, digits=0, key='VmSize'):
+        self.mem_cur.update()
+        dm = self.mem_cur-self.mem_ref
+        self.assertAlmostEqual(dm[key], bytes, digits)
+
+    def assertAlmostExceeded(self, bytes, digits=0, key='VmPeak'):
+        self.mem_cur.update()
+        dm = self.mem_cur-self.mem_ini
+        #self.assertAlmostEqual(dm[key], bytes, digits) #TODO what really?
+        #self.assertAlmostEqual(max(0, dm[key]-bytes), 0, digits) #TODO ???
+        #dm = 200 MB, bytes = 100MB     ok
+        #dm = 101 MB, bytes = 100MB     ok
+        #dm = 0 MB, bytes = 100MB       bad
+
+    def convert_to_vtk_array(self, data):
+        """Convert an ndarray to a VTK data array.
+
+         data: NumPy array
+            NumPy data with a specific memory footprint
+        """
+
+        raise RuntimeError('Virtual member function.')
+
+    def get_leaktest_scenario(self):
+        """Construct the necessary conversion objects for leak testing.
+
+        Returns tuple of the form (conv, data, vtk_da,) where:
+
+        conv: instance of vtkDataArrayFromNumPyBuffer of subclass thereof
+            The object in charge of the conversion
+        data: NumPy array
+            NumPy data with a specific memory footprint
+        vtk_da: instance of vtkDataArray of subclass thereof
+            VTK data array with a similar memory footprint
+        """
+
+        raise RuntimeError('Virtual member function.')
+
+    # =================================
+
+    def test_deletion_case_a(self):
+        # Case A: 012 i.e. deletion order is conv, data, vtk_da
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+        # NumPy cleanup
+        del data
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+    def test_deletion_case_b(self):
+        # Case B: 021 i.e. deletion order is conv, vtk_da, data
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+        # Numpy cleanup
+        del data
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+    def test_deletion_case_c(self):
+        # Case C: 102 i.e. deletion order is data, conv, vtk_da
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # NumPy cleanup
+        del data
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+    def test_deletion_case_d(self):
+        # Case D: 120 i.e. deletion order is data, vtk_da, conv
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # NumPy cleanup
+        del data
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+    def test_deletion_case_e(self):
+        # Case E: 201 i.e. deletion order is vtk_da, conv, data
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+        # NumPy cleanup
+        del data
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+    def test_deletion_case_f(self):
+        # Case F: 210 i.e. deletion order is vtk_da, data, conv
+        (conv, data, vtk_da,) = self.get_leaktest_scenario()
+
+        # VTK cleanup
+        del vtk_da
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'VTK cleanup=', self.mem_cur-self.mem_ref
+
+        # NumPy cleanup
+        del data
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy cleanup=', self.mem_cur-self.mem_ref
+
+        # Conversion cleanup
+        del conv
+        self.assertAlmostConsumed(0, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion cleanup=', self.mem_cur-self.mem_ref
+
+# -------------------------------------------------------------------
+
+# class UTDataArrayFromNumPyBuffer(...): TODO
+
+# -------------------------------------------------------------------
+
+class UTDataArrayFromNumPyArray_Scalar(UTConversionDataArrayNumPy):
+    """
+    Test cases for memory leaks during VTK/NumPy data conversion.
+    Conversion of 1D NumPy array to VTK data array using buffers."""
+
+    def setUp(self):
+        UTConversionDataArrayNumPy.setUp(self)
+        self.shape = self.footprint//np.nbytes[self.dtype]
+
+    def get_leaktest_scenario(self):
+        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
+                               self.footprint, -2) #100B
+
+        # Update memory reference
+        self.mem_ref.update()
+
+        # NumPy allocation
+        data = np.empty(self.shape, self.dtype)
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
+
+        # NumPy to VTK conversion
+        np2da = self.convert_to_vtk_array(data)
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
+
+        # VTK retrieval
+        vtk_da = np2da.get_output()
+        self.assertTrue(isinstance(vtk_da, vtkDataArray))
+        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
+                               self.footprint, -3) #1kB
+        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
+
+        return (np2da, data, vtk_da,)
+
+class UTFloatArrayFromNumPyArray_Scalar(UTDataArrayFromNumPyArray_Scalar):
+    __doc__ = UTDataArrayFromNumPyArray_Scalar.__doc__
+    dtype = np.float32
+    convert_to_vtk_array = vtkFloatArrayFromNumPyArray
+
+class UTDoubleArrayFromNumPyArray_Scalar(UTDataArrayFromNumPyArray_Scalar):
+    __doc__ = UTDataArrayFromNumPyArray_Scalar.__doc__
+    dtype = np.float64
+    convert_to_vtk_array = vtkDoubleArrayFromNumPyArray
+
+class UTDataArrayFromNumPyArray_Vector(UTConversionDataArrayNumPy):
+    """
+    Test cases for memory leaks during VTK/NumPy data conversion.
+    Conversion of 2D NumPy array to VTK data array using buffers."""
+
+    def setUp(self):
+        UTConversionDataArrayNumPy.setUp(self)
+        size = self.footprint//np.nbytes[self.dtype]
+        self.shape = (size//3, 3)
+
+    def get_leaktest_scenario(self):
+        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
+                               self.footprint, -2) #100B
+
+        # Update memory reference
+        self.mem_ref.update()
+
+        # NumPy allocation
+        data = np.empty(self.shape, self.dtype)
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
+
+        # NumPy to VTK conversion
+        np2da = self.convert_to_vtk_array(data)
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
+
+        # VTK retrieval
+        vtk_da = np2da.get_output()
+        self.assertTrue(isinstance(vtk_da, vtkDataArray))
+        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
+                               self.footprint, -3) #1kB
+        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
+
+        return (np2da, data, vtk_da,)
+
+class UTFloatArrayFromNumPyArray_Vector(UTDataArrayFromNumPyArray_Vector):
+    __doc__ = UTDataArrayFromNumPyArray_Vector.__doc__
+    dtype = np.float32
+    convert_to_vtk_array = vtkFloatArrayFromNumPyArray
+
+class UTDoubleArrayFromNumPyArray_Vector(UTDataArrayFromNumPyArray_Vector):
+    __doc__ = UTDataArrayFromNumPyArray_Vector.__doc__
+    dtype = np.float64
+    convert_to_vtk_array = vtkDoubleArrayFromNumPyArray
+
+# -------------------------------------------------------------------
+
+class UTDataArrayFromNumPyMultiArray_Scalar(UTConversionDataArrayNumPy):
+    """
+    Test cases for memory leaks during VTK/NumPy data conversion.
+    Conversion of NumPy grid scalars to VTK data array using buffers."""
+
+    def setUp(self):
+        UTConversionDataArrayNumPy.setUp(self)
+        size = self.footprint//np.nbytes[self.dtype]
+        digits, shape = shapeopt(1000, size, ndims=3, ecc=0.3)
+        if self.verbose>=1: print 'digits=%8.3f, shape=%s' % (digits,shape)
+        self.shape = shape + (1,)
+        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
+                               self.footprint, -4) #10kB
+
+    def get_leaktest_scenario(self):
+
+        # Update memory reference
+        self.mem_ref.update()
+
+        # NumPy allocation
+        data = np.empty(self.shape, self.dtype)
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
+
+        # NumPy to VTK conversion
+        np2da = self.convert_to_vtk_array(data)
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
+
+        # VTK retrieval
+        vtk_da = np2da.get_output()
+        self.assertTrue(isinstance(vtk_da, vtkDataArray))
+        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
+                               self.footprint, -4) #10kB
+        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
+
+        return (np2da, data, vtk_da,)
+
+class UTFloatArrayFromNumPyMultiArray_Scalar(UTDataArrayFromNumPyMultiArray_Scalar):
+    __doc__ = UTDataArrayFromNumPyMultiArray_Scalar.__doc__
+    dtype = np.float32
+    convert_to_vtk_array = vtkFloatArrayFromNumPyMultiArray
+
+class UTDoubleArrayFromNumPyMultiArray_Scalar(UTDataArrayFromNumPyMultiArray_Scalar):
+    __doc__ = UTDataArrayFromNumPyMultiArray_Scalar.__doc__
+    dtype = np.float64
+    convert_to_vtk_array = vtkDoubleArrayFromNumPyMultiArray
+
+class UTDataArrayFromNumPyMultiArray_Vector(UTConversionDataArrayNumPy):
+    """
+    Test cases for memory leaks during VTK/NumPy data conversion.
+    Conversion of NumPy grid vectors to VTK data array using buffers."""
+
+    def setUp(self):
+        UTConversionDataArrayNumPy.setUp(self)
+        size = self.footprint//np.nbytes[self.dtype]
+        digits, shape = shapeopt(1000, size//3, ndims=3, ecc=0.3)
+        if self.verbose>=1: print 'digits=%8.3f, shape=%s' % (digits,shape)
+        self.shape = shape + (3,)
+        self.assertAlmostEqual(np.prod(self.shape)*np.nbytes[self.dtype], \
+                               self.footprint, -4) #10kB
+
+    def get_leaktest_scenario(self):
+
+        # Update memory reference
+        self.mem_ref.update()
+
+        # NumPy allocation
+        data = np.empty(self.shape, self.dtype)
+        self.assertAlmostConsumed(self.footprint, self.ctol)
+        self.assertAlmostExceeded(self.footprint, self.etol)
+        if self.verbose>=1: print 'NumPy allocation=', self.mem_cur-self.mem_ref
+
+        # NumPy to VTK conversion
+        np2da = self.convert_to_vtk_array(data)
+        self.assertAlmostConsumed(2*self.footprint, self.ctol)
+        self.assertAlmostExceeded(2*self.footprint, self.etol)
+        if self.verbose>=1: print 'Conversion=', self.mem_cur-self.mem_ref
+
+        # VTK retrieval
+        vtk_da = np2da.get_output()
+        self.assertTrue(isinstance(vtk_da, vtkDataArray))
+        self.assertAlmostEqual(vtk_da.GetActualMemorySize()*vtk_kilobyte, \
+                               self.footprint, -4) #10kB
+        if self.verbose>=1: print 'VTK retrieval=', self.mem_cur-self.mem_ref
+
+        return (np2da, data, vtk_da,)
+
+class UTFloatArrayFromNumPyMultiArray_Vector(UTDataArrayFromNumPyMultiArray_Vector):
+    __doc__ = UTDataArrayFromNumPyMultiArray_Vector.__doc__
+    dtype = np.float32
+    convert_to_vtk_array = vtkFloatArrayFromNumPyMultiArray
+
+class UTDoubleArrayFromNumPyMultiArray_Vector(UTDataArrayFromNumPyMultiArray_Vector):
+    __doc__ = UTDataArrayFromNumPyMultiArray_Vector.__doc__
+    dtype = np.float64
+    convert_to_vtk_array = vtkDoubleArrayFromNumPyMultiArray
+
+# -------------------------------------------------------------------
+
+if __name__ in ['__main__', '__builtin__']:
+    # We may have been imported by test.py, if so we should redirect to logfile
+    if __name__ == '__builtin__':
+        testrunner = CustomTextTestRunner('vtk_data.log', verbosity=2)
+    else:
+        testrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
+
+    testcases = [UTFloatArrayFromNumPyArray_Scalar, \
+                 UTDoubleArrayFromNumPyArray_Scalar, \
+                 UTFloatArrayFromNumPyArray_Vector, \
+                 UTDoubleArrayFromNumPyArray_Vector, \
+                 UTFloatArrayFromNumPyMultiArray_Scalar, \
+                 UTDoubleArrayFromNumPyMultiArray_Scalar, \
+                 UTFloatArrayFromNumPyMultiArray_Vector, \
+                 UTDoubleArrayFromNumPyMultiArray_Vector]
+
+    for test in testcases:
+        info = '\n' + test.__name__ + '\n' + test.__doc__.strip('\n') + '\n'
+        testsuite = unittest.defaultTestLoader.loadTestsFromTestCase(test)
+        testrunner.stream.writeln(info)
+        testresult = testrunner.run(testsuite)
+        # Provide feedback on failed tests if imported by test.py
+        if __name__ == '__builtin__' and not testresult.wasSuccessful():
+            raise SystemExit('Test failed. Check vtk_data.log for details.')
+
diff --git a/ase/test/vtk_pipeline.py b/ase/test/vtk_pipeline.py
new file mode 100755
index 0000000..0cb2bb1
--- /dev/null
+++ b/ase/test/vtk_pipeline.py
@@ -0,0 +1,452 @@
+#!/usr/bin/env python
+
+from ase.visualize.vtk import requirevtk
+
+requirevtk()
+
+import sys, unittest
+from ase.test import CustomTestCase, CustomTextTestRunner
+
+from vtk import vtkContourFilter, vtkPolyDataNormals, \
+                vtkLinearSubdivisionFilter, vtkPolyDataMapper
+from ase.visualize.vtk.pipeline import vtkPolyDataPipeline
+
+# -------------------------------------------------------------------
+
+class UTPipeline(CustomTestCase):
+    """
+    Abstract test case class - TODO."""
+
+    def assertConnected(self, vtk_one, vtk_two, port=0):
+        self.assertEqual(vtk_two.GetNumberOfInputConnections(port), 1)
+        self.assertEqual(vtk_one.GetOutputPort(),
+                         vtk_two.GetInputConnection(port, 0))
+
+    def assertNotConnected(self, vtk_one, vtk_two, port=0):
+        self.assertEqual(vtk_two.GetNumberOfInputConnections(port), 0)
+
+
+# -------------------------------------------------------------------
+
+class UTPolyDataPipeline(UTPipeline):
+    """
+    TODO"""
+
+    def setUp(self):
+        self.vtk_iso = vtkContourFilter()
+        #self.vtk_iso.SetInput(...)
+
+        self.vtk_dnorm = vtkPolyDataNormals()
+        self.vtk_subdiv = vtkLinearSubdivisionFilter()
+        self.vtk_dmap = vtkPolyDataMapper()
+
+    def tearDown(self):
+        del self.vtk_dmap
+        del self.vtk_subdiv
+        del self.vtk_dnorm
+        del self.vtk_iso
+
+# -------------------------------------------------------------------
+
+class UTPolyDataPipeline_PureVTK(UTPolyDataPipeline):
+    """
+    Consistency tests for pure-VTK objects with the purpose
+    of ultimately mapping the polygonal data within the pipeline."""
+
+    # =================================
+
+    def test_consistent_data_oninit(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_data_direct(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_data_postponed(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_data_onclose(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.isclosed())
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+    # =================================
+
+    def test_failure_duplicate_data(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        self.assertRaises(ValueError, pipeline.append, self.vtk_iso)
+
+    def test_failure_duplicate_mixed(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        pipeline.append(self.vtk_dnorm)
+        self.assertRaises(ValueError, pipeline.append, self.vtk_iso)
+
+    def test_failure_duplicate_cyclic(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        pipeline.append(self.vtk_dnorm)
+        pipeline.append(self.vtk_subdiv)
+        self.assertRaises(ValueError, pipeline.append, self.vtk_dnorm)
+
+    def test_failure_duplicate_filter(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        pipeline.append(self.vtk_dnorm)
+        self.assertRaises(ValueError, pipeline.append, self.vtk_dnorm)
+
+    # =================================
+
+    def test_failure_output_missing(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertRaises(RuntimeError, pipeline.get_output_port)
+
+    def test_failure_output_closed(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        pipeline.append(self.vtk_dmap)
+        self.assertRaises(RuntimeError, pipeline.append, self.vtk_dnorm)
+
+# -------------------------------------------------------------------
+
+class UTPolyDataPipeline_PipelineVTK(UTPolyDataPipeline):
+    """
+    Consistency tests for mixed VTK objects and pipelines with the purpose
+    of ultimately mapping the polygonal data through embedded pipelines."""
+
+    # =================================
+
+    def get_consistent_datapipe(self):
+        datapipe = vtkPolyDataPipeline(self.vtk_iso)
+        self.assertTrue(datapipe.hasdata())
+        self.assertTrue(self.vtk_iso in datapipe)
+        self.assertEqual(datapipe.vtkish_data, self.vtk_iso)
+        self.assertFalse(datapipe.hasfilters())
+        self.assertFalse(datapipe.isclosed())
+
+        return datapipe
+
+    def test_consistent_datapipe_oninit(self):
+        datapipe = self.get_consistent_datapipe()
+
+        pipeline = vtkPolyDataPipeline(datapipe)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(datapipe in pipeline)
+        self.assertTrue(self.vtk_iso in pipeline)
+        self.assertEqual(pipeline.vtkish_data, datapipe)
+        self.assertRaises(RuntimeError, datapipe.isclosed)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_datapipe_direct(self):
+        datapipe = self.get_consistent_datapipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(datapipe in pipeline)
+        self.assertFalse(self.vtk_iso in pipeline)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.set_data(datapipe)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(datapipe in pipeline)
+        self.assertTrue(self.vtk_iso in pipeline)
+        self.assertEqual(pipeline.vtkish_data, datapipe)
+        self.assertRaises(RuntimeError, datapipe.isclosed)
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_datapipe_postponed(self):
+        datapipe = self.get_consistent_datapipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(datapipe in pipeline)
+        self.assertFalse(self.vtk_iso in pipeline)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.set_data(datapipe)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(datapipe in pipeline)
+        self.assertTrue(self.vtk_iso in pipeline)
+        self.assertEqual(pipeline.vtkish_data, datapipe)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertTrue(datapipe.isclosed())
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_datapipe_onclose(self):
+        datapipe = self.get_consistent_datapipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(datapipe in pipeline)
+        self.assertFalse(self.vtk_iso in pipeline)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dnorm)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.isclosed())
+
+        pipeline.set_data(datapipe)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, datapipe)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertTrue(datapipe.isclosed())
+
+    # =================================
+
+    def get_consistent_filterpipe(self):
+        filterpipe = vtkPolyDataPipeline()
+        self.assertFalse(filterpipe.hasdata())
+        self.assertFalse(self.vtk_iso in filterpipe)
+        self.assertFalse(filterpipe.hasfilters())
+        self.assertFalse(filterpipe.isclosed())
+
+        filterpipe.append(self.vtk_dnorm)
+        self.assertTrue(filterpipe.hasfilters())
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+
+        filterpipe.append(self.vtk_subdiv)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        return filterpipe
+
+    def test_consistent_filterpipe_oninit(self):
+        filterpipe = self.get_consistent_filterpipe()
+
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(filterpipe)
+        self.assertRaises(RuntimeError, filterpipe.isclosed)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dmap)
+        self.assertTrue(filterpipe.isclosed())
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_filterpipe_direct(self):
+        filterpipe = self.get_consistent_filterpipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+
+        pipeline.append(filterpipe)
+        self.assertRaises(RuntimeError, filterpipe.isclosed)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(self.vtk_dmap)
+        self.assertTrue(filterpipe.isclosed())
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.hasfilters())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_filterpipe_postponed(self):
+        filterpipe = self.get_consistent_filterpipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(filterpipe)
+        self.assertRaises(RuntimeError, filterpipe.isclosed)
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertTrue(filterpipe.isclosed())
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertTrue(pipeline.hasdata())
+        self.assertTrue(pipeline.isclosed())
+
+    def test_consistent_filterdata_onclose(self):
+        filterpipe = self.get_consistent_filterpipe()
+
+        pipeline = vtkPolyDataPipeline()
+        self.assertFalse(pipeline.hasdata())
+        self.assertFalse(pipeline.hasfilters())
+        self.assertFalse(pipeline.isclosed())
+
+        pipeline.append(filterpipe)
+        self.assertRaises(RuntimeError, filterpipe.isclosed)
+        self.assertNotConnected(self.vtk_iso, self.vtk_dnorm)
+        self.assertConnected(self.vtk_dnorm, self.vtk_subdiv)
+
+        pipeline.append(self.vtk_dmap)
+        self.assertTrue(filterpipe.isclosed())
+        self.assertConnected(self.vtk_subdiv, self.vtk_dmap)
+        self.assertFalse(pipeline.hasdata())
+        self.assertTrue(pipeline.isclosed())
+
+        pipeline.set_data(self.vtk_iso)
+        self.assertTrue(pipeline.hasdata())
+        self.assertEqual(pipeline.vtkish_data, self.vtk_iso)
+        self.assertConnected(self.vtk_iso, self.vtk_dnorm)
+
+    # =================================
+
+    def test_failure_recursive_data(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertRaises(ValueError, pipeline.set_data, pipeline)
+
+    def test_failure_recursive_mixed(self):
+        pipeline = vtkPolyDataPipeline(self.vtk_iso)
+        pipeline.append(self.vtk_dnorm)
+        self.assertRaises(ValueError, pipeline.append, pipeline)
+
+    def test_failure_recursive_filter(self):
+        pipeline = vtkPolyDataPipeline()
+        self.assertRaises(ValueError, pipeline.append, pipeline)
+
+
+# -------------------------------------------------------------------
+
+if __name__ in ['__main__', '__builtin__']:
+    # We may have been imported by test.py, if so we should redirect to logfile
+    if __name__ == '__builtin__':
+        testrunner = CustomTextTestRunner('vtk_pipeline.log', verbosity=2)
+    else:
+        testrunner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)
+
+    testcases = [UTPolyDataPipeline_PureVTK, UTPolyDataPipeline_PipelineVTK]
+
+    for test in testcases:
+        info = '\n' + test.__name__ + '\n' + test.__doc__.strip('\n') + '\n'
+        testsuite = unittest.defaultTestLoader.loadTestsFromTestCase(test)
+        testrunner.stream.writeln(info)
+        testresult = testrunner.run(testsuite)
+        # Provide feedback on failed tests if imported by test.py
+        if __name__ == '__builtin__' and not testresult.wasSuccessful():
+            raise SystemExit('Test failed. Check vtk_pipeline.log for details.')
+
diff --git a/ase/thermochemistry.py b/ase/thermochemistry.py
new file mode 100644
index 0000000..57addd6
--- /dev/null
+++ b/ase/thermochemistry.py
@@ -0,0 +1,401 @@
+#!/usr/bin/env python
+"""Modules for calculating thermochemical information from computational
+outputs."""
+
+import os
+import sys
+import numpy as np
+
+from ase import units
+
+
+def rotationalinertia(atoms):
+    """Calculates the three principle moments of inertia for an ASE atoms
+    object. This uses the atomic masses from ASE, which (if not explicitly
+    specified by the user) gives an inexact approximation of an isotopically
+    averaged result. Units are in amu*angstroms**2."""
+
+    # Calculate the center of mass.
+    xcm, ycm, zcm = atoms.get_center_of_mass()
+    masses = atoms.get_masses()
+
+    # Calculate moments of inertia in the current frame of reference.
+    Ixx = 0.
+    Iyy = 0.
+    Izz = 0.
+    Ixy = 0.
+    Ixz = 0.
+    Iyz = 0.
+    for index, atom in enumerate(atoms):
+        m = masses[index]
+        x = atom.x - xcm
+        y = atom.y - ycm
+        z = atom.z - zcm
+        Ixx += m * (y**2. + z**2.)
+        Iyy += m * (x**2. + z**2.)
+        Izz += m * (x**2. + y**2.)
+        Ixy += m * x * y
+        Ixz += m * x * z
+        Iyz += m * y * z
+    # Create the inertia tensor in the current frame of reference.
+    I_ = np.matrix([[ Ixx, -Ixy, -Ixz],
+                    [-Ixy,  Iyy, -Iyz],
+                    [-Ixz, -Iyz,  Izz]])
+    # Find the eigenvalues, which are the principle moments of inertia.
+    I = np.linalg.eigvals(I_)
+    return I
+
+
+class ThermoChem:
+    """Base class containing common methods used in thermochemistry
+    calculations."""
+
+    def get_ZPE_correction(self):
+        """Returns the zero-point vibrational energy correction in eV."""
+        zpe = 0.
+        for energy in self.vib_energies:
+            zpe += 0.5 * energy
+        return zpe
+
+    def _vibrational_energy_contribution(self, temperature):
+        """Calculates the change in internal energy due to vibrations from
+        0K to the specified temperature for a set of vibrations given in
+        eV and a temperature given in Kelvin. Returns the energy change
+        in eV."""
+        kT = units.kB * temperature
+        dU = 0.
+        for energy in self.vib_energies:
+            dU += energy / (np.exp(energy/kT) - 1.)
+        return dU
+
+    def _vibrational_entropy_contribution(self, temperature):
+        """Calculates the entropy due to vibrations for a set of vibrations
+        given in eV and a temperature given in Kelvin.  Returns the entropy
+        in eV/K."""
+        kT = units.kB * temperature
+        S_v = 0.
+        for energy in self.vib_energies:
+            x = energy / kT
+            S_v += x / (np.exp(x)-1.) - np.log(1-np.exp(-x))
+        S_v *= units.kB
+        return S_v
+
+    def _vprint(self, text):
+        """Print output if verbose flag True."""
+        if self.verbose:
+            sys.stdout.write(text + os.linesep)
+
+
+class HarmonicThermo(ThermoChem):
+    """Class for calculating thermodynamic properties in the approximation
+    that all degrees of freedom are treated harmonically. Often used for
+    adsorbates.
+    
+    Inputs:
+
+    vib_energies : list
+        a list of the harmonic energies of the adsorbate (e.g., from
+        ase.vibrations.Vibrations.get_energies). The number of
+        energies should match the number of degrees of freedom of the
+        adsorbate; i.e., 3*n, where n is the number of atoms. Note that
+        this class does not check that the user has supplied the correct
+        number of energies. Units of energies are eV.
+    electronicenergy : float
+        the electronic energy in eV
+        (If the electronicenergy is unspecified, then the methods of this
+        class can be interpreted as the energy corrections.)
+    """
+
+    def __init__(self, vib_energies, electronicenergy=None):
+        self.vib_energies = vib_energies
+        # Check for imaginary frequencies.
+        if sum(np.iscomplex(self.vib_energies)):
+            raise ValueError('Imaginary vibrational energies are present.')
+        else:
+            self.vib_energies = np.real(self.vib_energies)  # clear +0.j 
+
+        if electronicenergy:
+            self.electronicenergy = electronicenergy
+        else:
+            self.electronicenergy = 0.
+
+    def get_internal_energy(self, temperature, verbose=True):
+        """Returns the internal energy, in eV, in the harmonic approximation
+        at a specified temperature (K)."""
+
+        self.verbose = verbose
+        write = self._vprint
+        fmt = '%-15s%13.3f eV'
+        write('Internal energy components at T = %.2f K:' % temperature)
+        write('='*31)
+
+        U = 0.
+
+        write(fmt % ('E_elec', self.electronicenergy))
+        U += self.electronicenergy
+
+        zpe = self.get_ZPE_correction()
+        write(fmt % ('E_ZPE', zpe))
+        U += zpe
+
+        dU_v = self._vibrational_energy_contribution(temperature)
+        write(fmt % ('Cv_harm (0->T)', dU_v))
+        U += dU_v
+
+        write('-'*31)
+        write(fmt % ('U', U))
+        write('='*31)
+        return U
+
+    def get_entropy(self, temperature, verbose=True):
+        """Returns the entropy, in eV/K, in the harmonic approximation
+        at a specified temperature (K)."""
+
+        self.verbose = verbose
+        write = self._vprint
+        fmt = '%-15s%13.7f eV/K%13.3f eV'
+        write('Entropy components at T = %.2f K:' % temperature)
+        write('='*49)
+        write('%15s%13s     %13s'%('', 'S', 'T*S'))
+
+        S = 0.
+
+        S_v = self._vibrational_entropy_contribution(temperature)
+        write(fmt%('S_harm', S_v, S_v*temperature))
+        S += S_v
+
+        write('-'*49)
+        write(fmt%('S', S, S*temperature))
+        write('='*49)
+        return S
+
+    def get_free_energy(self, temperature, verbose=True):
+        """Returns the free energy, in eV, in the harmonic approximation
+        at a specified temperature (K)."""
+
+        self.verbose = True
+        write = self._vprint
+
+        U = self.get_internal_energy(temperature, verbose=verbose)
+        write('')
+        S = self.get_entropy(temperature, verbose=verbose)
+        G = U - temperature * S
+
+        write('')
+        write('Free energy components at T = %.2f K:' % temperature)
+        write('='*23)
+        fmt = '%5s%15.3f eV'
+        write(fmt % ('U', U))
+        write(fmt % ('-T*S', -temperature * S))
+        write('-'*23)
+        write(fmt % ('G', G))
+        write('='*23)
+        return G
+
+
+class IdealGasThermo(ThermoChem):
+    """Class for calculating thermodynamic properties of a molecule
+    based on statistical mechanical treatments in the ideal gas
+    approximation.
+
+    Inputs for enthalpy calculations:
+
+    vib_energies : list
+        a list of the vibrational energies of the molecule (e.g., from
+        ase.vibrations.Vibrations.get_energies). The number of vibrations
+        used is automatically calculated by the geometry and the number of
+        atoms. If more are specified than are needed, then the lowest 
+        numbered vibrations are neglected. If either atoms or natoms is 
+        unspecified, then uses the entire list. Units are eV.
+    geometry : 'monatomic', 'linear', or 'nonlinear'
+        geometry of the molecule
+    electronicenergy : float
+        the electronic energy in eV
+        (If electronicenergy is unspecified, then the methods of this
+        class can be interpreted as the enthalpy and free energy
+        corrections.)
+    natoms : integer
+        the number of atoms, used along with 'geometry' to determine how
+        many vibrations to use. (Not needed if an atoms object is supplied
+        in 'atoms' or if the user desires the entire list of vibrations
+        to be used.)
+
+    Extra inputs needed for for entropy / free energy calculations:
+
+    atoms : an ASE atoms object
+        used to calculate rotational moments of inertia and molecular mass
+    symmetrynumber : integer
+        symmetry number of the molecule. See, for example, Table 10.1 and
+        Appendix B of C. Cramer "Essentials of Computational Chemistry",
+        2nd Ed.
+    spin : float
+        the total electronic spin. (0 for molecules in which all electrons
+        are paired, 0.5 for a free radical with a single unpaired electron,
+        1.0 for a triplet with two unpaired electrons, such as O_2.)
+    """
+
+    def __init__(self, vib_energies, geometry, electronicenergy=None,
+                 atoms=None, symmetrynumber=None, spin=None, natoms=None):
+        if electronicenergy == None:
+            self.electronicenergy = 0.
+        else:
+            self.electronicenergy = electronicenergy
+        self.geometry = geometry
+        self.atoms = atoms
+        self.sigma = symmetrynumber
+        self.spin = spin
+        if natoms == None:
+            if atoms:
+                natoms = len(atoms)
+        # Cut the vibrations to those needed from the geometry.
+        if natoms:
+            if geometry == 'nonlinear':
+                self.vib_energies = vib_energies[-(3*natoms-6):]
+            elif geometry == 'linear':
+                self.vib_energies = vib_energies[-(3*natoms-5):]
+            elif geometry == 'monatomic':
+                self.vib_energies = []
+        else:
+            self.vib_energies = vib_energies
+        # Make sure no imaginary frequencies remain.
+        if sum(np.iscomplex(self.vib_energies)):
+            raise ValueError('Imaginary frequencies are present.')
+        else:
+            self.vib_energies = np.real(self.vib_energies) # clear +0.j 
+        self.referencepressure = 101325. # Pa
+
+    def get_enthalpy(self, temperature, verbose=True):
+        """Returns the enthalpy, in eV, in the ideal gas approximation
+        at a specified temperature (K)."""
+
+        self.verbose = verbose
+        write = self._vprint
+        fmt = '%-15s%13.3f eV'
+        write('Enthalpy components at T = %.2f K:' % temperature)
+        write('='*31)
+
+        H = 0.
+
+        write(fmt % ('E_elec', self.electronicenergy))
+        H += self.electronicenergy
+
+        zpe = self.get_ZPE_correction()
+        write(fmt % ('E_ZPE', zpe))
+        H += zpe
+
+        Cv_t = 3./2. * units.kB # translational heat capacity (3-d gas)
+        write(fmt % ('Cv_trans (0->T)', Cv_t * temperature))
+        H += Cv_t * temperature
+
+        if self.geometry == 'nonlinear': # rotational heat capacity
+            Cv_r = 3./2.*units.kB
+        elif self.geometry == 'linear':
+            Cv_r = units.kB
+        elif self.geometry == 'monatomic':
+            Cv_r = 0.
+        write(fmt % ('Cv_rot (0->T)', Cv_r * temperature))
+        H += Cv_r*temperature
+
+        dH_v = self._vibrational_energy_contribution(temperature)
+        write(fmt % ('Cv_vib (0->T)', dH_v))
+        H += dH_v
+
+        Cp_corr = units.kB * temperature
+        write(fmt % ('(C_v -> C_p)', Cp_corr))
+        H += Cp_corr
+
+        write('-'*31)
+        write(fmt % ('H', H))
+        write('='*31)
+        return H
+
+    def get_entropy(self, temperature, pressure, verbose=True):
+        """Returns the entropy, in eV/K, in the ideal gas approximation
+        at a specified temperature (K) and pressure (Pa)."""
+
+        if self.atoms == None or self.sigma == None or self.spin == None:
+            raise RuntimeError('atoms, symmetrynumber, and spin must be '
+                               'specified for entropy and free energy '
+                               'calculations.')
+        self.verbose = verbose
+        write = self._vprint
+        fmt = '%-15s%13.7f eV/K%13.3f eV'
+        write('Entropy components at T = %.2f K and P = %.1f Pa:' %
+               (temperature, pressure))
+        write('=' * 49)
+        write('%15s%13s     %13s' % ('', 'S', 'T*S'))
+
+        S = 0.0
+
+        # Translational entropy (term inside the log is in SI units).
+        mass = sum(self.atoms.get_masses()) * units._amu  # kg/molecule
+        S_t = (2 * np.pi * mass * units._k *
+               temperature / units._hplanck**2)**(3.0 / 2)
+        S_t *= units._k * temperature / self.referencepressure 
+        S_t = units.kB * (np.log(S_t) + 5.0 / 2.0)
+        write(fmt % ('S_trans (1 atm)', S_t, S_t * temperature))
+        S += S_t
+
+        # Rotational entropy (term inside the log is in SI units).
+        if self.geometry == 'monatomic':
+            S_r = 0.0
+        elif self.geometry == 'nonlinear':
+            inertias = (rotationalinertia(self.atoms) * units._amu /
+                        (10.0**10)**2)  # kg m^2
+            S_r = np.sqrt(np.pi * np.product(inertias)) / self.sigma
+            S_r *= (8.0 * np.pi**2 * units._k * temperature /
+                    units._hplanck**2)**(3.0 / 2.0)
+            S_r = units.kB * (np.log(S_r) + 3.0 / 2.0)
+        elif self.geometry == 'linear':
+            inertias = (rotationalinertia(self.atoms) * units._amu /
+                        (10.0**10)**2)  # kg m^2
+            inertia = max(inertias)  # should be two identical and one zero
+            S_r = (8 * np.pi**2 * inertia * units._k * temperature /
+                   self.sigma / units._hplanck**2)
+            S_r = units.kB * (np.log(S_r) + 1.)
+        write(fmt % ('S_rot', S_r, S_r * temperature))
+        S += S_r
+
+        # Electronic entropy.
+        S_e = units.kB * np.log(2 * self.spin + 1)
+        write(fmt % ('S_elec', S_e, S_e * temperature))
+        S += S_e
+
+        # Vibrational entropy.
+        S_v = self._vibrational_entropy_contribution(temperature)
+        write(fmt % ('S_vib', S_v, S_v * temperature))
+        S += S_v
+
+        # Pressure correction to translational entropy.
+        S_p = - units.kB * np.log(pressure / self.referencepressure)
+        write(fmt % ('S (1 atm -> P)', S_p, S_p * temperature))
+        S += S_p
+
+        write('-' * 49)
+        write(fmt % ('S', S, S * temperature))
+        write('=' * 49)
+        return S
+
+    def get_free_energy(self, temperature, pressure, verbose=True):
+        """Returns the free energy, in eV, in the ideal gas approximation
+        at a specified temperature (K) and pressure (Pa)."""
+
+        self.verbose = verbose
+        write = self._vprint
+
+        H = self.get_enthalpy(temperature, verbose=verbose)
+        write('')
+        S = self.get_entropy(temperature, pressure, verbose=verbose)
+        G = H - temperature * S
+
+        write('')
+        write('Free energy components at T = %.2f K and P = %.1f Pa:' %
+               (temperature, pressure))
+        write('=' * 23)
+        fmt = '%5s%15.3f eV'
+        write(fmt % ('H', H))
+        write(fmt % ('-T*S', -temperature * S))
+        write('-' * 23)
+        write(fmt % ('G', G))
+        write('=' * 23)
+        return G
diff --git a/ase/transport/__init__.py b/ase/transport/__init__.py
new file mode 100644
index 0000000..ddd63e2
--- /dev/null
+++ b/ase/transport/__init__.py
@@ -0,0 +1 @@
+from calculators import TransportCalculator
diff --git a/ase/transport/calculators.py b/ase/transport/calculators.py
new file mode 100644
index 0000000..de666af
--- /dev/null
+++ b/ase/transport/calculators.py
@@ -0,0 +1,414 @@
+import numpy as np
+
+from numpy import linalg
+from ase.transport.selfenergy import LeadSelfEnergy, BoxProbe
+from ase.transport.greenfunction import GreenFunction
+from ase.transport.tools import subdiagonalize, cutcoupling, tri2full, dagger,\
+    rotate_matrix
+
+
+class TransportCalculator:
+    """Determine transport properties of a device sandwiched between
+    two semi-infinite leads using a Green function method.
+    """
+
+    def __init__(self, **kwargs):
+        """Create the transport calculator.
+
+        Parameters
+        ==========
+        h : (N, N) ndarray
+            Hamiltonian matrix for the central region. 
+        s : {None, (N, N) ndarray}, optional
+            Overlap matrix for the central region. 
+            Use None for an orthonormal basis.
+        h1 : (N1, N1) ndarray
+            Hamiltonian matrix for lead1.
+        h2 : {None, (N2, N2) ndarray}, optional
+            Hamiltonian matrix for lead2. You may use None if lead1 and lead2 
+            are identical.
+        s1 : {None, (N1, N1) ndarray}, optional
+            Overlap matrix for lead1. Use None for an orthonomormal basis.
+        hc1 : {None, (N1, N) ndarray}, optional
+            Hamiltonian coupling matrix between the first principal
+            layer in lead1 and the central region.
+        hc2 : {None, (N2, N} ndarray), optional
+            Hamiltonian coupling matrix between the first principal
+            layer in lead2 and the central region.
+        sc1 : {None, (N1, N) ndarray}, optional  
+            Overlap coupling matrix between the first principal
+            layer in lead1 and the central region.
+        sc2 : {None, (N2, N) ndarray}, optional  
+            Overlap coupling matrix between the first principal
+            layer in lead2 and the central region.
+        energies : {None, array_like}, optional
+            Energy points for which calculated transport properties are
+            evaluated.
+        eta : {1.0e-5, float}, optional
+            Infinitesimal for the central region Green function. 
+        eta1/eta2 : {1.0e-5, float}, optional
+            Infinitesimal for lead1/lead2 Green function.
+        align_bf : {None, int}, optional
+            Use align_bf=m to shift the central region 
+            by a constant potential such that the m'th onsite element
+            in the central region is aligned to the m'th onsite element
+            in lead1 principal layer.
+        logfile : {None, str}, optional 
+            Write a logfile to file with name `logfile`.
+            Use '-' to write to std out.
+        eigenchannels: {0, int}, optional
+            Number of eigenchannel transmission coefficients to 
+            calculate. 
+        pdos : {None, (N,) array_like}, optional
+            Specify which basis functions to calculate the
+            projected density of states for.
+        dos : {False, bool}, optional
+            The total density of states of the central region.
+        box: XXX
+            YYY
+            
+        If hc1/hc2 are None, they are assumed to be identical to
+        the coupling matrix elements between neareste neighbor 
+        principal layers in lead1/lead2.
+
+        Examples
+        ========
+        >>> import numpy as np
+        >>> h = np.array((0,)).reshape((1,1))
+        >>> h1 = np.array((0, -1, -1, 0)).reshape(2,2)
+        >>> energies = np.arange(-3, 3, 0.1)
+        >>> calc = TransportCalculator(h=h, h1=h1, energies=energies)
+        >>> T = calc.get_transmission()
+
+        """
+        
+        # The default values for all extra keywords
+        self.input_parameters = {'energies': None,
+                                 'h': None,
+                                 'h1': None,
+                                 'h2': None,
+                                 's': None,
+                                 's1': None,
+                                 's2': None,
+                                 'hc1': None,
+                                 'hc2': None,
+                                 'sc1': None,
+                                 'sc2': None,
+                                 'box': None,
+                                 'align_bf': None,
+                                 'eta1': 1e-5,
+                                 'eta2': 1e-5,
+                                 'eta': 1e-5,
+                                 'logfile': None, # '-',
+                                 'eigenchannels': 0,
+                                 'dos': False,
+                                 'pdos': [],
+                                 }
+        self.initialized = False # Changed Hamiltonians?
+        self.uptodate = False # Changed energy grid?
+        self.set(**kwargs)
+
+    def set(self, **kwargs):
+        for key in kwargs:
+            if key in ['h', 'h1', 'h2', 'hc1', 'hc2',
+                       's', 's1', 's2', 'sc1', 'sc2',
+                       'eta', 'eta1', 'eta2', 'align_bf', 'box']:
+                self.initialized = False
+                self.uptodate = False
+                break
+            elif key in ['energies', 'eigenchannels', 'dos', 'pdos']:
+                self.uptodate = False
+            elif key not in self.input_parameters:
+                raise KeyError, '\'%s\' not a vaild keyword' % key
+
+        self.input_parameters.update(kwargs)
+        log = self.input_parameters['logfile']
+        if log is None:
+            class Trash:
+                def write(self, s):
+                    pass
+                def flush(self):
+                    pass
+            self.log = Trash()
+        elif log == '-':
+            from sys import stdout
+            self.log = stdout
+        elif 'logfile' in kwargs:
+            self.log = open(log, 'w')
+
+    def initialize(self):
+        if self.initialized:
+            return
+
+        print >> self.log, '# Initializing calculator...'
+
+        p = self.input_parameters
+        if p['s'] == None:
+            p['s'] = np.identity(len(p['h']))
+        
+        identical_leads = False
+        if p['h2'] == None:   
+            p['h2'] = p['h1'] # Lead2 is idendical to lead1
+            identical_leads = True
+ 
+        if p['s1'] == None: 
+            p['s1'] = np.identity(len(p['h1']))
+       
+        if p['s2'] == None and not identical_leads:
+            p['s2'] = np.identity(len(p['h2'])) # Orthonormal basis for lead 2
+        else: # Lead2 is idendical to lead1
+            p['s2'] = p['s1']
+
+           
+        h_mm = p['h']
+        s_mm = p['s']
+        pl1 = len(p['h1']) / 2
+        pl2 = len(p['h2']) / 2
+        h1_ii = p['h1'][:pl1, :pl1]
+        h1_ij = p['h1'][:pl1, pl1:2 * pl1]
+        s1_ii = p['s1'][:pl1, :pl1]
+        s1_ij = p['s1'][:pl1, pl1:2 * pl1]
+        h2_ii = p['h2'][:pl2, :pl2]
+        h2_ij = p['h2'][pl2: 2 * pl2, :pl2]
+        s2_ii = p['s2'][:pl2, :pl2]
+        s2_ij = p['s2'][pl2: 2 * pl2, :pl2]
+        
+        if p['hc1'] is None:
+            nbf = len(h_mm)
+            h1_im = np.zeros((pl1, nbf), complex)
+            s1_im = np.zeros((pl1, nbf), complex)
+            h1_im[:pl1, :pl1] = h1_ij
+            s1_im[:pl1, :pl1] = s1_ij
+            p['hc1'] = h1_im
+            p['sc1'] = s1_im
+        else:
+            h1_im = p['hc1']
+            if p['sc1'] is not None:
+                s1_im = p['sc1']
+            else:
+                s1_im = np.zeros(h1_im.shape, complex)
+                p['sc1'] = s1_im
+
+        if p['hc2'] is None:
+            h2_im = np.zeros((pl2, nbf), complex)
+            s2_im = np.zeros((pl2, nbf), complex)
+            h2_im[-pl2:, -pl2:] = h2_ij
+            s2_im[-pl2:, -pl2:] = s2_ij
+            p['hc2'] = h2_im
+            p['sc2'] = s2_im
+        else:
+            h2_im = p['hc2']
+            if p['sc2'] is not None:
+                s2_im = p['sc2']
+            else:
+                s2_im = np.zeros(h2_im.shape, complex)
+                p['sc2'] = s2_im
+
+        align_bf = p['align_bf']
+        if align_bf != None:
+            diff = (h_mm[align_bf, align_bf] - h1_ii[align_bf, align_bf]) \
+                   / s_mm[align_bf, align_bf]
+            print >> self.log, '# Aligning scat. H to left lead H. diff=', diff
+            h_mm -= diff * s_mm
+
+        # Setup lead self-energies
+        # All infinitesimals must be > 0 
+        assert np.all(np.array((p['eta'], p['eta1'], p['eta2'])) > 0.0)
+        self.selfenergies = [LeadSelfEnergy((h1_ii, s1_ii), 
+                                            (h1_ij, s1_ij),
+                                            (h1_im, s1_im),
+                                            p['eta1']),
+                             LeadSelfEnergy((h2_ii, s2_ii), 
+                                            (h2_ij, s2_ij),
+                                            (h2_im, s2_im),
+                                            p['eta2'])]
+        box = p['box']
+        if box is not None:
+            print 'Using box probe!'
+            self.selfenergies.append(
+                BoxProbe(eta=box[0], a=box[1], b=box[2], energies=box[3],
+                         S=s_mm, T=0.3))
+        
+        #setup scattering green function
+        self.greenfunction = GreenFunction(selfenergies=self.selfenergies,
+                                           H=h_mm,
+                                           S=s_mm,
+                                           eta=p['eta'])
+
+        self.initialized = True
+    
+    def update(self):
+        if self.uptodate:
+            return
+        
+        p = self.input_parameters
+        self.energies = p['energies']
+        nepts = len(self.energies)
+        nchan = p['eigenchannels']
+        pdos = p['pdos']
+        self.T_e = np.empty(nepts)
+        if p['dos']:
+            self.dos_e = np.empty(nepts)
+        if pdos != []:
+            self.pdos_ne = np.empty((len(pdos), nepts))
+        if nchan > 0:
+            self.eigenchannels_ne = np.empty((nchan, nepts))
+
+        for e, energy in enumerate(self.energies):
+            Ginv_mm = self.greenfunction.retarded(energy, inverse=True)
+            lambda1_mm = self.selfenergies[0].get_lambda(energy)
+            lambda2_mm = self.selfenergies[1].get_lambda(energy)
+            a_mm = linalg.solve(Ginv_mm, lambda1_mm)
+            b_mm = linalg.solve(dagger(Ginv_mm), lambda2_mm)
+            T_mm = np.dot(a_mm, b_mm)
+            if nchan > 0:
+                t_n = linalg.eigvals(T_mm).real
+                self.eigenchannels_ne[:, e] = np.sort(t_n)[-nchan:]
+                self.T_e[e] = np.sum(t_n)
+            else:
+                self.T_e[e] = np.trace(T_mm).real
+
+            print >> self.log, energy, self.T_e[e]
+            self.log.flush()
+
+            if p['dos']:
+                self.dos_e[e] = self.greenfunction.dos(energy)
+
+            if pdos != []:
+                self.pdos_ne[:, e] = np.take(self.greenfunction.pdos(energy),
+                                             pdos)
+        
+        self.uptodate = True
+
+    def print_pl_convergence(self):
+        self.initialize()
+        pl1 = len(self.input_parameters['h1']) / 2
+        
+        h_ii = self.selfenergies[0].h_ii
+        s_ii = self.selfenergies[0].s_ii
+        ha_ii = self.greenfunction.H[:pl1, :pl1]
+        sa_ii = self.greenfunction.S[:pl1, :pl1]
+        c1 = np.abs(h_ii - ha_ii).max()
+        c2 = np.abs(s_ii - sa_ii).max()
+        print 'Conv (h,s)=%.2e, %2.e' % (c1, c2)
+
+    def plot_pl_convergence(self):
+        self.initialize()
+        pl1 = len(self.input_parameters['h1']) / 2       
+        hlead = self.selfenergies[0].h_ii.real.diagonal()
+        hprincipal = self.greenfunction.H.real.diagonal[:pl1]
+
+        import pylab as pl
+        pl.plot(hlead, label='lead')
+        pl.plot(hprincipal, label='principal layer')
+        pl.axis('tight')
+        pl.show()
+
+    def get_transmission(self):
+        self.initialize()
+        self.update()
+        return self.T_e
+
+    def get_dos(self):
+        self.initialize()
+        self.update()
+        return self.dos_e
+
+    def get_eigenchannels(self, n=None):
+        """Get ``n`` first eigenchannels."""
+        self.initialize()
+        self.update()
+        if n is None:
+            n = self.input_parameters['eigenchannels']
+        return self.eigenchannels_ne[:n]
+
+    def get_pdos(self):
+        self.initialize()
+        self.update()
+        return self.pdos_ne
+
+    def subdiagonalize_bfs(self, bfs, apply=False):
+        self.initialize()
+        bfs = np.array(bfs)
+        p = self.input_parameters
+        h_mm = p['h']
+        s_mm = p['s']
+        ht_mm, st_mm, c_mm, e_m = subdiagonalize(h_mm, s_mm, bfs)
+        if apply:
+            self.uptodate = False
+            h_mm[:] = ht_mm 
+            s_mm[:] = st_mm 
+            # Rotate coupling between lead and central region
+            for alpha, sigma in enumerate(self.selfenergies):
+                sigma.h_im[:] = np.dot(sigma.h_im, c_mm)
+                sigma.s_im[:] = np.dot(sigma.s_im, c_mm)
+        
+        c_mm = np.take(c_mm, bfs, axis=0)
+        c_mm = np.take(c_mm, bfs, axis=1)
+        return ht_mm, st_mm, e_m, c_mm
+
+    def cutcoupling_bfs(self, bfs, apply=False):
+        self.initialize()
+        bfs = np.array(bfs)
+        p = self.input_parameters
+        h_pp = p['h'].copy()
+        s_pp = p['s'].copy()
+        cutcoupling(h_pp, s_pp, bfs)
+        if apply:
+            self.uptodate = False
+            p['h'][:] = h_pp
+            p['s'][:] = s_pp
+            for alpha, sigma in enumerate(self.selfenergies):
+                for m in bfs:
+                    sigma.h_im[:, m] = 0.0
+                    sigma.s_im[:, m] = 0.0
+        return h_pp, s_pp
+
+    def lowdin_rotation(self, apply=False):
+        p = self.input_parameters
+        h_mm = p['h']
+        s_mm = p['s']
+        eig, rot_mm = linalg.eigh(s_mm)
+        eig = np.abs(eig)
+        rot_mm = np.dot(rot_mm / np.sqrt(eig), dagger(rot_mm))
+        if apply:
+            self.uptodate = False
+            h_mm[:] = rotate_matrix(h_mm, rot_mm) # rotate C region
+            s_mm[:] = rotate_matrix(s_mm, rot_mm)
+            for alpha, sigma in enumerate(self.selfenergies):
+                sigma.h_im[:] = np.dot(sigma.h_im, rot_mm) # rotate L-C coupl.
+                sigma.s_im[:] = np.dot(sigma.s_im, rot_mm)
+
+        return rot_mm
+
+    def get_left_channels(self, energy, nchan=1):
+        self.initialize()
+        g_s_ii = self.greenfunction.retarded(energy)
+        lambda_l_ii = self.selfenergies[0].get_lambda(energy)
+        lambda_r_ii = self.selfenergies[1].get_lambda(energy)
+
+        if self.greenfunction.S is None:
+            s_s_qsrt_ii = s_s_isqrt = np.identity(len(g_s_ii))
+        else:
+            s_mm = self.greenfunction.S
+            s_s_i, s_s_ii = linalg.eig(s_mm)
+            s_s_i = np.abs(s_s_i)
+            s_s_sqrt_i = np.sqrt(s_s_i) # sqrt of eigenvalues  
+            s_s_sqrt_ii = np.dot(s_s_ii * s_s_sqrt_i, dagger(s_s_ii))
+            s_s_isqrt_ii = np.dot(s_s_ii / s_s_sqrt_i, dagger(s_s_ii))
+
+        lambdab_r_ii = np.dot(np.dot(s_s_isqrt_ii, lambda_r_ii),s_s_isqrt_ii)
+        a_l_ii = np.dot(np.dot(g_s_ii, lambda_l_ii), dagger(g_s_ii))
+        ab_l_ii = np.dot(np.dot(s_s_sqrt_ii, a_l_ii), s_s_sqrt_ii)
+        lambda_i, u_ii = linalg.eig(ab_l_ii)
+        ut_ii = np.sqrt(lambda_i / (2.0 * np.pi)) * u_ii
+        m_ii = 2 * np.pi * np.dot(np.dot(dagger(ut_ii), lambdab_r_ii),ut_ii)
+        T_i,c_in = linalg.eig(m_ii)
+        T_i = np.abs(T_i)
+        
+        channels = np.argsort(-T_i)[:nchan]
+        c_in = np.take(c_in, channels, axis=1)
+        T_n = np.take(T_i, channels)
+        v_in = np.dot(np.dot(s_s_isqrt_ii, ut_ii), c_in)
+
+        return T_n, v_in
diff --git a/ase/transport/greenfunction.py b/ase/transport/greenfunction.py
new file mode 100644
index 0000000..517eea5
--- /dev/null
+++ b/ase/transport/greenfunction.py
@@ -0,0 +1,66 @@
+import numpy as np
+
+class GreenFunction:
+    """Equilibrium retarded Green function."""
+    
+    def __init__(self, H, S=None, selfenergies=[], eta=1e-4):
+        self.H = H
+        self.S = S
+        self.selfenergies = selfenergies
+        self.eta = eta
+        self.energy = None
+        self.Ginv = np.empty(H.shape, complex)
+
+    def retarded(self, energy, inverse=False):
+        """Get retarded Green function at specified energy.
+
+        If 'inverse' is True, the inverse Green function is returned (faster).
+        """
+        if energy != self.energy:
+            self.energy = energy
+            z = energy + self.eta * 1.j
+
+            if self.S is None:
+                self.Ginv[:] = 0.0
+                self.Ginv.flat[:: len(self.S) + 1] = z
+            else:
+                self.Ginv[:] = z
+                self.Ginv *= self.S
+            self.Ginv -= self.H
+
+            for selfenergy in self.selfenergies:
+                self.Ginv -= selfenergy.retarded(energy)
+
+        if inverse:
+            return self.Ginv
+        else:
+            return np.linalg.inv(self.Ginv)
+
+    def calculate(self, energy, sigma):
+        """XXX is this really needed"""
+        ginv = energy * self.S - self.H - sigma 
+        return np.linalg.inv(ginv)
+
+    def apply_retarded(self, energy, X):
+        """Apply retarded Green function to X.
+        
+        Returns the matrix product G^r(e) . X
+        """
+        return np.linalg.solve(self.retarded(energy, inverse=True), X)
+
+    def dos(self, energy):
+        """Total density of states -1/pi Im(Tr(GS))"""
+        if self.S is None:
+            return -self(energy).imag.trace() / np.pi
+        else:
+            GS = self.apply_retarded(energy, self.S)
+            return -GS.imag.trace() / np.pi
+        
+    def pdos(self, energy):
+        """Projected density of states -1/pi Im(SGS/S)"""
+        if self.S is None:
+            return -self.retarded(energy).imag.diagonal() / np.pi
+        else:
+            S = self.S
+            SGS = np.dot(S, self.apply_retarded(energy, S))
+            return -(SGS.diagonal() / S.diagonal()).imag / np.pi
diff --git a/ase/transport/selfenergy.py b/ase/transport/selfenergy.py
new file mode 100644
index 0000000..77e196e
--- /dev/null
+++ b/ase/transport/selfenergy.py
@@ -0,0 +1,83 @@
+import numpy as np
+
+
+class LeadSelfEnergy:
+    conv = 1e-8 # Convergence criteria for surface Green function
+    
+    def __init__(self, hs_dii, hs_dij, hs_dim, eta=1e-4):
+        self.h_ii, self.s_ii = hs_dii # onsite principal layer
+        self.h_ij, self.s_ij = hs_dij # coupling between principal layers
+        self.h_im, self.s_im = hs_dim # coupling to the central region
+        self.nbf = self.h_im.shape[1] # nbf for the scattering region
+        self.eta = eta
+        self.energy = None
+        self.bias = 0
+        self.sigma_mm = np.empty((self.nbf, self.nbf), complex)
+    
+    def retarded(self, energy):
+        """Return self-energy (sigma) evaluated at specified energy."""
+        if energy != self.energy:
+            self.energy = energy
+            z = energy - self.bias + self.eta * 1.j           
+            tau_im = z * self.s_im - self.h_im
+            a_im = np.linalg.solve(self.get_sgfinv(energy), tau_im)
+            tau_mi = z * self.s_im.T.conj() - self.h_im.T.conj()
+            self.sigma_mm[:] = np.dot(tau_mi, a_im)
+
+        return self.sigma_mm
+
+    def set_bias(self, bias):
+        self.bias = bias
+
+    def get_lambda(self, energy):
+        """Return the lambda (aka Gamma) defined by i(S-S^d).
+
+        Here S is the retarded selfenergy, and d denotes the hermitian
+        conjugate.
+        """
+        sigma_mm = self.retarded(energy)
+        return 1.j * (sigma_mm - sigma_mm.T.conj())
+        
+    def get_sgfinv(self, energy):
+        """The inverse of the retarded surface Green function""" 
+        z = energy - self.bias + self.eta * 1.j
+        
+        v_00 = z * self.s_ii.T.conj() - self.h_ii.T.conj()
+        v_11 = v_00.copy()
+        v_10 = z * self.s_ij - self.h_ij
+        v_01 = z * self.s_ij.T.conj() - self.h_ij.T.conj()
+
+        delta = self.conv + 1
+        while delta > self.conv:
+            a = np.linalg.solve(v_11, v_01)
+            b = np.linalg.solve(v_11, v_10)
+            v_01_dot_b = np.dot(v_01, b)
+            v_00 -= v_01_dot_b
+            v_11 -= np.dot(v_10, a) 
+            v_11 -= v_01_dot_b
+            v_01 = -np.dot(v_01, a)
+            v_10 = -np.dot(v_10, b)
+            delta = abs(v_01).max()
+
+        return v_00
+
+
+class BoxProbe:
+    """Box shaped Buttinger probe.
+    
+    Kramers-kroning: real = H(imag); imag = -H(real)
+    """
+    def __init__(self, eta, a, b, energies, S, T=0.3):
+        from Transport.Hilbert import hilbert
+        se = np.empty(len(energies), complex)
+        se.imag = .5 * (np.tanh(.5 * (energies - a) / T) -
+                        np.tanh(.5 * (energies - b) / T))
+        se.real = hilbert(se.imag)
+        se.imag -= 1
+        self.selfenergy_e = eta * se
+        self.energies = energies
+        self.S = S
+    
+    def retarded(self, energy):
+        return self.selfenergy_e[self.energies.searchsorted(energy)] * self.S
+        
diff --git a/ase/transport/stm.py b/ase/transport/stm.py
new file mode 100644
index 0000000..fda25cb
--- /dev/null
+++ b/ase/transport/stm.py
@@ -0,0 +1,212 @@
+import time
+
+import numpy as np
+
+from ase.transport.tools import dagger
+from ase.transport.selfenergy import LeadSelfEnergy
+from ase.transport.greenfunction import GreenFunction
+from ase.parallel import world
+
+
+class STM:
+    def __init__(self, h1, s1, h2, s2 ,h10, s10, h20, s20, eta1, eta2, w=0.5, pdos=[], logfile = None):
+        """XXX
+        
+        1. Tip
+        2. Surface
+        
+        h1: ndarray
+            Hamiltonian and overlap matrix for the isolated tip
+            calculation.  Note, h1 should contain (at least) one
+            principal layer.
+
+        h2: ndarray
+            Same as h1 but for the surface.
+
+        h10: ndarray
+            periodic part of the tip. must include two and only
+            two principal layers.
+
+        h20: ndarray
+            same as h10, but for the surface
+
+        The s* are the corresponding overlap matrices.  eta1, and eta
+        2 are (finite) infinitesimals.  """
+        
+        self.pl1 = len(h10) // 2 #principal layer size for the tip
+        self.pl2 = len(h20) // 2 #principal layer size for the surface
+        self.h1 = h1 
+        self.s1 = s1
+        self.h2 = h2
+        self.s2 = s2
+        self.h10 = h10 
+        self.s10 = s10 
+        self.h20 = h20 
+        self.s20 = s20
+        self.eta1 = eta1
+        self.eta2 = eta2
+        self.w = w #asymmetry of the applied bias (0.5=>symmetric)
+        self.pdos = []
+        self.log = logfile
+
+    def initialize(self, energies, bias=0):
+        """
+            energies: list of energies 
+            for which the transmission function should be evaluated.
+            bias.
+            Will precalculate the surface greenfunctions of the tip and
+            surface.
+        """
+        self.bias = bias
+        self.energies = energies
+        nenergies = len(energies)
+        pl1, pl2 = self.pl1, self.pl2
+        nbf1, nbf2 = len(self.h1), len(self.h2)
+      
+        #periodic part of the tip
+        hs1_dii = self.h10[:pl1, :pl1], self.s10[:pl1, :pl1]
+        hs1_dij = self.h10[:pl1, pl1:2*pl1], self.s10[:pl1, pl1:2*pl1]
+        #coupling betwen per. and non. per part of the tip
+        h1_im = np.zeros((pl1, nbf1), complex) 
+        s1_im = np.zeros((pl1, nbf1), complex)
+        h1_im[:pl1, :pl1], s1_im[:pl1, :pl1] = hs1_dij
+        hs1_dim = [h1_im, s1_im]
+
+        #periodic part the surface 
+        hs2_dii = self.h20[:pl2, :pl2], self.s20[:pl2, :pl2]
+        hs2_dij = self.h20[pl2:2*pl2, :pl2], self.s20[pl2:2*pl2, :pl2]
+        #coupling betwen per. and non. per part of the surface
+        h2_im = np.zeros((pl2, nbf2), complex)
+        s2_im = np.zeros((pl2, nbf2), complex) 
+        h2_im[-pl2:, -pl2:], s2_im[-pl2:, -pl2:] = hs2_dij
+        hs2_dim = [h2_im, s2_im]
+
+        #tip and surface greenfunction 
+        self.selfenergy1 = LeadSelfEnergy(hs1_dii, hs1_dij, hs1_dim, self.eta1)
+        self.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim, self.eta2)
+        self.greenfunction1 = GreenFunction(self.h1-self.bias*self.w*self.s1, self.s1, 
+                                            [self.selfenergy1], self.eta1)
+        self.greenfunction2 = GreenFunction(self.h2-self.bias*(self.w-1)*self.s2, self.s2, 
+                                            [self.selfenergy2], self.eta2)
+        
+        #Shift the bands due to the bias.
+        bias_shift1 = -bias * self.w
+        bias_shift2 = -bias * (self.w - 1)
+        self.selfenergy1.set_bias(bias_shift1)
+        self.selfenergy2.set_bias(bias_shift2)
+        
+        #tip and surface greenfunction matrices.
+        nbf1_small = nbf1 #XXX Change this for efficiency in the future
+        nbf2_small = nbf2 #XXX -||-
+        coupling_list1 = range(nbf1_small)# XXX -||-
+        coupling_list2 = range(nbf2_small)# XXX -||-
+        self.gft1_emm = np.zeros((nenergies, nbf1_small, nbf1_small), complex) 
+        self.gft2_emm = np.zeros((nenergies, nbf2_small, nbf2_small), complex)
+ 
+        for e, energy in enumerate(self.energies):
+            if self.log != None: # and world.rank == 0:
+                    T = time.localtime()
+                    self.log.write(' %d:%02d:%02d, ' % (T[3], T[4], T[5]) +
+                                   '%d, %d, %02f\n' % (world.rank, e, energy))
+            gft1_mm = self.greenfunction1.retarded(energy)[coupling_list1]
+            gft1_mm = np.take(gft1_mm, coupling_list1, axis=1)
+
+            gft2_mm = self.greenfunction2.retarded(energy)[coupling_list2]
+            gft2_mm = np.take(gft2_mm, coupling_list2, axis=1)
+ 
+            self.gft1_emm[e] = gft1_mm
+            self.gft2_emm[e] = gft2_mm
+
+            if self.log != None and world.rank == 0:
+                self.log.flush()
+
+    def get_transmission(self, v_12, v_11_2=None, v_22_1=None):
+        """XXX
+
+        v_12:
+            coupling between tip and surface 
+        v_11_2:
+            correction to "on-site" tip elements due to the 
+            surface (eq.16). Is only included to first order.
+        v_22_1:
+            corretion to "on-site" surface elements due to he
+            tip (eq.17). Is only included to first order.
+        """
+
+        dim0 = v_12.shape[0]
+        dim1 = v_12.shape[1]
+
+        nenergies = len(self.energies)
+        T_e = np.empty(nenergies,float)
+        v_21 = dagger(v_12)
+        for e, energy in enumerate(self.energies):
+            gft1 = self.gft1_emm[e]
+            if v_11_2!=None:
+                gf1 = np.dot(v_11_2, np.dot(gft1, v_11_2)) 
+                gf1 += gft1 #eq. 16
+            else:
+                gf1 = gft1
+            
+            gft2 = self.gft2_emm[e]
+            if v_22_1!=None:
+                gf2 = np.dot(v_22_1,np.dot(gft2, v_22_1))
+                gf2 += gft2 #eq. 17
+            else:
+                gf2 = gft2
+            
+            a1 = (gf1 - dagger(gf1))
+            a2 = (gf2 - dagger(gf2))
+            self.v_12 = v_12
+            self.a2 = a2
+            self.v_21 = v_21
+            self.a1 = a1
+            v12_a2 = np.dot(v_12, a2[:dim1])
+            v21_a1 = np.dot(v_21, a1[-dim0:])
+            self.v12_a2 = v12_a2
+            self.v21_a1 = v21_a1
+            T = -np.trace(np.dot(v12_a2[:,:dim1], v21_a1[:,-dim0:])) #eq. 11
+            assert abs(T.imag).max() < 1e-14
+            T_e[e] = T.real
+            self.T_e = T_e
+        return T_e
+
+
+    def get_current(self, bias, v_12, v_11_2=None, v_22_1=None):
+        """Very simple function to calculate the current.
+        
+        Asummes zero temperature.
+
+        bias: type? XXX
+            bias voltage (V)
+            
+        v_12: XXX
+            coupling between tip and surface.
+            
+        v_11_2:
+            correction to onsite elements of the tip
+            due to the potential of the surface.
+        v_22_1:
+            correction to onsite elements of the surface
+            due to the potential of the tip.
+        """
+        energies = self.energies
+        T_e = self.get_transmission(v_12, v_11_2, v_22_1)
+        bias_window = -np.array([bias * self.w, bias * (self.w - 1)])
+        bias_window.sort()
+        self.bias_window = bias_window
+        #print 'bias window', np.around(bias_window,3)
+        #print 'Shift of tip lead do to the bias:', self.selfenergy1.bias
+        #print 'Shift of surface lead do to the bias:', self.selfenergy2.bias
+        i1 = sum(energies < bias_window[0]) 
+        i2 = sum(energies < bias_window[1])
+        step = 1 
+        if i2 < i1:
+            step = -1
+        
+        return np.sign(bias)*np.trapz(x=energies[i1:i2:step], y=T_e[i1:i2:step])
+
+
+
+
+
+
diff --git a/ase/transport/stm_test.py b/ase/transport/stm_test.py
new file mode 100644
index 0000000..bff15ea
--- /dev/null
+++ b/ase/transport/stm_test.py
@@ -0,0 +1,94 @@
+import numpy as np
+import pylab
+import ase.transport.stm as stm
+
+
+#           Parameters for a simple model.
+#
+#                           
+#                         * eps_a
+#                 v_ts /   \ v_a2 
+#  ... *  *  *  *            *  *  *  *  * ...
+#       \/                          \/
+#       t1                          t2
+#
+#       Tip                      Surface
+# ----------------|      |-----------------------
+t1 = -1.0
+t2 = -2.0
+eps_a = 0.4
+
+v_ts  = 0.05
+v_a2 = 1.0
+
+#Tip
+h1 = np.zeros([2, 2])
+h1[0, 1] = t1
+h1[1, 0] = t1
+s1 = np.identity(2)
+
+h10 = np.zeros([2,2])
+h10[0, 1] = t1
+h10[1, 0] = t1
+s10 = np.identity(2)
+
+
+#Surface with "molecule" a.
+h2 = np.zeros([2,2])
+h2[0, 1] = v_a2 
+h2[1, 0] = v_a2
+h1[0, 0] = eps_a
+s2 = np.identity(2)
+
+h20 = np.zeros([2,2])
+h20[0, 1] = t2
+h20[1, 0] = t2
+s20 = np.identity(2)
+
+
+#Tip Surface coupling
+V_ts = np.zeros([2,2])
+V_ts[1, 0] = v_ts
+
+eta1 = 0.0001
+eta2 = 0.0001
+
+stm = reload(stm)
+stm_calc = stm.STM(h1, s1, h2, s2, h10, s10, h20, s20, eta1, eta2)
+energies = np.arange(-3.0, 3.0, 0.01)
+stm_calc.initialize(energies)
+
+
+T_stm = stm_calc.get_transmission(V_ts)
+
+
+#Perform the full calculation and compare
+from ase.transport.calculators import TransportCalculator as TC
+
+h = np.zeros([4,4])
+h[:2, :2] = h1
+h[-2:, -2:] = h2
+h[:2, -2:] = V_ts
+h[-2:, :2] = V_ts.T
+
+
+tc = TC(energies=energies,
+        h=h,
+        h1=h10,
+        h2=h20,
+        eta=eta1, eta1=eta1, eta2=eta2)
+        
+T_full = tc.get_transmission()
+
+pylab.plot(stm_calc.energies, T_stm, 'b')
+pylab.plot(tc.energies, T_full, 'r--')
+pylab.show()
+
+
+#bias stuff
+biass = np.arange(-2.0, 2.0, 0.2)
+Is = [stm_calc.get_current(bias, V_ts) for bias in biass]
+pylab.plot(biass, Is, '+')
+pylab.show()
+
+
diff --git a/ase/transport/test_transport_calulator.py b/ase/transport/test_transport_calulator.py
new file mode 100644
index 0000000..d1dd23a
--- /dev/null
+++ b/ase/transport/test_transport_calulator.py
@@ -0,0 +1,85 @@
+from ase.transport.calculators import TransportCalculator
+import numpy as np
+
+#Aux. function to write data to a text file.
+def write(fname,xs,ys):
+    fd = open(fname,'w')
+    for x,y in zip(xs,ys):
+        print >> fd, x, y
+    fd.close()
+
+H_lead = np.zeros([4,4])
+
+# On-site energies are zero
+for i in range(4):
+    H_lead[i,i] = 0.0
+
+# Nearest neighbor hopping is -1.0
+for i in range(3):
+    H_lead[i,i+1] = -1.0
+    H_lead[i+1,i] = -1.0
+
+# Next-nearest neighbor hopping is 0.2
+for i in range(2):
+    H_lead[i,i+2] = 0.2
+    H_lead[i+2,i] = 0.2
+
+H_scat = np.zeros([6,6])
+# Principal layers on either side of S
+H_scat[:2,:2] = H_lead[:2,:2]
+H_scat[-2:,-2:] = H_lead[:2,:2]
+
+# Scattering region
+H_scat[2,2] = 0.0
+H_scat[3,3] = 0.0
+H_scat[2,3] = -0.8
+H_scat[3,2] = -0.8
+
+# External coupling
+H_scat[1,2] = 0.2
+H_scat[2,1] = 0.2
+H_scat[3,4] = 0.2
+H_scat[4,3] = 0.2
+
+energies = np.arange(-3,3,0.02)
+tcalc = TransportCalculator(h=H_scat,
+                            h1=H_lead,
+                            eta=0.02,
+                            energies=energies)
+
+T = tcalc.get_transmission()
+tcalc.set(pdos=[2, 3])
+pdos = tcalc.get_pdos()
+
+tcalc.set(dos=True)
+dos = tcalc.get_dos()
+
+write('T.dat',tcalc.energies,T)
+write('pdos0.dat', tcalc.energies,pdos[0])
+write('pdos1.dat', tcalc.energies,pdos[1])
+
+#subdiagonalize
+h_rot, s_rot, eps, u = tcalc.subdiagonalize_bfs([2, 3], apply=True)
+T_rot = tcalc.get_transmission()
+dos_rot = tcalc.get_dos()
+pdos_rot = tcalc.get_pdos()
+
+write('T_rot.dat', tcalc.energies,T_rot)
+write('pdos0_rot.dat', tcalc.energies, pdos_rot[0])
+write('pdos1_rot.dat', tcalc.energies, pdos_rot[1])
+
+print 'Subspace eigenvalues:', eps
+assert sum(abs(eps-(-0.8, 0.8))) < 2.0e-15, 'Subdiagonalization. error'
+print 'Max deviation of T after the rotation:', np.abs(T-T_rot).max()
+assert max(abs(T-T_rot)) < 2.0e-15, 'Subdiagonalization. error'
+
+#remove coupling
+h_cut, s_cut = tcalc.cutcoupling_bfs([2], apply=True)
+T_cut = tcalc.get_transmission()
+dos_cut = tcalc.get_dos()
+pdos_cut = tcalc.get_pdos()
+
+write('T_cut.dat', tcalc.energies, T_cut)
+write('pdos0_cut.dat', tcalc.energies,pdos_cut[0])
+write('pdos1_cut.dat', tcalc.energies,pdos_cut[1])
+
diff --git a/ase/transport/tools.py b/ase/transport/tools.py
new file mode 100644
index 0000000..f8fda7b
--- /dev/null
+++ b/ase/transport/tools.py
@@ -0,0 +1,432 @@
+import numpy as np
+from math import sqrt, exp
+
+def tri2full(H_nn, UL='L'):
+    """Fill in values of hermitian matrix.
+
+    Fill values in lower or upper triangle of H_nn based on the opposite
+    triangle, such that the resulting matrix is symmetric/hermitian.
+
+    UL='U' will copy (conjugated) values from upper triangle into the
+    lower triangle.
+
+    UL='L' will copy (conjugated) values from lower triangle into the
+    upper triangle.
+    """
+    N, tmp = H_nn.shape
+    assert N == tmp, 'Matrix must be square'
+    #assert np.isreal(H_nn.diagonal()).all(), 'Diagonal should be real'
+    if UL != 'L':
+        H_nn = H_nn.T
+
+    for n in range(N - 1):
+        H_nn[n, n + 1:] = H_nn[n + 1:, n].conj()
+
+def dagger(matrix):
+    return np.conj(matrix.T)
+
+def rotate_matrix(h, u):
+    return np.dot(u.T.conj(), np.dot(h, u))
+
+def get_subspace(matrix, index):
+    """Get the subspace spanned by the basis function listed in index"""
+    assert matrix.ndim == 2 and matrix.shape[0] == matrix.shape[1]
+    return matrix.take(index, 0).take(index, 1)   
+
+permute_matrix = get_subspace
+
+def normalize(matrix, S=None):
+    """Normalize column vectors.
+
+    ::
+
+      <matrix[:,i]| S |matrix[:,i]> = 1
+
+    """
+    for col in matrix.T:
+        if S is None:
+            col /= np.linalg.norm(col)
+        else:
+            col /= np.sqrt(np.dot(col.conj(), np.dot(S, col)))
+
+def subdiagonalize(h_ii, s_ii, index_j):
+    nb = h_ii.shape[0]
+    nb_sub = len(index_j)
+    h_sub_jj = get_subspace(h_ii, index_j)
+    s_sub_jj = get_subspace(s_ii, index_j)
+    e_j, v_jj = np.linalg.eig(np.linalg.solve(s_sub_jj, h_sub_jj))
+    normalize(v_jj, s_sub_jj) # normalize: <v_j|s|v_j> = 1
+    permute_list = np.argsort(e_j.real)
+    e_j = np.take(e_j, permute_list)
+    v_jj = np.take(v_jj, permute_list, axis=1)
+    
+    #setup transformation matrix
+    c_ii = np.identity(nb, complex)
+    for i in xrange(nb_sub):
+        for j in xrange(nb_sub):
+            c_ii[index_j[i], index_j[j]] = v_jj[i, j]
+
+    h1_ii = rotate_matrix(h_ii, c_ii)
+    s1_ii = rotate_matrix(s_ii, c_ii)
+
+    return h1_ii, s1_ii, c_ii, e_j
+
+def cutcoupling(h, s, index_n):
+    for i in index_n:
+        s[:, i] = 0.0
+        s[i, :] = 0.0
+        s[i, i] = 1.0
+        Ei = h[i, i]
+        h[:, i] = 0.0
+        h[i, :] = 0.0
+        h[i, i] = Ei
+
+def fermidistribution(energy, kt):
+    #fermi level is fixed to zero
+    return 1.0 / (1.0 + np.exp(energy / kt) )
+
+def fliplr(a):
+    length=len(a)
+    b = [0] * length
+    for i in range(length):
+        b[i] = a[length - i - 1]
+    return b
+
+def plot_path(energy):
+    import pylab
+    pylab.plot(np.real(energy), np.imag(energy), 'b--o')
+    pylab.show()
+    
+
+def function_integral(function, calcutype):
+    #return the integral of the 'function' on 'intrange'    
+    #the function can be a value or a matrix, arg1,arg2 are the possible
+    #parameters of the function
+    
+    intctrl = function.intctrl
+    if calcutype == 'eqInt':
+        intrange = intctrl.eqintpath
+        tol = intctrl.eqinttol
+        if hasattr(function.intctrl, 'eqpath_radius'):
+            radius = function.intctrl.eqpath_radius
+        else:
+            radius = -1
+        if hasattr(function.intctrl, 'eqpath_origin'):
+            origin = function.intctrl.eqpath_origin
+        else:
+            origin = 1000
+    elif calcutype == 'neInt':
+        intrange = intctrl.neintpath
+        tol = intctrl.neinttol
+        radius = -1
+        origin = 1000
+    elif calcutype == 'locInt':
+        intrange = intctrl.locintpath
+        tol = intctrl.locinttol
+        if hasattr(function.intctrl, 'locpath_radius'):
+            radius = function.intctrl.locpath_radius
+        else:
+            radius = -1
+        if hasattr(function.intctrl, 'locpath_origin'):
+            origin = function.intctrl.locpath_origin
+        else:
+            origin = 1000
+    trace = 0    
+    a = 0.
+    b = 1.
+
+    #Initialize with 13 function evaluations.
+    c = (a + b) / 2
+    h = (b - a) / 2
+    realmin = 2e-17
+
+    s = [.942882415695480, sqrt(2.0/3),
+         .641853342345781, 1/sqrt(5.0), .236383199662150]
+    s1 = [0] * len(s)
+    s2 = [0] * len(s)
+    for i in range(len(s)):
+        s1[i] = c - s[i] * h
+        s2[i] = c + fliplr(s)[i] * h
+    x0 = [a] + s1 + [c] + s2 + [b]
+
+    s0 = [.0158271919734802, .094273840218850, .155071987336585,
+          .188821573960182,  .199773405226859, .224926465333340]
+    w0 = s0 + [.242611071901408] + fliplr(s0)
+    w1 = [1, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 1]
+    w2 = [77, 0, 432, 0, 625, 0, 672, 0, 625, 0, 432, 0, 77]
+    for i in range(len(w1)):
+        w1[i] = w1[i] / 6.0
+        w2[i] = w2[i] / 1470.0
+                                                        
+    dZ = [intrange[:len(intrange) - 1], intrange[1:]]
+    hmin = [0] * len(dZ[1])
+
+    path_type = []
+    for i in range(len(intrange) - 1):
+        rs = np.abs(dZ[0][i] - origin)
+        re = np.abs(dZ[1][i] - origin)
+        if abs(rs - radius) < 1.0e-8 and abs(re - radius) < 1.0e-8:
+            path_type.append('half_circle')
+        else:
+            path_type.append('line')
+   
+    for i in range(len(dZ[1])):
+        if path_type[i] == 'half_circle':
+            dZ[0][i] = 0
+            dZ[1][i] = np.pi
+    for i in range(len(dZ[1])):
+        dZ[1][i] = dZ[1][i] - dZ[0][i]
+        hmin[i] = realmin / 1024 * abs(dZ[1][i])
+
+
+    temp = np.array([[1] * 13, x0]).transpose()
+    
+    Zx = np.dot(temp, np.array(dZ))
+      
+    Zxx = []
+    for i in range(len(intrange) - 1):
+        for j in range(13):
+            Zxx.append(Zx[j][i])
+
+    ns = 0
+    ne = 12
+    if path_type[0] == 'line':
+        yns = function.calgfunc(Zxx[ns], calcutype)
+    elif path_type[0] == 'half_circle':
+        energy = origin + radius * np.exp((np.pi - Zxx[ns + i]) * 1.j)
+        yns = -1.j * radius * np.exp(-1.j* Zxx[ns +i])* function.calgfunc(energy, calcutype)        
+    fcnt = 0
+    
+
+    for n in range(len(intrange)-1):
+        # below evaluate the integral and adjust the tolerance
+        Q1pQ0 = yns * (w1[0] - w0[0])
+        Q2pQ0 = yns * (w2[0] - w0[0])
+        fcnt = fcnt + 12
+        for i in range(1,12):
+            if path_type[n] == 'line':
+                yne = function.calgfunc(Zxx[ns + i], calcutype)
+            elif path_type[n] == 'half_circle':
+                energy = origin + radius * np.exp((np.pi -Zxx[ns + i]) * 1.j)
+                yne = -1.j * radius * np.exp(-1.j * Zxx[ns + i])* function.calgfunc(energy, calcutype)
+            Q1pQ0 += yne * (w1[i] - w0[i])
+            Q2pQ0 += yne * (w2[i] - w0[i])
+
+        # Increase the tolerance if refinement appears to be effective
+        r = np.abs(Q2pQ0) / (np.abs(Q1pQ0) + np.abs(realmin))
+        dim = np.product(r.shape)
+        r = np.sum(r) / dim
+        if r > 0 and r < 1:
+            thistol = tol / r
+        else:
+            thistol = tol
+        if path_type[n] == 'line':
+            yne = function.calgfunc(Zxx[ne], calcutype)
+        elif path_type[n] == 'half_circle':
+            energy = origin + radius * np.exp((np.pi -Zxx[ne]) * 1.j)
+            yne = -1.j * radius * np.exp(-1.j * Zxx[ne])* function.calgfunc(energy, calcutype)
+        #Call the recursive core integrator
+       
+        Qk, xpk, wpk, fcnt, warn = quadlstep(function, Zxx[ns],
+                                            Zxx[ne], yns, yne,
+                                            thistol, trace, fcnt,
+                                            hmin[n], calcutype, path_type[n],
+                                            origin, radius)
+        if n == 0:
+            Q = np.copy(Qk)
+            Xp = xpk[:]
+            Wp = wpk[:]
+        else:
+            Q += Qk
+            Xp = Xp[:-1] + xpk
+            Wp = Wp[:-1] + [Wp[-1] + wpk[0]] + wpk[1:]
+        if warn == 1:
+            print 'warning: Minimum step size reached,singularity possible'
+        elif warn == 2:
+            print 'warning: Maximum function count excced; singularity likely'
+        elif warn == 3:
+            print 'warning: Infinite or Not-a-Number function value encountered'
+        else:
+            pass
+        
+        ns += 13
+        ne += 13
+        yns = np.copy(yne)
+      
+    return Q,Xp,Wp,fcnt
+
+def quadlstep(f, Za, Zb, fa, fb, tol, trace, fcnt, hmin, calcutype,
+                                                   path_type, origin, radius):
+    #Gaussian-Lobatto and Kronrod method
+    #QUADLSTEP Recursive core routine for integral
+    #input parameters:
+    #      f      ----------   function, here we just use the module calgfunc
+    #                          to return the value, if wanna use it for
+    #                          another one, change it
+    #     Za, Zb  ----------   the start and end point of the integral
+    #     fa, fb  ----------   the function value on Za and Zb
+    #     fcnt    ----------   the number of the funtion recalled till now
+    #output parameters:
+    #      Q      ----------   integral
+    #     Xp      ----------   selected points
+    #     Wp      ----------   weight
+    #    fcnt     ----------   the number of the function recalled till now
+
+    maxfcnt = 10000
+
+    # Evaluate integrand five times in interior of subintrval [a,b]
+    Zh = (Zb - Za) / 2.0
+    if abs(Zh) < hmin:
+        # Minimun step size reached; singularity possible
+        Q = Zh * (fa + fb)
+        if path_type == 'line':
+            Xp = [Za, Zb]
+        elif path_type == 'half_circle':
+            Xp = [origin + radius * np.exp((np.pi - Za) * 1.j),
+                                      origin + radius * np.exp((np.pi - Zb) * 1.j)]
+        Wp = [Zh, Zh]
+        warn = 1
+        return Q, Xp, Wp, fcnt, warn
+    fcnt += 5
+    if fcnt > maxfcnt:
+        #Maximum function count exceed; singularity likely
+        Q = Zh * (fa + fb)
+        if path_type == 'line':
+            Xp = [Za, Zb]
+        elif path_type == 'half_circle':
+            Xp = [origin + radius * np.exp((np.pi - Za) * 1.j),
+                                      origin + radius * np.exp((np.pi - Zb) * 1.j)]
+        Wp = [Zh, Zh]
+        warn = 2
+        return Q, Xp, Wp, fcnt, warn
+    x = [0.18350341907227,   0.55278640450004,   1.0,
+         1.44721359549996,   1.81649658092773];
+    Zx = [0] * len(x)
+    y = [0] * len(x)
+    for i in range(len(x)):
+        x[i] *= 0.5
+        Zx[i] = Za + (Zb - Za) * x[i]
+        if path_type == 'line':
+            y[i] = f.calgfunc(Zx[i], calcutype)
+        elif path_type == 'half_circle':
+            energy = origin + radius * np.exp((np.pi - Zx[i]) * 1.j)
+            y[i] = f.calgfunc(energy, calcutype)
+    #Four point Lobatto quadrature
+    s1 = [1.0, 0.0, 5.0, 0.0, 5.0, 0.0, 1.0]
+    s2 = [77.0, 432.0, 625.0, 672.0, 625.0, 432.0, 77.0]
+    Wk = [0] * 7
+    Wp = [0] * 7
+    for i in range(7):
+        Wk[i] = (Zh / 6.0) * s1[i]
+        Wp[i] = (Zh / 1470.0) * s2[i]
+    if path_type == 'line':
+        Xp = [Za] + Zx + [Zb]
+    elif path_type == 'half_circle':
+        Xp = [Za] + Zx + [Zb] 
+        for i in range(7):
+            factor = -1.j * radius * np.exp(1.j * (np.pi - Xp[i]))
+            Wk[i] *= factor
+            Wp[i] *= factor
+            Xp[i] = origin + radius * np.exp((np.pi - Xp[i]) * 1.j)
+    Qk = fa * Wk[0] + fb * Wk[6]
+    Q = fa * Wp[0] + fb * Wp[6]
+    for i in range(1, 6):
+        Qk += y[i-1] * Wk[i]
+        Q  += y[i-1] * Wp[i]
+    if np.isinf(np.max(np.abs(Q))):
+        Q = Zh * (fa + fb)
+        if path_type == 'line':
+            Xp = [Za, Zb]
+        elif path_type == 'half_circle':
+            Xp = [origin + radius * np.exp((np.pi - Za) * 1.j),
+                                      origin + radius * np.exp((np.pi - Zb) * 1.j)]
+        Wp = [Zh, Zh]
+        warn = 3
+        return Qk, Xp, Wp, fcnt, warn
+    else:
+        pass
+    if trace:
+        print fcnt, real(Za), imag(Za), abs(Zh)
+    #Check accurancy of integral over this subinterval
+    XXk = [Xp[0], Xp[2], Xp[4], Xp[6]]
+    WWk = [Wk[0], Wk[2], Wk[4], Wk[6]]
+    YYk = [fa, y[1], y[3], fb]
+    if np.max(np.abs(Qk - Q)) <= tol:
+        warn = 0
+        return Q, XXk, WWk, fcnt, warn
+    #Subdivide into six subintevals
+    else:
+        Q, Xk, Wk, fcnt, warn = quadlstep(f, Za, Zx[1], fa, YYk[1],
+                                           tol, trace, fcnt, hmin,
+                                               calcutype, path_type,
+                                                origin, radius)
+
+        Qk, xkk, wkk, fcnt, warnk = quadlstep(f, Zx[1],
+                          Zx[3], YYk[1], YYk[2], tol, trace, fcnt, hmin,
+                                             calcutype, path_type,
+                                             origin, radius)
+        Q += Qk
+        Xk = Xk[:-1] + xkk
+        Wk = Wk[:-1] + [Wk[-1] + wkk[0]] + wkk[1:]
+        warn = max(warn, warnk)
+        
+        Qk, xkk, wkk, fcnt, warnk = quadlstep(f, Zx[3], Zb, YYk[2], fb,
+                                           tol, trace, fcnt, hmin,
+                                                   calcutype, path_type,
+                                                   origin, radius)
+        Q += Qk
+        Xk = Xk[:-1] + xkk
+        Wk = Wk[:-1] + [Wk[-1] + wkk[0]] + wkk[1:]
+        warn = max(warn, warnk)
+    return Q, Xk, Wk, fcnt, warn
+
+def mytextread0(filename):
+    num = 0
+    df = file(filename)
+    df.seek(0)
+    for line in df:
+        if num == 0:
+            dim = line.strip().split(' ')
+            row = int(dim[0])
+            col = int(dim[1])
+            mat = np.empty([row, col])
+        else:
+            data = line.strip().split(' ')
+            if len(data) == 0 or len(data)== 1:
+                break
+            else:
+                for i in range(len(data)):
+                    mat[num - 1, i] = float(data[i])
+        num += 1
+    return mat
+
+def mytextread1(filename):
+    num = 0
+    df = file(filename)
+    df.seek(0)
+    data = []
+    for line in df:
+        tmp = line.strip()
+        if len(tmp) != 0: 
+            data.append(float(tmp))
+        else:
+            break
+    dim = int(sqrt(len(data)))
+    mat = np.empty([dim, dim])
+    for i in range(dim):
+        for j in range(dim):
+            mat[i, j] = data[num]
+            num += 1
+    return mat
+
+def mytextwrite1(filename, mat):
+    num = 0
+    df = open(filename,'w')
+    df.seek(0)
+    dim = mat.shape[0]
+    if dim != mat.shape[1]:
+        print 'matwirte, matrix is not square'
+    for i in range(dim):
+        for j in range(dim):
+            df.write('%20.20e\n'% mat[i, j])
+    df.close()
diff --git a/ase/units.py b/ase/units.py
new file mode 100644
index 0000000..b995ba2
--- /dev/null
+++ b/ase/units.py
@@ -0,0 +1,49 @@
+from math import pi, sqrt
+
+# Constants from Konrad Hinsen's PhysicalQuantities module (1986 CODATA):
+_c = 299792458.              # speed of light, m/s
+_mu0 = 4.e-7 * pi            # permeability of vacuum
+_eps0 = 1 / _mu0 / _c**2     # permittivity of vacuum
+_Grav = 6.67259e-11          # gravitational constant
+_hplanck = 6.6260755e-34     # Planck constant, J s
+_hbar = _hplanck / (2 * pi)  # Planck constant / 2pi, J s
+_e = 1.60217733e-19          # elementary charge
+_me = 9.1093897e-31          # electron mass
+_mp = 1.6726231e-27          # proton mass
+_Nav = 6.0221367e23          # Avogadro number
+_k = 1.380658e-23            # Boltzmann constant, J/K
+_amu = 1.6605402e-27         # atomic mass unit, kg
+
+Ang = Angstrom = 1.0
+nm = 10.0
+Bohr = 4e10 * pi * _eps0 * _hbar**2 / _me / _e**2  # Bohr radius
+
+eV = 1.0
+Hartree = _me * _e**3 / 16 / pi**2 / _eps0**2 / _hbar**2
+kJ = 1000.0 / _e
+kcal = 4.184 * kJ
+mol = _Nav
+Rydberg = 0.5 * Hartree
+Ry = Rydberg
+Ha = Hartree
+
+second = 1e10 * sqrt(_e / _amu)
+fs = 1e-15 * second
+
+kB = _k / _e                 # Boltzmann constant, eV/K
+
+Pascal = (1 / _e) / 1e30  # J/m^3
+GPa = 1e9 * Pascal
+
+Debye = 1e11 *_e * _c
+alpha = _e**2 / (4 * pi * _eps0) / _hbar / _c # fine structure constant
+
+# Derived atomic units that have no assigned name:
+_aut = _hbar / (alpha**2 * _me * _c**2)      # atomic unit of time, s
+_auv =  _e**2 / _hbar / (4 * pi * _eps0)     # atomic unit of velocity, m/s
+_auf = alpha**3 * _me**2 * _c**3 / _hbar     # atomic unit of force, N
+_aup = alpha**5 * _me**4 * _c**5 / _hbar**3  # atomic unit of pressure, Pa
+
+AUT = second * _aut
+
+del pi, sqrt
diff --git a/ase/utils/__init__.py b/ase/utils/__init__.py
new file mode 100644
index 0000000..6ced1ea
--- /dev/null
+++ b/ase/utils/__init__.py
@@ -0,0 +1,189 @@
+import os
+import sys
+from math import sin, cos, radians, atan2, degrees
+
+import numpy as np
+
+from ase.parallel import world
+
+
+class DevNull:
+    def write(self, string):
+        pass
+
+    def flush(self):
+        pass
+
+    def seek(self, offset, whence=0):
+        return 0
+
+    def tell(self):
+        return 0
+
+    def close(self):
+        pass
+
+devnull = DevNull()
+
+
+def opencew(filename):
+    """Create and open filename exclusively for writing.
+
+    If master cpu gets exclusive write access til filename, a file
+    descriptor is returned (a dummy file descriptor is returned on the
+    slaves).  If the master cpu doet not get write access, None is
+    returned on all processors."""
+
+    if world.rank == 0:
+        try:
+            fd = os.open(filename, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+        except OSError:
+            ok = 0
+        else:
+            ok = 1
+            fd = os.fdopen(fd, 'w')
+    else:
+        ok = 0
+        fd = devnull
+
+    # Syncronize:
+    if world.sum(ok) == 0:
+        return None
+    else:
+        return fd
+
+
+def prnt(*args, **kwargs):
+    """Python 3 style print function."""
+    kwargs.pop('file', sys.stdout).write(
+        kwargs.pop('sep', ' ').join(str(arg) for arg in args) +
+        kwargs.pop('end', '\n'))
+    if kwargs:
+        raise TypeError('%r is an invalid keyword argument for this function' %
+                        kwargs.keys()[0])
+
+
+def gcd(a, b):
+    """Greatest common divisor of a and b."""
+    while a != 0:
+        a, b = b % a, a
+    return b
+
+
+def rotate(rotations, rotation=np.identity(3)):
+    """Convert string of format '50x,-10y,120z' to a rotation matrix.
+
+    Note that the order of rotation matters, i.e. '50x,40z' is different
+    from '40z,50x'.
+    """
+
+    if rotations == '':
+        return rotation.copy()
+
+    for i, a in [('xyz'.index(s[-1]), radians(float(s[:-1])))
+                 for s in rotations.split(',')]:
+        s = sin(a)
+        c = cos(a)
+        if i == 0:
+            rotation = np.dot(rotation, [(1, 0, 0),
+                                         (0, c, s),
+                                         (0, -s, c)])
+        elif i == 1:
+            rotation = np.dot(rotation, [(c, 0, -s),
+                                         (0, 1, 0),
+                                         (s, 0, c)])
+        else:
+            rotation = np.dot(rotation, [(c, s, 0),
+                                         (-s, c, 0),
+                                         (0, 0, 1)])
+    return rotation
+
+
+def givens(a, b):
+    """Solve the equation system::
+
+      [ c s]   [a]   [r]
+      [    ] . [ ] = [ ]
+      [-s c]   [b]   [0]
+    """
+    sgn = lambda x: cmp(x, 0)
+    if b == 0:
+        c = sgn(a)
+        s = 0
+        r = abs(a)
+    elif abs(b) >= abs(a):
+        cot = a / b
+        u = sgn(b) * (1 + cot**2)**0.5
+        s = 1. / u
+        c = s * cot
+        r = b * u
+    else:
+        tan = b / a
+        u = sgn(a) * (1 + tan**2)**0.5
+        c = 1. / u
+        s = c * tan
+        r = a * u
+    return c, s, r
+
+
+def irotate(rotation, initial=np.identity(3)):
+    """Determine x, y, z rotation angles from rotation matrix."""
+    a = np.dot(initial, rotation)
+    cx, sx, rx = givens(a[2, 2], a[1, 2])
+    cy, sy, ry = givens(rx, a[0, 2])
+    cz, sz, rz = givens(cx * a[1, 1] - sx * a[2, 1],
+                        cy * a[0, 1] - sy * (sx * a[1, 1] + cx * a[2, 1]))
+    x = degrees(atan2(sx, cx))
+    y = degrees(atan2(-sy, cy))
+    z = degrees(atan2(sz, cz))
+    return x, y, z
+
+
+def hsv2rgb(h, s, v):
+    """http://en.wikipedia.org/wiki/HSL_and_HSV
+
+    h (hue) in [0, 360[
+    s (saturation) in [0, 1]
+    v (value) in [0, 1]
+
+    return rgb in range [0, 1]
+    """
+    if v == 0:
+        return 0, 0, 0
+    if s == 0:
+        return v, v, v
+
+    i, f = divmod(h / 60., 1)
+    p = v * (1 - s)
+    q = v * (1 - s * f)
+    t = v * (1 - s * (1 - f))
+
+    if i == 0:
+        return v, t, p
+    elif i == 1:
+        return q, v, p
+    elif i == 2:
+        return p, v, t
+    elif i == 3:
+        return p, q, v
+    elif i == 4:
+        return t, p, v
+    elif i == 5:
+        return v, p, q
+    else:
+        raise RuntimeError('h must be in [0, 360]')
+
+
+def hsv(array, s=.9, v=.9):
+    array = (array + array.min()) * 359. / (array.max() - array.min())
+    result = np.empty((len(array.flat), 3))
+    for rgb, h in zip(result, array.flat):
+        rgb[:] = hsv2rgb(h, s, v)
+    return np.reshape(result, array.shape + (3,))
+
+## This code does the same, but requires pylab
+## def cmap(array, name='hsv'):
+##     import pylab
+##     a = (array + array.min()) / array.ptp()
+##     rgba = getattr(pylab.cm, name)(a)
+##     return rgba[:-1] # return rgb only (not alpha)
diff --git a/ase/utils/adsorb.py b/ase/utils/adsorb.py
new file mode 100644
index 0000000..8410c44
--- /dev/null
+++ b/ase/utils/adsorb.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+
+# Copyright 2010 CAMd
+# (see accompanying license files for details).
+ 
+from optparse import OptionParser
+
+import numpy as np
+
+from ase.lattice.surface import fcc111, hcp0001, bcc110, diamond111, \
+    add_adsorbate
+from ase.structure import estimate_lattice_constant
+from ase.data import reference_states, atomic_numbers, covalent_radii
+from ase.io import write
+from ase.visualize import view
+from ase.atoms import Atoms, string2symbols
+from ase.data.molecules import molecule
+
+def build():
+    p = OptionParser(usage='%prog  [options] [ads@]surf [output file]',
+                     version='%prog 0.1',
+                     description='Example ads/surf: fcc-CO at 2x2Ru0001')
+    p.add_option('-l', '--layers', type='int',
+                 default=4,
+                 help='Number of layers.')
+    p.add_option('-v', '--vacuum', type='float',
+                 default=5.0,
+                 help='Vacuum.')
+    p.add_option('-x', '--crystal-structure',
+                 help='Crystal structure.',
+                 choices=['sc', 'fcc', 'bcc', 'hcp'])
+    p.add_option('-a', '--lattice-constant', type='float',
+                 help='Lattice constant in Angstrom.')
+    p.add_option('--c-over-a', type='float',
+                 help='c/a ratio.')
+    p.add_option('--height', type='float',
+                 help='Height of adsorbate over surface.')
+    p.add_option('--distance', type='float',
+                 help='Distance between adsorbate and nearest surface atoms.')
+    p.add_option('-M', '--magnetic-moment', type='float', default=0.0,
+                 help='Magnetic moment.')
+    p.add_option('-G', '--gui', action='store_true',
+                 help="Pop up ASE's GUI.")
+    p.add_option('-P', '--python', action='store_true',
+                 help="Write Python script.")
+
+    opt, args = p.parse_args()
+
+    if not 1 <= len(args) <= 2:
+        p.error("incorrect number of arguments")
+
+    if '@' in args[0]:
+        ads, surf = args[0].split('@')
+    else:
+        ads = None
+        surf = args[0]
+
+    if surf[0].isdigit():
+        i1 = surf.index('x')
+        n = int(surf[:i1])
+        i2 = i1 + 1
+        while surf[i2].isdigit():
+            i2 += 1
+        m = int(surf[i1 + 1:i2])
+        surf = surf[i2:]
+    else:
+        n = 1
+        m = 1
+
+    if surf[-1].isdigit():
+        if surf[1].isdigit():
+            face = surf[1:]
+            surf = surf[0]
+        else:
+            face = surf[2:]
+            surf = surf[:2]
+    else:
+        face = None
+
+    Z = atomic_numbers[surf]
+    state = reference_states[Z]
+
+    if opt.crystal_structure:
+        x = opt.crystal_structure
+    else:
+        x = state['symmetry']
+    
+    if opt.lattice_constant:
+        a = opt.lattice_constant
+    else:
+        a = estimate_lattice_constant(surf, x, opt.c_over_a)
+
+    script = ['from ase.lattice.surface import ',
+              'vac = %r' % opt.vacuum,
+              'a = %r' % a]
+    
+    if x == 'fcc':
+        if face is None:
+            face = '111'
+        slab = fcc111(surf, (n, m, opt.layers), a, opt.vacuum)
+        script[0] += 'fcc111'
+        script += ['slab = fcc111(%r, (%d, %d, %d), a, vac)' %
+                   (surf, n, m, opt.layers)]
+        r = a / np.sqrt(2) / 2
+    elif x == 'bcc':
+        if face is None:
+            face = '110'
+        slab = bcc110(surf, (n, m, opt.layers), a, opt.vacuum)
+        script[0] += 'bcc110'
+        script += ['slab = bcc110(%r, (%d, %d, %d), a, vac)' %
+                   (surf, n, m, opt.layers)]
+        r = a * np.sqrt(3) / 4
+    elif x == 'hcp':
+        if face is None:
+            face = '0001'
+        if opt.c_over_a is None:
+            c = np.sqrt(8 / 3.0) * a
+        else:
+            c = opt.c_over_a * a
+        slab = hcp0001(surf, (n, m, opt.layers), a, c, opt.vacuum)
+        script[0] += 'hcp0001'
+        script += ['c = %r * a' % (c / a),
+                   'slab = hcp0001(%r, (%d, %d, %d), a, c, vac)' %
+                   (surf, n, m, opt.layers)]
+        r = a / 2
+    elif x == 'diamond':
+        if face is None:
+            face = '111'
+        slab = diamond111(surf, (n, m, opt.layers), a, opt.vacuum)
+        script[0] += 'diamond111'
+        script += ['slab = diamond111(%r, (%d, %d, %d), a, vac)' %
+                   (surf, n, m, opt.layers)]
+        r = a * np.sqrt(3) / 8
+    else:
+        raise NotImplementedError
+
+    magmom = opt.magnetic_moment
+    if magmom is None:
+        magmom = {'Ni': 0.6, 'Co': 1.2, 'Fe': 2.3}.get(surf, 0.0)
+    slab.set_initial_magnetic_moments([magmom] * len(slab))
+    if magmom != 0:
+        script += ['slab.set_initial_magnetic_moments([%r] * len(slab))' %
+                   magmom]
+    
+    slab.pbc = 1
+    script += ['slab.pbc = True']
+    
+    name = '%dx%d%s%s' % (n, m, surf, face) 
+
+    if ads:
+        site = 'ontop'
+        if '-' in ads:
+            site, ads = ads.split('-')
+
+        name = site + '-' + ads + '@' + name
+        symbols = string2symbols(ads)
+        nads = len(symbols) 
+        if nads == 1:
+            script[:0] = ['from ase import Atoms']
+            script += ['ads = Atoms(%r)' % ads]
+            ads = Atoms(ads)
+        else:
+            script[:0] = ['from ase.data.molecules import molecule']
+            script += ['ads = molecule(%r)' % ads]
+            ads = molecule(ads)
+
+        add_adsorbate(slab, ads, 0.0, site)
+
+        d = opt.distance
+        if d is None:
+            d = r + covalent_radii[ads[0].number] / 2
+        
+        h = opt.height
+        if h is None:
+            R = slab.positions
+            y = ((R[:-nads] - R[-nads])**2).sum(1).min()**0.5
+            h = (d**2 - y**2)**0.5
+        else:
+            assert opt.distance is None
+        
+        slab.positions[-nads:, 2] += h
+
+        script[1] += ', add_adsorbate'
+        script += ['add_adsorbate(slab, ads, %r, %r)' % (h, site)]
+        
+    if len(args) == 2:
+        write(args[1], slab)
+        script[1:1] = ['from ase.io import write']
+        script += ['write(%r, slab)' % args[1]]
+    elif not opt.gui:
+        write(name + '.traj', slab)
+        script[1:1] = ['from ase.io import write']
+        script += ['write(%r, slab)' % (name + '.traj')]
+        
+    if opt.gui:
+        view(slab)
+        script[1:1] = ['from ase.visualize import view']
+        script += ['view(slab)']
+
+    if opt.python:
+        print('\n'.join(script))
+
+
+if __name__ == '__main__':
+    build()
diff --git a/ase/utils/bee.py b/ase/utils/bee.py
new file mode 100644
index 0000000..9228776
--- /dev/null
+++ b/ase/utils/bee.py
@@ -0,0 +1,45 @@
+import numpy as np
+
+# NB! This module was ported from a 4 year old CamposASE2 module.
+
+"""Bayesian Error Estimation
+
+For details, see: "Bayesian Error Estimation in Density Functional
+Theory", J. J. Mortensen, K. Kaasbjerg, S. L. Frederiksen,
+J. K. Norskov, J. P. Sethna, K. W. Jacobsen, Phys. Rev. Lett. 95,
+216401 (2005)."""
+
+#                                 T
+# cost(c) = cost0 + 0.5 * (c - c0) H (c - c0)
+#
+
+# Cost function minimum value:
+cost0 = 3.4660625596
+
+# Best fit parameters:
+c0 = np.array([1.000787451, 0.1926284063, 1.896191546])
+
+# Hessian:
+# H = np.array([[ 1.770035168e+03, -3.732470432e+02, -2.105836167e+02],
+#               [-3.732470432e+02,  1.188857209e+02,  6.054102443e+01],
+#               [-2.105836167e+02,  6.054102443e+01,  3.211200293e+01]])
+#
+# 0.5 * np * T = cost0 (np=3: number of parameters)
+T = cost0 * 2 / 3
+
+def make_ensemble(N=1000, seed=None):
+    np.random.seed(seed) # None means /dev/urandom seed
+    M = np.array([(0.066, -0.812, 1.996),
+                  (0.055, 0.206, 0.082),
+                  (-0.034, 0.007, 0.004)])
+    alpha = np.random.normal(0.0, 1.0, (N, 3))
+    return c0 + np.dot(alpha, M)
+
+c = make_ensemble()
+
+def get_ensemble_energies(atoms, c=c):
+    if hasattr(atoms, 'get_calculator'):
+        coefs = atoms.get_calculator().get_ensemble_coefficients()
+    else:
+        coefs = atoms
+    return coefs[0] + np.dot(c, coefs[1:])
diff --git a/ase/utils/deprecate.py b/ase/utils/deprecate.py
new file mode 100644
index 0000000..db4401e
--- /dev/null
+++ b/ase/utils/deprecate.py
@@ -0,0 +1,44 @@
+import warnings
+
+class Deprecate:
+    def __init__(self, obj, name, newmodule, oldmodule='ase'):
+        self.obj = obj
+        self.name = name
+        self.newmodule = newmodule
+        self.oldmodule = oldmodule
+
+    def __call__(self, *args, **kwargs):
+        message = ('%s.%s is deprecated, use %s.%s instead' %
+                   (self.oldmodule, self.name, self.newmodule, self.name))
+        warnings.warn(message, DeprecationWarning, stacklevel=2)
+        return self.obj(*args, **kwargs)
+
+def _dep(method):
+    def _method(self, *args):
+        message = ('ase.%s is deprecated, use %s.%s instead' %
+                   (self.name, self.newmodule, self.name))
+        warnings.warn(message, DeprecationWarning, stacklevel=2)
+        return method(self, *args)
+    return _method
+
+class DeprecatedFloat(float):
+    def __new__(cls, value, name, newmodule):
+        return float.__new__(cls, value)
+
+    def __init__(self, value, name, newmodule):
+        self.name = name
+        self.newmodule = newmodule
+
+    __mul__ = _dep(float.__mul__)
+    __rmul__ = _dep(float.__rmul__)
+    __div__ = _dep(float.__div__)
+    __rdiv__ = _dep(float.__rdiv__)
+
+class DeprecatedNumpyImport:
+    def __init__(self):
+        import numpy
+        self.numpy = numpy
+
+    def __getattr__(self, key):
+        warnings.warn('ase.np is deprecated; use import numpy as np instead')
+        return getattr(self.numpy, key)
diff --git a/ase/utils/eos.py b/ase/utils/eos.py
new file mode 100644
index 0000000..325a615
--- /dev/null
+++ b/ase/utils/eos.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+from math import sqrt
+
+import numpy as np
+
+from ase.units import kJ
+
+class EquationOfState:
+    """Fit equation of state for bulk systems.
+
+    The following equation is used::
+
+                          2      3        -1/3
+      E(V) = c + c t + c t  + c t ,  t = V
+              0   1     2      3
+
+    Use::
+
+       eos = EquationOfState(volumes, energies)
+       v0, e0, B = eos.fit()
+       eos.plot()
+
+    """
+    def __init__(self, volumes, energies):
+        self.v = np.array(volumes)
+        self.e = np.array(energies)
+        self.v0 = None
+
+    def fit(self):
+        """Calculate volume, energy, and bulk modulus.
+
+        Returns the optimal volume, the minumum energy, and the bulk
+        modulus.  Notice that the ASE units for the bulk modulus is
+        eV/Angstrom^3 - to get the value in GPa, do this::
+
+          v0, e0, B = eos.fit()
+          print B / kJ * 1.0e24, 'GPa'
+          
+        """
+        
+        fit0 = np.poly1d(np.polyfit(self.v**-(1.0 / 3), self.e, 3))
+        fit1 = np.polyder(fit0, 1)
+        fit2 = np.polyder(fit1, 1)
+
+        self.v0 = None
+        for t in np.roots(fit1):
+            if t > 0 and fit2(t) > 0:
+                self.v0 = t**-3
+                break
+
+        if self.v0 is None:
+            raise ValueError('No minimum!')
+        
+        self.e0 = fit0(t)
+        self.B = t**5 * fit2(t) / 9
+        self.fit0 = fit0
+        
+        return self.v0, self.e0, self.B
+
+    def plot(self, filename=None, show=None):
+        """Plot fitted energy curve.
+
+        Uses Matplotlib to plot the energy curve.  Use *show=True* to
+        show the figure and *filename='abc.png'* or
+        *filename='abc.eps'* to save the figure to a file."""
+        
+        #import matplotlib.pyplot as plt
+        import pylab as plt
+
+        if self.v0 is None:
+            self.fit()
+            
+        if filename is None and show is None:
+            show = True
+
+        x = 3.95
+        f = plt.figure(figsize=(x * 2.5**0.5, x))
+        f.subplots_adjust(left=0.12, right=0.9, top=0.9, bottom=0.15)
+        plt.plot(self.v, self.e, 'o')
+        x = np.linspace(min(self.v), max(self.v), 100)
+        plt.plot(x, self.fit0(x**-(1.0 / 3)), '-r')
+        plt.xlabel(u'volume [Å^3]')
+        plt.ylabel(u'energy [eV]')
+        plt.title(u'E: %.3f eV, V: %.3f Å^3, B: %.3f GPa' %
+                  (self.e0, self.v0, self.B / kJ * 1.0e24))
+
+        if show:
+            plt.show()
+        if filename is not None:
+            f.savefig(filename)
+
+        return f
diff --git a/ase/utils/geometry.py b/ase/utils/geometry.py
new file mode 100644
index 0000000..6af01ee
--- /dev/null
+++ b/ase/utils/geometry.py
@@ -0,0 +1,437 @@
+# Copyright (C) 2010, Jesper Friis
+# (see accompanying license files for details).
+
+"""Utility tools for convenient creation of slabs and interfaces of
+different orientations."""
+
+import numpy as np
+
+
+def get_layers(atoms, miller, tolerance=0.001):
+    """Returns two arrays describing which layer each atom belongs
+    to and the distance between the layers and origo. 
+
+    Parameters:
+
+    miller: 3 integers
+        The Miller indices of the planes. Actually, any direction
+        in reciprocal space works, so if a and b are two float
+        vectors spanning an atomic plane, you can get all layers
+        parallel to this with miller=np.cross(a,b).
+    tolerance: float
+        The maximum distance in Angstrom along the plane normal for
+        counting two atoms as belonging to the same plane.
+
+    Returns:
+
+    tags: array of integres
+        Array of layer indices for each atom.
+    levels: array of floats
+        Array of distances in Angstrom from each layer to origo.
+
+    Example:
+
+    >>> import numpy as np
+    >>> from ase.lattice.spacegroup import crystal
+    >>> atoms = crystal('Al', [(0,0,0)], spacegroup=225, cellpar=4.05)
+    >>> np.round(atoms.positions, decimals=5)
+    array([[ 0.   ,  0.   ,  0.   ],
+           [ 0.   ,  2.025,  2.025],
+           [ 2.025,  0.   ,  2.025],
+           [ 2.025,  2.025,  0.   ]])
+    >>> get_layers(atoms, (0,0,1))
+    (array([0, 1, 1, 0]), array([ 0.   ,  2.025]))
+    """
+    miller = np.asarray(miller)
+
+    metric = np.dot(atoms.cell, atoms.cell.T)
+    c = np.linalg.solve(metric.T, miller.T).T
+    miller_norm = np.sqrt(np.dot(c, miller))
+    d = np.dot(atoms.get_scaled_positions(), miller)/miller_norm
+
+    keys = np.argsort(d)
+    ikeys = np.argsort(keys)
+    mask = np.concatenate(([True], np.diff(d[keys]) > tolerance))
+    tags = np.cumsum(mask)[ikeys]
+    if tags.min() == 1:
+        tags -= 1
+
+    levels = d[keys][mask]
+    return tags, levels
+
+
+
+
+def cut(atoms, a=(1, 0, 0), b=(0, 1, 0), c=None, clength=None, 
+        origo=(0, 0, 0), nlayers=None, extend=1.0, tolerance=0.01, 
+        maxatoms=None):
+    """Cuts out a cell defined by *a*, *b*, *c* and *origo* from a
+    sufficiently repeated copy of *atoms*.
+
+    Typically, this function is used to create slabs of different
+    sizes and orientations. The vectors *a*, *b* and *c* are in scaled
+    coordinates and defines the returned cell and should normally be
+    integer-valued in order to end up with a periodic
+    structure. However, for systems with sub-translations, like fcc,
+    integer multiples of 1/2 or 1/3 might also make sence for some
+    directions (and will be treated correctly).
+
+    Parameters:
+    
+    atoms: Atoms instance
+        This should correspond to a repeatable unit cell.
+    a: int | 3 floats
+        The a-vector in scaled coordinates of the cell to cut out. If
+        integer, the a-vector will be the scaled vector from *origo* to the
+        atom with index *a*.
+    b: int | 3 floats
+        The b-vector in scaled coordinates of the cell to cut out. If
+        integer, the b-vector will be the scaled vector from *origo* to the
+        atom with index *b*.
+    c: None | int | 3 floats
+        The c-vector in scaled coordinates of the cell to cut out. 
+        if integer, the c-vector will be the scaled vector from *origo* to 
+        the atom with index *c*. 
+        If *None* it will be along cross(a, b) converted to real space
+        and normalised with the cube root of the volume. Note that this
+        in general is not perpendicular to a and b for non-cubic
+        systems. For cubic systems however, this is redused to 
+        c = cross(a, b).
+    clength: None | float
+        If not None, the length of the c-vector will be fixed to
+        *clength* Angstroms. Should not be used together with
+        *nlayers*.
+    origo: int | 3 floats
+        Position of origo of the new cell in scaled coordinates. If
+        integer, the position of the atom with index *origo* is used.
+    nlayers: None | int
+        If *nlayers* is not *None*, the returned cell will have
+        *nlayers* atomic layers in the c-direction.
+    extend: 1 or 3 floats
+        The *extend* argument scales the effective cell in which atoms
+        will be included. It must either be three floats or a single
+        float scaling all 3 directions.  By setting to a value just
+        above one, e.g. 1.05, it is possible to all the corner and
+        edge atoms in the returned cell.  This will of cause make the
+        returned cell non-repeatable, but is very usefull for
+        visualisation.
+    tolerance: float
+        Determines what is defined as a plane.  All atoms within
+        *tolerance* Angstroms from a given plane will be considered to
+        belong to that plane.
+    maxatoms: None | int
+        This option is used to auto-tune *tolerance* when *nlayers* is
+        given for high zone axis systems.  For high zone axis one
+        needs to reduce *tolerance* in order to distinguise the atomic
+        planes, resulting in the more atoms will be added and
+        eventually MemoryError.  A too small *tolerance*, on the other
+        hand, might result in inproper splitting of atomic planes and
+        that too few layers are returned.  If *maxatoms* is not None,
+        *tolerance* will automatically be gradually reduced until
+        *nlayers* atomic layers is obtained, when the number of atoms
+        exceeds *maxatoms*.
+
+    Example:
+
+    >>> import ase
+    >>> from ase.lattice.spacegroup import crystal
+    >>>
+    # Create an aluminium (111) slab with three layers
+    #
+    # First an unit cell of Al
+    >>> a = 4.05
+    >>> aluminium = crystal('Al', [(0,0,0)], spacegroup=225,
+    ...                     cellpar=[a, a, a, 90, 90, 90])
+    >>>
+    # Then cut out the slab
+    >>> al111 = cut(aluminium, (1,-1,0), (0,1,-1), nlayers=3)
+    >>>
+    # Visualisation of the skutterudite unit cell 
+    #
+    # Again, create a skutterudite unit cell
+    >>> a = 9.04
+    >>> skutterudite = crystal(
+    ...     ('Co', 'Sb'), 
+    ...     basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)], 
+    ...     spacegroup=204, 
+    ...     cellpar=[a, a, a, 90, 90, 90])
+    >>>
+    # Then use *origo* to put 'Co' at the corners and *extend* to
+    # include all corner and edge atoms.
+    >>> s = cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01)
+    >>> ase.view(s)  # doctest: +SKIP
+    """
+    atoms = atoms.copy()
+    cell = atoms.cell
+
+    if isinstance(origo, int):
+        origo = atoms.get_scaled_positions()[origo]
+    origo = np.array(origo, dtype=float)
+
+    scaled = (atoms.get_scaled_positions() - origo)%1.0
+    scaled %= 1.0 # needed to ensure that all numbers are *less* than one
+    atoms.set_scaled_positions(scaled)
+
+    if isinstance(a, int):
+        a = scaled[a] - origo
+    if isinstance(b, int):
+        b = scaled[b] - origo
+    if isinstance(c, int):
+        c = scaled[c] - origo
+
+    a = np.array(a, dtype=float)
+    b = np.array(b, dtype=float)
+    if c is None:
+        metric = np.dot(cell, cell.T)
+        vol = np.sqrt(np.linalg.det(metric))
+        h = np.cross(a, b) 
+        H = np.linalg.solve(metric.T, h.T)
+        c = vol*H/vol**(1./3.)
+    c = np.array(c, dtype=float)
+
+    if nlayers:
+        # Recursive increase the length of c until we have at least
+        # *nlayers* atomic layers parallell to the a-b plane
+        while True:
+            at = cut(atoms, a, b, c, origo=origo, extend=extend, 
+                        tolerance=tolerance)
+            scaled = at.get_scaled_positions()
+            d = scaled[:,2]
+            keys = np.argsort(d)
+            ikeys = np.argsort(keys)
+            tol = tolerance
+            while True:
+                mask = np.concatenate(([True], np.diff(d[keys]) > tol))
+                tags = np.cumsum(mask)[ikeys] - 1
+                levels = d[keys][mask]
+                if (maxatoms is None or len(at) < maxatoms or 
+                    len(levels) > nlayers): 
+                    break
+                tol *= 0.9
+            if len(levels) > nlayers: 
+                break
+            c *= 2
+
+        at.cell[2] *= levels[nlayers]
+        return at[tags < nlayers]
+
+    newcell = np.dot(np.array([a, b, c]), cell)
+    if nlayers is None and clength is not None:
+        newcell[2,:] *= clength/np.linalg.norm(newcell[2])
+
+    # Create a new atoms object, repeated and translated such that
+    # it completely covers the new cell
+    scorners_newcell = np.array([[0., 0., 0.], [0., 0., 1.], 
+                                 [0., 1., 0.], [0., 1., 1.], 
+                                 [1., 0., 0.], [1., 0., 1.], 
+                                 [1., 1., 0.], [1., 1., 1.]])
+    corners = np.dot(scorners_newcell, newcell*extend)
+    scorners = np.linalg.solve(cell.T, corners.T).T
+    rep = np.ceil(scorners.ptp(axis=0)).astype('int') + 1
+    trans = np.dot(np.floor(scorners.min(axis=0)), cell)
+    atoms = atoms.repeat(rep)
+    atoms.translate(trans)
+    atoms.set_cell(newcell)
+
+    # Mask out atoms outside new cell
+    stol = 0.1*tolerance  # scaled tolerance, XXX
+    maskcell = atoms.cell*extend
+    sp = np.linalg.solve(maskcell.T, (atoms.positions).T).T
+    mask = np.all(np.logical_and(-stol <= sp, sp < 1-stol), axis=1)
+    atoms = atoms[mask]
+    return atoms
+
+
+class IncompatibleCellError(ValueError):
+    """Exception raised if stacking fails due to incompatible cells
+    between *atoms1* and *atoms2*."""
+    pass
+
+
+def stack(atoms1, atoms2, axis=2, cell=None, fix=0.5,  
+          maxstrain=0.5, distance=None, reorder=False):
+    """Return a new Atoms instance with *atoms2* stacked on top of
+    *atoms1* along the given axis. Periodicity in all directions is
+    ensured.
+
+    The size of the final cell is determined by *cell*, except
+    that the length alongh *axis* will be the sum of
+    *atoms1.cell[axis]* and *atoms2.cell[axis]*. If *cell* is None,
+    it will be interpolated between *atoms1* and *atoms2*, where
+    *fix* determines their relative weight. Hence, if *fix* equals
+    zero, the final cell will be determined purely from *atoms1* and
+    if *fix* equals one, it will be determined purely from
+    *atoms2*.
+
+    An ase.geometry.IncompatibleCellError exception is raised if the
+    cells of *atoms1* and *atoms2* are incopatible, e.g. if the far
+    corner of the unit cell of either *atoms1* or *atoms2* is
+    displaced more than *maxstrain*. Setting *maxstrain* to None,
+    disable this check.
+
+    If *distance* is not None, the size of the final cell, along the
+    direction perpendicular to the interface, will be adjusted such
+    that the distance between the closest atoms in *atoms1* and
+    *atoms2* will be equal to *distance*. This option uses
+    scipy.optimize.fmin() and hence require scipy to be installed.
+
+    If *reorder* is True, then the atoms will be reordred such that
+    all atoms with the same symbol will follow sequensially after each
+    other, eg: 'Al2MnAl10Fe' -> 'Al12FeMn'.    
+
+    Example:
+
+    >>> import ase
+    >>> from ase.lattice.spacegroup import crystal
+    >>>
+    # Create an Ag(110)-Si(110) interface with three atomic layers
+    # on each side. 
+    >>> a_ag = 4.09
+    >>> ag = crystal(['Ag'], basis=[(0,0,0)], spacegroup=225, 
+    ...              cellpar=[a_ag, a_ag, a_ag, 90., 90., 90.])
+    >>> ag110 = cut(ag, (0, 0, 3), (-1.5, 1.5, 0), nlayers=3)
+    >>>
+    >>> a_si = 5.43
+    >>> si = crystal(['Si'], basis=[(0,0,0)], spacegroup=227, 
+    ...              cellpar=[a_si, a_si, a_si, 90., 90., 90.])
+    >>> si110 = cut(si, (0, 0, 2), (-1, 1, 0), nlayers=3)
+    >>>
+    >>> interface = stack(ag110, si110, maxstrain=1)
+    >>> ase.view(interface)  # doctest: +SKIP
+    >>>
+    # Once more, this time adjusted such that the distance between
+    # the closest Ag and Si atoms will be 2.3 Angstrom (requires scipy).
+    >>> interface2 = stack(ag110, si110, 
+    ...                    maxstrain=1, distance=2.3)   # doctest:+ELLIPSIS
+    Optimization terminated successfully.
+        ...
+    >>> ase.view(interface2)  # doctest: +SKIP
+    """
+    atoms1 = atoms1.copy()
+    atoms2 = atoms2.copy()
+
+    if (np.sign(np.linalg.det(atoms1.cell)) != 
+        np.sign(np.linalg.det(atoms2.cell))):
+        raise IncompatibleCellError('*atoms1* amd *atoms2* must both either '
+                                    'have a lefthanded or a righanded cell.')
+
+    c1 = np.linalg.norm(atoms1.cell[axis])
+    c2 = np.linalg.norm(atoms2.cell[axis])
+    if cell is None:
+        cell1 = atoms1.cell.copy()
+        cell2 = atoms2.cell.copy()
+        cell1[axis] /= c1
+        cell2[axis] /= c2
+        cell = cell1 + fix*(cell2 - cell1)
+    cell[axis] /= np.linalg.norm(cell[axis])
+    cell1 = cell.copy()
+    cell2 = cell.copy()
+    cell1[axis] *= c1
+    cell2[axis] *= c2
+
+    if maxstrain:
+        strain1 = np.sqrt(((cell1 - atoms1.cell).sum(axis=0)**2).sum())
+        strain2 = np.sqrt(((cell2 - atoms2.cell).sum(axis=0)**2).sum())
+        if strain1 > maxstrain or strain2 > maxstrain:
+            raise IncompatibleCellError(
+                '*maxstrain* exceeded. *atoms1* strained %f and '
+                '*atoms2* strained %f.'%(strain1, strain2))
+
+    atoms1.set_cell(cell1, scale_atoms=True)
+    atoms2.set_cell(cell2, scale_atoms=True)
+
+    if distance is not None:
+        from scipy.optimize import fmin
+        def mindist(pos1, pos2):
+            n1 = len(pos1)
+            n2 = len(pos2)
+            idx1 = np.arange(n1).repeat(n2)
+            idx2 = np.tile(np.arange(n2), n1)
+            return np.sqrt(((pos1[idx1] - pos2[idx2])**2).sum(axis=1).min())
+        def func(x):
+            t1, t2, h1, h2 = x[0:3], x[3:6], x[6], x[7]
+            pos1 = atoms1.positions + t1
+            pos2 = atoms2.positions + t2
+            d1 = mindist(pos1, pos2 + (h1 + 1.0)*atoms1.cell[axis])
+            d2 = mindist(pos2, pos1 + (h2 + 1.0)*atoms2.cell[axis])
+            return (d1 - distance)**2 + (d2 - distance)**2
+        atoms1.center()
+        atoms2.center()
+        x0 = np.zeros((8,))
+        x = fmin(func, x0)
+        t1, t2, h1, h2 = x[0:3], x[3:6], x[6], x[7]
+        atoms1.translate(t1)
+        atoms2.translate(t2)
+        atoms1.cell[axis] *= 1.0 + h1
+        atoms2.cell[axis] *= 1.0 + h2
+
+    atoms2.translate(atoms1.cell[axis])
+    atoms1.cell[axis] += atoms2.cell[axis]
+    atoms1.extend(atoms2)
+
+    if reorder:
+        atoms1 = sort(atoms1)
+
+    return atoms1
+
+
+def sort(atoms, tags=None):
+    """Return a new Atoms object with sorted atomic order. The default
+    is to order according to chemical symbols, but if *tags* is not
+    None, it will be used instead. A stable sorting algorithm is used.
+
+    Example:
+    >>> import ase
+    >>> from ase.lattice.spacegroup import crystal
+    >>>
+    # Two unit cells of NaCl
+    >>> a = 5.64
+    >>> nacl = crystal(['Na', 'Cl'], [(0, 0, 0), (0.5, 0.5, 0.5)], 
+    ... spacegroup=225, cellpar=[a, a, a, 90, 90, 90]).repeat((2, 1, 1))
+    >>> nacl.get_chemical_symbols()
+    ['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl', 'Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl']
+    >>> nacl_sorted = sort(nacl)
+    >>> nacl_sorted.get_chemical_symbols()
+    ['Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Cl', 'Na', 'Na', 'Na', 'Na', 'Na', 'Na', 'Na', 'Na']
+    >>> np.all(nacl_sorted.cell == nacl.cell)
+    True
+    """
+    if tags is None:
+        tags = atoms.get_chemical_symbols()
+    else:
+        tags = list(tags)
+    deco = [(tag, i) for i, tag in enumerate(tags)]
+    deco.sort()
+    indices = [i for tag, i in deco]
+    return atoms[indices]
+
+
+def rotate(atoms, a1, a2, b1, b2, rotate_cell=True):
+    """Rotate *atoms*, such that *a1* will be rotated to *a2* and *b1*
+    to *b2*."""
+    from numpy.linalg import norm, det
+    a1 = np.asarray(a1, dtype=float)/norm(a1)
+    a2 = np.asarray(a2, dtype=float)/norm(a2)
+    b1 = np.asarray(b1, dtype=float)/norm(b1)
+    b2 = np.asarray(b2, dtype=float)/norm(b2)
+    if norm(a2 - a1) < 1e-5:
+        n = 0.5*(a1 + a2)
+        a1, a2 = b1, b2
+    elif norm(b2 - b1) < 1e-5:
+        n = 0.5*(b1 + b2)
+    else:
+        n = np.cross(a2 - a1, b2 - b1)
+    n /= norm(n)
+    ap1 = a1 - np.dot(a1, n)*n
+    ap2 = a2 - np.dot(a2, n)*n
+    angle = np.arccos(np.dot(ap1, ap2)/(norm(ap1)*norm(ap2)))
+    angle *= np.sign(det((ap1, ap2, n)))
+    atoms.rotate(n, angle, rotate_cell=rotate_cell)
+
+
+
+#-----------------------------------------------------------------
+# Self test
+if __name__ == '__main__':
+    import doctest
+    print 'doctest: ', doctest.testmod()
diff --git a/ase/utils/linesearch.py b/ase/utils/linesearch.py
new file mode 100644
index 0000000..291968e
--- /dev/null
+++ b/ase/utils/linesearch.py
@@ -0,0 +1,397 @@
+import numpy as np
+import __builtin__
+pymin = __builtin__.min
+pymax = __builtin__.max
+
+class LineSearch:
+    def __init__(self,  xtol=1e-14):
+
+        self.xtol = xtol
+        self.task = 'START'
+        self.isave = np.zeros((2,), np.intc)
+        self.dsave = np.zeros((13,), float)
+        self.fc = 0
+        self.gc = 0
+        self.case = 0
+        self.old_stp = 0
+
+    def _line_search(self, func, myfprime, xk, pk, gfk, old_fval, old_old_fval,
+                     maxstep=.2, c1=.23, c2=0.46, xtrapl=1.1, xtrapu=4.,
+                     stpmax=50., stpmin=1e-8, args=()):
+        self.stpmin = stpmin
+        self.pk = pk
+        p_size = np.sqrt((pk **2).sum())
+        self.stpmax = stpmax
+        self.xtrapl = xtrapl
+        self.xtrapu = xtrapu
+        self.maxstep = maxstep
+        phi0 = old_fval
+        derphi0 = np.dot(gfk,pk)
+        self.dim = len(pk)
+        self.gms = np.sqrt(self.dim) * maxstep
+        #alpha1 = pymin(maxstep,1.01*2*(phi0-old_old_fval)/derphi0)
+        alpha1 = 1.
+        self.no_update = False
+
+        if isinstance(myfprime,type(())):
+            eps = myfprime[1]
+            fprime = myfprime[0]
+            newargs = (f,eps) + args
+            gradient = False
+        else:
+            fprime = myfprime
+            newargs = args
+            gradient = True
+
+        fval = old_fval
+        gval = gfk
+        self.steps=[]
+
+        while 1:
+            stp = self.step(alpha1, phi0, derphi0, c1, c2,
+                                             self.xtol,
+                                             self.isave, self.dsave)
+
+            if self.task[:2] == 'FG':
+                alpha1 = stp
+                fval = func(xk + stp * pk, *args)
+                self.fc += 1
+                gval = fprime(xk + stp * pk, *newargs)
+                if gradient: self.gc += 1
+                else: self.fc += len(xk) + 1
+                phi0 = fval
+                derphi0 = np.dot(gval,pk)
+                self.old_stp = alpha1
+                if self.no_update == True:
+                    break
+            else:
+                break
+
+        if self.task[:5] == 'ERROR' or self.task[1:4] == 'WARN':
+            stp = None  # failed
+        return stp, fval, old_fval, self.no_update
+
+    def step(self, stp, f, g, c1, c2, xtol, isave, dsave):
+        if self.task[:5] == 'START':
+            # Check the input arguments for errors.
+            if stp < self.stpmin:
+                self.task = 'ERROR: STP .LT. minstep'
+            if stp > self.stpmax:
+                self.task = 'ERROR: STP .GT. maxstep'
+            if g >= 0:
+                self.task = 'ERROR: INITIAL G >= 0'
+            if c1 < 0:
+                self.task = 'ERROR: c1 .LT. 0'
+            if c2 < 0:
+                self.task = 'ERROR: c2 .LT. 0'
+            if xtol < 0:
+                self.task = 'ERROR: XTOL .LT. 0'
+            if self.stpmin < 0:
+                self.task = 'ERROR: minstep .LT. 0'
+            if self.stpmax < self.stpmin:
+                self.task = 'ERROR: maxstep .LT. minstep'
+            if self.task[:5] == 'ERROR':
+                return stp
+
+            # Initialize local variables.
+            self.bracket = False
+            stage = 1
+            finit = f
+            ginit = g
+            gtest = c1 * ginit
+            width = self.stpmax - self.stpmin
+            width1 = width / .5
+#           The variables stx, fx, gx contain the values of the step,
+#           function, and derivative at the best step.
+#           The variables sty, fy, gy contain the values of the step,
+#           function, and derivative at sty.
+#           The variables stp, f, g contain the values of the step,
+#           function, and derivative at stp.
+            stx = 0
+            fx = finit
+            gx = ginit
+            sty = 0
+            fy = finit
+            gy = ginit
+            stmin = 0
+            stmax = stp + self.xtrapu * stp
+            self.task = 'FG'
+            self.save((stage, ginit, gtest, gx,
+                       gy, finit, fx, fy, stx, sty,
+                       stmin, stmax, width, width1))
+            stp = self.determine_step(stp)
+            #return stp, f, g
+            return stp
+        else:
+            if self.isave[0] == 1:
+                self.bracket = True
+            else:
+                self.bracket = False
+            stage = self.isave[1]
+            (ginit, gtest, gx, gy, finit, fx, fy, stx, sty, stmin, stmax, \
+            width, width1) =self.dsave
+
+#           If psi(stp) <= 0 and f'(stp) >= 0 for some step, then the
+#           algorithm enters the second stage.
+            ftest = finit + stp * gtest
+            if stage == 1 and f < ftest and g >= 0.:
+                stage = 2
+
+#           Test for warnings.
+            if self.bracket and (stp <= stmin or stp >= stmax):
+                self.task = 'WARNING: ROUNDING ERRORS PREVENT PROGRESS'
+            if self.bracket and stmax - stmin <= self.xtol * stmax:
+                self.task = 'WARNING: XTOL TEST SATISFIED'
+            if stp == self.stpmax and f <= ftest and g <= gtest:
+                self.task = 'WARNING: STP = maxstep'
+            if stp == self.stpmin and (f > ftest or g >= gtest):
+                self.task = 'WARNING: STP = minstep'
+
+#           Test for convergence.
+            if f <= ftest and abs(g) <= c2 * (- ginit):
+                self.task = 'CONVERGENCE'
+
+#           Test for termination.
+            if self.task[:4] == 'WARN' or self.task[:4] == 'CONV':
+                self.save((stage, ginit, gtest, gx,
+                           gy, finit, fx, fy, stx, sty,
+                           stmin, stmax, width, width1))
+                #return stp, f, g
+                return stp
+
+#              A modified function is used to predict the step during the
+#              first stage if a lower function value has been obtained but
+#              the decrease is not sufficient.
+            #if stage == 1 and f <= fx and f > ftest:
+#           #    Define the modified function and derivative values.
+            #    fm =f - stp * gtest
+            #    fxm = fx - stx * gtest
+            #    fym = fy - sty * gtest
+            #    gm = g - gtest
+            #    gxm = gx - gtest
+            #    gym = gy - gtest
+
+#               Call step to update stx, sty, and to compute the new step.
+            #    stx, sty, stp, gxm, fxm, gym, fym = self.update (stx, fxm, gxm, sty,
+            #                                        fym, gym, stp, fm, gm,
+            #                                        stmin, stmax)
+
+#           #    Reset the function and derivative values for f.
+
+            #    fx = fxm + stx * gtest
+            #    fy = fym + sty * gtest
+            #    gx = gxm + gtest
+            #    gy = gym + gtest
+
+            #else:
+#           Call step to update stx, sty, and to compute the new step.
+
+            stx, sty, stp, gx, fx, gy, fy= self.update(stx, fx, gx, sty,
+                                               fy, gy, stp, f, g,
+                                               stmin, stmax)
+
+
+#           Decide if a bisection step is needed.
+
+            if self.bracket:
+                if abs(sty-stx) >= .66 * width1:
+                    stp = stx + .5 * (sty - stx)
+                width1 = width
+                width = abs(sty - stx)
+
+#           Set the minimum and maximum steps allowed for stp.
+
+            if self.bracket:
+                stmin = min(stx, sty)
+                stmax = max(stx, sty)
+            else:
+                stmin = stp + self.xtrapl * (stp - stx)
+                stmax = stp + self.xtrapu * (stp - stx)
+
+#           Force the step to be within the bounds maxstep and minstep.
+
+            stp = max(stp, self.stpmin)
+            stp = min(stp, self.stpmax)
+
+            if (stx == stp and stp == self.stpmax and stmin > self.stpmax):
+                self.no_update = True
+#           If further progress is not possible, let stp be the best
+#           point obtained during the search.
+
+            if (self.bracket and stp < stmin or stp >= stmax) \
+               or (self.bracket and stmax - stmin < self.xtol * stmax):
+                stp = stx
+
+#           Obtain another function and derivative.
+
+            self.task = 'FG'
+            self.save((stage, ginit, gtest, gx,
+                       gy, finit, fx, fy, stx, sty,
+                       stmin, stmax, width, width1))
+            return stp
+
+    def update(self, stx, fx, gx, sty, fy, gy, stp, fp, gp,
+               stpmin, stpmax):
+        sign = gp * (gx / abs(gx))
+
+#       First case: A higher function value. The minimum is bracketed.
+#       If the cubic step is closer to stx than the quadratic step, the
+#       cubic step is taken, otherwise the average of the cubic and
+#       quadratic steps is taken.
+        if fp > fx:  #case1
+            self.case = 1
+            theta = 3. * (fx - fp) / (stp - stx) + gx + gp
+            s = max(abs(theta), abs(gx), abs(gp))
+            gamma = s * np.sqrt((theta / s) ** 2. - (gx / s) * (gp / s))
+            if stp < stx:
+                gamma = -gamma
+            p = (gamma - gx) + theta
+            q = ((gamma - gx) + gamma) + gp
+            r = p / q
+            stpc = stx + r * (stp - stx)
+            stpq = stx + ((gx / ((fx - fp) / (stp-stx) + gx)) / 2.) \
+                   * (stp - stx)
+            if (abs(stpc - stx) < abs(stpq - stx)):
+               stpf = stpc
+            else:
+               stpf = stpc + (stpq - stpc) / 2.
+
+            self.bracket = True
+
+#       Second case: A lower function value and derivatives of opposite
+#       sign. The minimum is bracketed. If the cubic step is farther from
+#       stp than the secant step, the cubic step is taken, otherwise the
+#       secant step is taken.
+
+        elif sign < 0:  #case2
+            self.case = 2
+            theta = 3. * (fx - fp) / (stp - stx) + gx + gp
+            s = max(abs(theta), abs(gx), abs(gp))
+            gamma = s * np.sqrt((theta / s) ** 2 - (gx / s) * (gp / s))
+            if stp > stx:
+                 gamma = -gamma
+            p = (gamma - gp) + theta
+            q = ((gamma - gp) + gamma) + gx
+            r = p / q
+            stpc = stp + r * (stx - stp)
+            stpq = stp + (gp / (gp - gx)) * (stx - stp)
+            if (abs(stpc - stp) > abs(stpq - stp)):
+               stpf = stpc
+            else:
+               stpf = stpq
+            self.bracket = True
+
+#       Third case: A lower function value, derivatives of the same sign,
+#       and the magnitude of the derivative decreases.
+
+        elif abs(gp) < abs(gx):  #case3
+            self.case = 3
+#           The cubic step is computed only if the cubic tends to infinity
+#           in the direction of the step or if the minimum of the cubic
+#           is beyond stp. Otherwise the cubic step is defined to be the
+#           secant step.
+
+            theta = 3. * (fx - fp) / (stp - stx) + gx + gp
+            s = max(abs(theta), abs(gx), abs(gp))
+
+#           The case gamma = 0 only arises if the cubic does not tend
+#           to infinity in the direction of the step.
+
+            gamma = s * np.sqrt(max(0.,(theta / s) ** 2-(gx / s) * (gp / s)))
+            if stp > stx:
+                gamma = -gamma
+            p = (gamma - gp) + theta
+            q = (gamma + (gx - gp)) + gamma
+            r = p / q
+            if r < 0. and gamma != 0:
+               stpc = stp + r * (stx - stp)
+            elif stp > stx:
+               stpc = stpmax
+            else:
+               stpc = stpmin
+            stpq = stp + (gp / (gp - gx)) * (stx - stp)
+
+            if self.bracket:
+
+#               A minimizer has been bracketed. If the cubic step is
+#               closer to stp than the secant step, the cubic step is
+#               taken, otherwise the secant step is taken.
+
+                if abs(stpc - stp) < abs(stpq - stp):
+                    stpf = stpc
+                else:
+                    stpf = stpq
+                if stp > stx:
+                    stpf = min(stp + .66 * (sty - stp), stpf)
+                else:
+                    stpf = max(stp + .66 * (sty - stp), stpf)
+            else:
+
+#               A minimizer has not been bracketed. If the cubic step is
+#               farther from stp than the secant step, the cubic step is
+#               taken, otherwise the secant step is taken.
+
+                if abs(stpc - stp) > abs(stpq - stp):
+                   stpf = stpc
+                else:
+                   stpf = stpq
+                stpf = min(stpmax, stpf)
+                stpf = max(stpmin, stpf)
+
+#       Fourth case: A lower function value, derivatives of the same sign,
+#       and the magnitude of the derivative does not decrease. If the
+#       minimum is not bracketed, the step is either minstep or maxstep,
+#       otherwise the cubic step is taken.
+
+        else:  #case4
+            self.case = 4
+            if self.bracket:
+                theta = 3. * (fp - fy) / (sty - stp) + gy + gp
+                s = max(abs(theta), abs(gy), abs(gp))
+                gamma = s * np.sqrt((theta / s) ** 2 - (gy / s) * (gp / s))
+                if stp > sty:
+                    gamma = -gamma
+                p = (gamma - gp) + theta
+                q = ((gamma - gp) + gamma) + gy
+                r = p / q
+                stpc = stp + r * (sty - stp)
+                stpf = stpc
+            elif stp > stx:
+                stpf = stpmax
+            else:
+                stpf = stpmin
+
+#       Update the interval which contains a minimizer.
+
+        if fp > fx:
+            sty = stp
+            fy = fp
+            gy = gp
+        else:
+            if sign < 0:
+                sty = stx
+                fy = fx
+                gy = gx
+            stx = stp
+            fx = fp
+            gx = gp
+#       Compute the new step.
+
+        stp = self.determine_step(stpf)
+
+        return stx, sty, stp, gx, fx, gy, fy
+
+    def determine_step(self, stp):
+        dr = stp - self.old_stp
+        if abs(pymax(self.pk) * dr) > self.maxstep:
+            dr /= abs((pymax(self.pk) * dr) / self.maxstep)
+        stp = self.old_stp + dr
+        return stp
+
+    def save(self, data):
+        if self.bracket:
+            self.isave[0] = 1
+        else:
+            self.isave[0] = 0
+        self.isave[1] = data[0]
+        self.dsave = data[1:]
diff --git a/ase/utils/memory.py b/ase/utils/memory.py
new file mode 100644
index 0000000..63a4e03
--- /dev/null
+++ b/ase/utils/memory.py
@@ -0,0 +1,447 @@
+import os
+import numpy as np
+
+from UserDict import DictMixin
+
+# -------------------------------------------------------------------
+
+class MemoryBase(object, DictMixin):
+    """Virtual memory (VM) statistics of the current process
+    obtained from the relevant entries in /proc/<pid>/status:
+    VmPeak       Peak virtual memory size in bytes.
+    VmLck        ???
+    VmHWM        Peak resident set size ("high water mark") in bytes.
+    VmRSS        Resident memory usage in bytes.
+    VmSize       VM usage of the entire process in bytes.
+    VmData       VM usage of heap in bytes.
+    VmStk        VM usage of stack in bytes.
+    VmExe        VM usage of exe's and statically linked libraries in bytes.
+    VmLib        VM usage of dynamically linked libraries in bytes.
+    VmPTE        ???
+
+    Note that VmSize > VmData + VmStk + VmExe + VmLib due to overhead.
+    """
+
+    _scale = {'KB':1024.0, 'MB':1024.0**2}
+    _keys = ('VmPeak', 'VmLck', 'VmHWM', 'VmRSS', 'VmSize', 'VmData', \
+            'VmStk', 'VmExe', 'VmLib', 'VmPTE')
+
+    def __init__(self, verbose=0):
+        self.verbose = verbose
+        if self.verbose>=2: print 'MemoryBase.__init__'
+        object.__init__(self)
+        self._values = np.empty(len(self._keys), dtype=np.float)
+
+    def __repr__(self):
+        """Return a representation of recorded VM statistics.
+        x.__repr__() <==> repr(x)"""
+        if self.verbose>=2: print 'MemoryBase.__repr__'
+        s = object.__repr__(self)
+        w = max(map(len, self._keys))
+        unit = 'MB'
+        for k,v in self.iteritems():
+            res = '<N/A>'
+            if not np.isnan(v):
+                res = '%8.3f %s' % (v/self._scale[unit], unit)
+            s += '\n\t' + k.ljust(w) + ': ' + res.rjust(8)
+        return s
+
+    def __len__(self):
+        """Number of VM keys which have not been outdated.
+        x.__len__() <==> len(x)"""
+        if self.verbose>=3: print 'MemoryBase.__len__'
+        return np.sum(~np.isnan(self._values))
+
+    def __getitem__(self, key):
+        """Return floating point number associated with a VM key.
+        x.__getitem__(y) <==> x[y]"""
+        if self.verbose>=2: print 'MemoryBase.__getitem__'
+        if key not in self:
+            raise KeyError(key)
+        i = self.keys().index(key)
+        return self._values[i]
+
+    def __setitem__(self, key, value):
+        """x.__setitem__(i, y) <==> x[i]=y"""
+        if self.verbose>=2: print 'MemoryBase.__setitem__'
+        raise Exception('Virtual member function.')
+
+    def __delitem__(self, key):
+        """x.__delitem__(y) <==> del x[y]"""
+        if self.verbose>=2: print 'MemoryBase.__delitem__'
+        raise Exception('Virtual member function.')
+
+    def clear(self):
+        """D.clear() -> None.  Remove all items from D."""
+        if self.verbose>=1: print 'MemoryBase.clear'
+        raise Exception('Virtual member function.')
+
+    def update(self, other=None):
+        """D.update(E) -> None.  Update D from E: for k in E.keys(): D[k] = E[k]"""
+        if self.verbose>=1: print 'MemoryBase.update'
+        DictMixin.update(self, other)
+
+    def copy(self):
+        """Return a shallow copy of a VM statistics instance.
+        D.copy() -> a shallow copy of D"""
+        if self.verbose>=1: print 'MemoryBase.copy'
+        res = object.__new__(self.__class__)
+        MemoryBase.__init__(res, self.verbose)
+        DictMixin.update(res, self)
+        return res
+
+    def has_key(self, key): #necessary to avoid infinite recursion
+        """Return boolean to indicate whether key is a supported VM key.
+        D.has_key(k) -> True if D has a key k, else False"""
+        if self.verbose>=3: print 'MemoryBase.has_key'
+        return key in self._keys
+
+    def keys(self):
+        """Return list of supported VM keys.
+        D.keys() -> list of D's keys"""
+        if self.verbose>=3: print 'MemoryBase.keys'
+        return list(self._keys)
+
+    def values(self):
+        """Return list of recorded VM statistics.
+        D.values() -> list of D's values"""
+        if self.verbose>=3: print 'MemoryBase.values'
+        return list(self._values)
+
+    def get(self, key, default=None):
+        """Return floating point number associated with a VM key.
+        D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None."""
+        if self.verbose>=1: print 'MemoryBase.get'
+        v = self[key]
+
+        if type(default) in [int,float]:
+            default = np.float_(default)
+        if default is not None and not isinstance(default, np.floating):
+            raise ValueError('Default value must be a floating point number.')
+
+        if default is not None and np.isnan(v):
+            return default
+        else:
+            return v
+
+    def setdefault(self, key, default=None):
+        """Return floating point number associated with a VM key.
+        D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"""
+        if self.verbose>=1: print 'MemoryBase.setdefault'
+        v = self[key]
+
+        if type(default) in [int,float]:
+            default = np.float_(default)
+        if default is not None and not isinstance(default, np.floating):
+            raise ValueError('Default value must be a floating point number.')
+
+        if default is not None and np.isnan(v):
+            self[key] = default
+            return default
+        else:
+            return v
+
+    def pop(self, key, default=None):
+        """Return floating point number for a VM key and mark it as outdated.
+        D.pop(k[,d]) -> v, remove specified key and return the corresponding value
+        If key is not found, d is returned if given, otherwise KeyError is raised"""
+
+        if self.verbose>=1: print 'MemoryBase.pop'
+        v = self[key]
+
+        if type(default) in [int,float]:
+            default = np.float_(default)
+        if default is not None and not isinstance(default, np.floating):
+            raise ValueError('Default value must be a floating point number.')
+
+        if default is not None and np.isnan(v):
+            return default
+        else:
+            del self[key]
+            return v
+
+    def popitem(self):
+        """Return floating point number for some not-yet outdated VM key.
+        D.popitem() -> (k, v), remove and return some (key, value) pair as a
+        2-tuple; but raise KeyError if D is empty"""
+        if self.verbose>=1: print 'MemoryBase.popitem'
+
+        for k,v in self.iteritems():
+            if not np.isnan(v):
+                del self[k]
+                return (k,v)
+        raise KeyError
+
+    def __add__(self, other):
+        """x.__add__(y) <==> x+y"""
+        if self.verbose>=1: print 'MemoryBase.__add__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        res = self.copy()
+        if isinstance(other, MemoryBase):
+            res._values.__iadd__(other._values)
+        elif type(other) in [int,float]:
+            res._values.__iadd__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return res
+
+    def __sub__(self, other):
+        """x.__sub__(y) <==> x-y"""
+        if self.verbose>=1: print 'MemoryBase.__sub__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        res = self.copy()
+        if isinstance(other, MemoryBase):
+            res._values.__isub__(other._values)
+        elif type(other) in [int,float]:
+            res._values.__isub__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return res
+
+    def __radd__(self, other):
+        """x.__radd__(y) <==> y+x"""
+        if self.verbose>=1: print 'MemoryBase.__radd__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        res = self.copy()
+        if isinstance(other, MemoryBase):
+            res._values.__iadd__(other._values)
+        elif type(other) in [int,float]:
+            res._values.__iadd__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return res
+
+    def __rsub__(self, other):
+        """x.__rsub__(y) <==> y-x"""
+        if self.verbose>=1: print 'MemoryBase.__rsub__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        res = self.copy()
+        res._values.__imul__(-1.0)
+        if isinstance(other, MemoryBase):
+            res._values.__iadd__(other._values)
+        elif type(other) in [int,float]:
+            res._values.__iadd__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return res
+
+# -------------------------------------------------------------------
+
+class MemoryStatistics(MemoryBase):
+
+    def __init__(self, verbose=0):
+        MemoryBase.__init__(self, verbose)
+        self.update()
+
+    def __setitem__(self, key, value):
+        """Set VM key to a floating point number.
+        x.__setitem__(i, y) <==> x[i]=y"""
+        if self.verbose>=2: print 'MemoryStatistics.__setitem__'
+        if key not in self:
+            raise KeyError(key)
+        if type(value) in [int,float]:
+            value = np.float_(value)
+        if not isinstance(value, np.floating):
+            raise ValueError('Value must be a floating point number.')
+        i = self.keys().index(key)
+        self._values[i] = value
+
+    def __delitem__(self, key):
+        """Mark a VK key as outdated.
+        x.__delitem__(y) <==> del x[y]"""
+        if self.verbose>=2: print 'MemoryStatistics.__delitem__'
+        if key not in self:
+            raise KeyError(key)
+        self[key] = np.nan
+
+    def clear(self):
+        """Mark all supported VM keys as outdated.
+        D.clear() -> None.  Remove all items from D."""
+        if self.verbose>=1: print 'MemoryStatistics.clear'
+        self._values[:] = np.nan
+
+    def refresh(self):
+        """Refresh all outdated VM keys by reading /proc/<pid>/status."""
+        if self.verbose>=1: print 'MemoryBase.refresh'
+
+        # NB: Linux /proc is for humans; Solaris /proc is for programs!
+        # TODO: Use pipe from 'prstat -p <pid>' or 'pmap -x <pid> 1 1'
+
+        # Skip refresh if none are outdated (i.e. nan)
+        if not np.isnan(self._values).any():
+            if self.verbose>=2: print 'refresh: skipping...'
+            return
+
+        try:
+            f = open('/proc/%d/status' % os.getpid(), 'r')
+            for line in f:
+                k, v = line.decode('ascii').split(':')
+
+                # Only refresh supported keys that are outdated (i.e. nan)
+                if k in self and np.isnan(self[k]):
+                    t, s = v.strip().split(None, 1)
+                    if self.verbose >= 2:
+                        print 'refresh: k=%s, t=%s, s=%s' % (k, t, s)
+                    self[k] = float(t) * self._scale[s.upper()]
+
+            f.close()
+        except (IOError, UnicodeError, ValueError):
+            # Reset on error
+            self.clear()
+
+    def update(self, other=None):
+        """Update VM statistics from a supplied dict, else clear and refresh.
+        D.update(E) -> None.  Update D from E: for k in E.keys(): D[k] = E[k]"""
+        if self.verbose>=1: print 'MemoryStatistics.update'
+
+        # Call to update without arguments has special meaning
+        if other is None:
+            self.clear()
+            self.refresh()
+        else:
+            MemoryBase.update(self, other)
+
+    def __iadd__(self, other):
+        """x.__iadd__(y) <==> x+=y"""
+        if self.verbose>=1: print 'MemoryStatistics.__iadd__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        if isinstance(other, MemoryBase):
+            self._values.__iadd__(other._values)
+        elif type(other) in [int,float]:
+            self._values.__iadd__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return self
+
+    def __isub__(self, other):
+        """x.__isub__(y) <==> x-=y"""
+        if self.verbose>=1: print 'MemoryStatistics.__isub__(%s,%s)' \
+            % (object.__repr__(self), object.__repr__(other))
+        if isinstance(other, MemoryBase):
+            self._values.__isub__(other._values)
+        elif type(other) in [int,float]:
+            self._values.__isub__(other)
+        else:
+            raise TypeError('Unsupported operand type')
+        return self
+
+# -------------------------------------------------------------------
+
+#http://www.eecho.info/Echo/python/singleton/
+#http://mail.python.org/pipermail/python-list/2007-July/622333.html
+
+class Singleton(object):
+    """A Pythonic Singleton object."""
+    def __new__(cls, *args, **kwargs):
+        if '_inst' not in vars(cls):
+            cls._inst = object.__new__(cls, *args, **kwargs)
+            #cls._inst = super(type, cls).__new__(cls, *args, **kwargs)
+        return cls._inst
+
+class MemorySingleton(MemoryBase, Singleton):
+    __doc__ = MemoryBase.__doc__ + """
+    The singleton variant is immutable once it has been instantiated, which
+    makes it suitable for recording the initial overhead of starting Python."""
+
+    def __init__(self, verbose=0):
+        if verbose>=1: print 'MemorySingleton.__init__'
+        if '_values' not in vars(self):
+            if verbose>=1: print 'MemorySingleton.__init__ FIRST!'
+            # Hack to circumvent singleton immutability
+            self.__class__ = MemoryStatistics
+            self.__init__(verbose)
+            self.__class__ = MemorySingleton
+
+    def __setitem__(self, key, value):
+        """Disabled for the singleton.
+        x.__setitem__(i, y) <==> x[i]=y"""
+        if self.verbose>=2: print 'MemorySingleton.__setitem__'
+        raise ReferenceError('Singleton is immutable.')
+
+    def __delitem__(self, key):
+        """Disabled for the singleton.
+        x.__delitem__(y) <==> del x[y]"""
+        if self.verbose>=2: print 'MemorySingleton.__delitem__'
+        raise ReferenceError('Singleton is immutable.')
+
+    def clear(self):
+        """Disabled for the singleton.
+        D.clear() -> None.  Remove all items from D."""
+        if self.verbose>=1: print 'MemorySingleton.clear'
+        raise ReferenceError('Singleton is immutable.')
+
+    def update(self):
+        """Disabled for the singleton.
+        D.update(E) -> None.  Update D from E: for k in E.keys(): D[k] = E[k]"""
+        if self.verbose>=1: print 'MemorySingleton.update'
+        raise ReferenceError('Singleton is immutable.')
+
+    def copy(self):
+        """Return a shallow non-singleton copy of a VM statistics instance.
+        D.copy() -> a shallow copy of D"""
+        if self.verbose>=1: print 'MemorySingleton.copy'
+        # Hack to circumvent singleton self-copy
+        self.__class__ = MemoryStatistics
+        res = self.copy()
+        self.__class__ = MemorySingleton
+        return res
+
+# Make sure singleton is instantiated
+MemorySingleton()
+
+# -------------------------------------------------------------------
+
+# Helper functions for leak testing with NumPy arrays
+
+def shapegen(size, ndims, ecc=0.5):
+    """Return a generator of an N-dimensional array shape
+    which approximately contains a given number of elements.
+
+        size:       int or long in [1,inf[
+                    The total number of elements
+        ndims=3:    int in [1,inf[
+                    The number of dimensions
+        ecc=0.5:    float in ]0,1[
+                    The eccentricity of the distribution
+    """
+    assert type(size) in [int,float] and size>=1
+    assert type(ndims) is int and ndims>=1
+    assert type(ecc) in [int,float] and ecc>0 and ecc<1
+
+    for i in range(ndims-1):
+        scale = size**(1.0/(ndims-i))
+        c = round(np.random.uniform((1-ecc)*scale, 1.0/(1-ecc)*scale))
+        size/=c
+        yield c
+    yield round(size)
+
+def shapeopt(maxseed, size, ndims, ecc=0.5):
+    """Return optimal estimate of an N-dimensional array shape
+    which is closest to containing a given number of elements.
+
+        maxseed:    int in [1,inf[
+                    The maximal number of seeds to try
+        size:       int or long in [1,inf[
+                    The total number of elements
+        ndims=3:    int in [1,inf[
+                    The number of dimensions
+        ecc=0.5:    float in ]0,1[
+                    The eccentricity of the distribution
+    """
+    assert type(maxseed) is int and maxseed>=1
+    assert type(size) in [int,float] and size>=1
+    assert type(ndims) is int and ndims>=1
+    assert type(ecc) in [int,float] and ecc>0 and ecc<1
+
+    digits_best = np.inf
+    shape_best = None
+    for seed in range(maxseed):
+        np.random.seed(seed)
+        shape = tuple(shapegen(size, ndims, ecc))
+        if np.prod(shape) == size:
+            return -np.inf, shape
+        digits = np.log10(abs(np.prod(shape)-size))
+        if digits < digits_best:
+            (digits_best, shape_best) = (digits, shape)
+    return digits_best, shape_best
+
diff --git a/ase/utils/molecule_test.py b/ase/utils/molecule_test.py
new file mode 100755
index 0000000..49d3c24
--- /dev/null
+++ b/ase/utils/molecule_test.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+"""This module defines extensible classes for running tests on molecules.
+
+Use this to compare different calculators, XC functionals and so on by
+calculating e.g. atomization energies and bond lengths across the
+g2 database of small molecules.
+"""
+
+import os
+import sys
+import traceback
+
+import numpy as np
+
+from ase import PickleTrajectory, read
+from ase.calculators.emt import EMT
+from ase.data.molecules import molecule, atoms as g2_atoms, g1
+
+
+class BatchTest:
+    """Contains logic for looping over tests and file management."""
+    def __init__(self, test):
+        self.test = test
+        self.txt = sys.stdout # ?
+
+    def run_single_test(self, formula):
+        print >> self.txt, self.test.name, formula, '...',
+        self.txt.flush()
+        filename = self.test.get_filename(formula)
+        if os.path.exists(filename):
+            print >> self.txt, 'Skipped.'
+            return
+        try:
+            open(filename, 'w').close() # Empty file
+            system, calc = self.test.setup(formula)
+            self.test.run(formula, system, filename)
+            print >> self.txt, 'OK!'
+            self.txt.flush()
+        except self.test.exceptions:
+            print >> self.txt, 'Failed!'
+            traceback.print_exc(file=self.txt)
+            print >> self.txt
+            self.txt.flush()
+
+    def run(self, formulas):
+        """Run a batch of tests.
+
+        This will invoke the test method on each formula, printing
+        status to stdout.
+
+        Those formulas that already have test result files will
+        be skipped."""
+        
+        # Create directories if necessary
+        if self.test.dir and not os.path.isdir(self.test.dir):
+            os.mkdir(self.test.dir) # Won't work on 'dir1/dir2', but oh well
+        
+        for formula in formulas:
+            self.run_single_test(formula)
+    
+    def collect(self, formulas, verbose=False):
+        """Yield results of previous calculations."""
+        for formula in formulas:
+            try:
+                filename = self.test.get_filename(formula)
+                results = self.test.retrieve(formula, filename)
+                if verbose:
+                    print >> self.txt, 'Loaded:', formula, filename
+                yield formula, results
+            except (IOError, RuntimeError, TypeError):
+                # XXX which errors should we actually catch?
+                if verbose:
+                    print >> self.txt, 'Error:', formula, '[%s]' % filename
+                    traceback.print_exc(file=self.txt)
+
+
+class MoleculeTest:
+    """Generic class for runnings various tests on the g2 dataset.
+
+    Usage: instantiate MoleculeTest with desired test settings and
+    invoke its run() method on the desired formulas.
+
+    This class will use the ASE EMT calculator by default.  You can
+    create a subclass using an arbitrary calculator by overriding the
+    setup_calculator method.  Most methods can be overridden to
+    provide highly customized behaviour.  """
+    
+    def __init__(self, name, vacuum=6.0, exceptions=None):
+        """Create a molecule test.
+
+        The name parameter will be part of all output files generated
+        by this molecule test.  If name contains a '/' character, the
+        preceding part will be interpreted as a directory in which to
+        put files.
+
+        The vacuum parameter is used to set the cell size.
+
+        A tuple of exception types can be provided which will be
+        caught during a batch of calculations.  Types not specified
+        will be considered fatal."""
+
+        dir, path = os.path.split(name)
+        self.dir = dir
+        self.name = name
+        self.vacuum = vacuum
+        if exceptions is None:
+            exceptions = ()
+        self.exceptions = exceptions
+
+    def setup_calculator(self, system, formula):
+        """Create a new calculator.
+
+        Default is an EMT calculator.  Most implementations will want to
+        override this method."""
+        raise NotImplementedError
+
+    def setup_system(self, formula):
+        """Create an Atoms object from the given formula.
+
+        By default this will be loaded from the g2 database, setting
+        the cell size by means of the molecule test's vacuum parameter."""
+        system = molecule(formula)
+        system.center(vacuum=self.vacuum)
+        return system
+
+    def setup(self, formula):
+        """Build calculator and atoms objects.
+
+        This will invoke the setup_calculator and setup_system methods."""
+        system = self.setup_system(formula)
+        calc = self.setup_calculator(system, formula)
+        system.set_calculator(calc)
+        return system, calc
+        
+    def get_filename(self, formula, extension='traj'):
+        """Returns the filename for a test result file.
+
+        Default format is <name>.<formula>.traj
+
+        The test may write other files, but this filename is used as a
+        flag denoting whether the calculation has been done
+        already."""
+        return '.'.join([self.name, formula, extension])
+
+    def run(self, formula, system, filename):
+        raise NotImplementedError
+
+    def retrieve(self, formula, filename):
+        """Retrieve results of previous calculation from file.
+
+        Default implementation returns the total energy.
+
+        This method should be overridden whenever the test method is
+        overridden to calculate something else than the total energy."""
+        raise NotImplementedError
+
+
+class EnergyTest:
+    def run(self, formula, system, filename):
+        """Calculate energy of specified system and save to file."""
+        system.get_potential_energy()
+         # Won't create .bak file:
+        traj = PickleTrajectory(open(filename, 'w'), 'w')
+        traj.write(system)
+        traj.close()
+
+    def retrieve(self, formula, filename):
+        system = read(filename)
+        energy = system.get_potential_energy()
+        return energy
+    
+    def calculate_atomization_energies(self, molecular_energies,
+                                       atomic_energies):
+        atomic_energy_dict = dict(atomic_energies)
+        for formula, molecular_energy in molecular_energies:
+            try:
+                system = molecule(formula)
+                atomic = [atomic_energy_dict[s]
+                          for s in system.get_chemical_symbols()]            
+                atomization_energy = molecular_energy - sum(atomic)
+                yield formula, atomization_energy
+            except KeyError:
+                pass
+
+
+class BondLengthTest:
+    def run(self, formula, system, filename):
+        """Calculate bond length of a dimer.
+
+        This will calculate total energies for varying atomic
+        separations close to the g2 bond length, allowing
+        determination of bond length by fitting.
+        """
+        if len(system) != 2:
+            raise ValueError('Not a dimer')
+        traj = PickleTrajectory(open(filename, 'w'), 'w')
+        pos = system.positions
+        d = np.linalg.norm(pos[1] - pos[0])
+        for x in range(-2, 3):
+            system.set_distance(0, 1, d * (1.0 + x * 0.02))
+            traj.write(system)
+        traj.close()
+    
+    def retrieve(self, formula, filename):
+        traj = PickleTrajectory(filename, 'r')
+        distances = np.array([np.linalg.norm(a.positions[1] - a.positions[0])
+                              for a in traj])
+        energies = np.array([a.get_potential_energy() for a in traj])
+        polynomial = np.polyfit(distances, energies, 2) # or maybe 3rd order?
+        # With 3rd order it is not always obvious which root is right
+        pderiv = np.polyder(polynomial, 1)
+        d0 = np.roots(pderiv)
+        e0 = np.polyval(energies, d0)
+        return distances, energies, d0, e0, polynomial
+    
+
+class EMTTest(MoleculeTest):
+    def setup_calculator(self, system, calculator):
+        return EMT()
+
+
+class EMTEnergyTest(EnergyTest, EMTTest):
+    pass
+
+
+class EMTBondLengthTest(BondLengthTest, EMTTest):
+    pass
+
+
+def main():
+    supported_elements = 'Ni, C, Pt, Ag, H, Al, O, N, Au, Pd, Cu'.split(', ')
+    formulas = [formula for formula in g1
+                if np.all([symbol in supported_elements
+                           for symbol
+                           in molecule(formula).get_chemical_symbols()])]
+    
+    atoms = [symbol for symbol in g2_atoms if symbol in supported_elements]
+    dimers = [formula for formula in formulas if len(molecule(formula)) == 2]
+
+
+    name1 = 'testfiles/energy'
+    name2 = 'testfiles/bond'
+    test1 = BatchTest(EMTEnergyTest(name1, vacuum=3.0))
+    test2 = BatchTest(EMTBondLengthTest(name2, vacuum=3.0))
+
+    print 'Energy test'
+    print '-----------'
+    test1.run(formulas + atoms)
+
+    print
+    print 'Bond length test'
+    print '----------------'
+    test2.run(dimers)
+
+    print
+    print 'Atomization energies'
+    print '--------------------'
+    atomic_energies = dict(test1.collect(atoms))
+    molecular_energies = dict(test1.collect(formulas))
+    atomization_energies = {}
+    for formula, energy in molecular_energies.iteritems():
+        system = molecule(formula)
+        atomic = [atomic_energies[s] for s in system.get_chemical_symbols()]
+        atomization_energy = energy - sum(atomic)
+        atomization_energies[formula] = atomization_energy
+        print formula.rjust(10), '%.02f' % atomization_energy
+
+    print
+    print 'Bond lengths'
+    print '------------'
+    for formula, (d_i, e_i, d0, e0, poly) in test2.collect(dimers):
+        system = molecule(formula)
+        bref = np.linalg.norm(system.positions[1] - system.positions[0])
+        print formula.rjust(10), '%6.3f' % d0, '  g2ref =', '%2.3f' % bref
+
+        
+if __name__ == '__main__':
+    main()
diff --git a/ase/version.py b/ase/version.py
new file mode 100644
index 0000000..163b613
--- /dev/null
+++ b/ase/version.py
@@ -0,0 +1,11 @@
+# Copyright (C) 2003  CAMP
+# Please see the accompanying LICENSE file for further information.
+
+version_base = '3.6.0'
+
+try:
+    from ase.svnversion import svnversion
+except ImportError:
+    version = version_base
+else:
+    version = version_base + '.' + svnversion
diff --git a/ase/vibrations.py b/ase/vibrations.py
new file mode 100644
index 0000000..0e0dfb7
--- /dev/null
+++ b/ase/vibrations.py
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+
+"""Vibrational modes."""
+
+import pickle
+from math import sin, pi, sqrt
+from os import remove
+from os.path import isfile
+import sys
+
+import numpy as np
+
+import ase.units as units
+from ase.io.trajectory import PickleTrajectory
+from ase.parallel import rank, paropen
+from ase.utils import opencew
+
+
+class Vibrations:
+    """Class for calculating vibrational modes using finite difference.
+
+    The vibrational modes are calculated from a finite difference
+    approximation of the Hessian matrix.
+
+    The *summary()*, *get_energies()* and *get_frequencies()* methods all take
+    an optional *method* keyword.  Use method='Frederiksen' to use the method
+    described in:
+
+      T. Frederiksen, M. Paulsson, M. Brandbyge, A. P. Jauho:
+      "Inelastic transport theory from first-principles: methodology and
+      applications for nanoscale devices", Phys. Rev. B 75, 205413 (2007)
+
+    atoms: Atoms object
+        The atoms to work on.
+    indices: list of int
+        List of indices of atoms to vibrate.  Default behavior is
+        to vibrate all atoms.
+    name: str
+        Name to use for files.
+    delta: float
+        Magnitude of displacements.
+    nfree: int
+        Number of displacements per atom and cartesian coordinate, 2 and 4 are
+        supported. Default is 2 which will displace each atom +delta and
+        -delta for each cartesian coordinate.
+
+    Example:
+
+    >>> from ase import Atoms
+    >>> from ase.calculators import EMT
+    >>> from ase.optimize import BFGS
+    >>> from ase.vibrations import Vibrations
+    >>> n2 = Atoms('N2', [(0, 0, 0), (0, 0, 1.1)],
+    ...            calculator=EMT())
+    >>> BFGS(n2).run(fmax=0.01)
+    BFGS:   0  16:01:21        0.440339       3.2518
+    BFGS:   1  16:01:21        0.271928       0.8211
+    BFGS:   2  16:01:21        0.263278       0.1994
+    BFGS:   3  16:01:21        0.262777       0.0088
+    >>> vib = Vibrations(n2)
+    >>> vib.run()
+    Writing vib.eq.pckl
+    Writing vib.0x-.pckl
+    Writing vib.0x+.pckl
+    Writing vib.0y-.pckl
+    Writing vib.0y+.pckl
+    Writing vib.0z-.pckl
+    Writing vib.0z+.pckl
+    Writing vib.1x-.pckl
+    Writing vib.1x+.pckl
+    Writing vib.1y-.pckl
+    Writing vib.1y+.pckl
+    Writing vib.1z-.pckl
+    Writing vib.1z+.pckl
+    >>> vib.summary()
+    ---------------------
+    #    meV     cm^-1
+    ---------------------
+    0    0.0       0.0
+    1    0.0       0.0
+    2    0.0       0.0
+    3    2.5      20.4
+    4    2.5      20.4
+    5  152.6    1230.8
+    ---------------------
+    Zero-point energy: 0.079 eV
+    >>> vib.write_mode(-1)  # write last mode to trajectory file
+
+    """
+
+    def __init__(self, atoms, indices=None, name='vib', delta=0.01, nfree=2):
+        assert nfree in [2, 4]
+        self.atoms = atoms
+        if indices is None:
+            indices = range(len(atoms))
+        self.indices = np.asarray(indices)
+        self.name = name
+        self.delta = delta
+        self.nfree = nfree
+        self.H = None
+        self.ir = None
+
+    def run(self):
+        """Run the vibration calculations.
+
+        This will calculate the forces for 6 displacements per atom +/-x,
+        +/-y, +/-z. Only those calculations that are not already done will be
+        started. Be aware that an interrupted calculation may produce an empty
+        file (ending with .pckl), which must be deleted before restarting the
+        job. Otherwise the forces will not be calculated for that
+        displacement.
+
+        Note that the calculations for the different displacements can be done
+        simultaneously by several independent processes. This feature relies
+        on the existence of files and the subsequent creation of the file in
+        case it is not found.
+        """
+
+        filename = self.name + '.eq.pckl'
+        fd = opencew(filename)
+        if fd is not None:
+            self.calculate(filename, fd)
+
+        p = self.atoms.positions.copy()
+        for a in self.indices:
+            for i in range(3):
+                for sign in [-1, 1]:
+                    for ndis in range(1, self.nfree // 2 + 1):
+                        filename = ('%s.%d%s%s.pckl' %
+                                    (self.name, a, 'xyz'[i],
+                                     ndis * ' +-'[sign]))
+                        fd = opencew(filename)
+                        if fd is not None:
+                            disp = ndis * sign * self.delta
+                            self.atoms.positions[a, i] = p[a, i] + disp
+                            self.calculate(filename, fd)
+                            self.atoms.positions[a, i] = p[a, i]
+
+    def calculate(self, filename, fd):
+        forces = self.atoms.get_forces()
+        if self.ir:
+            dipole = self.calc.get_dipole_moment(self.atoms)
+        if rank == 0:
+            if self.ir:
+                pickle.dump([forces, dipole], fd)
+                sys.stdout.write(
+                    'Writing %s, dipole moment = (%.6f %.6f %.6f)\n' %
+                    (filename, dipole[0], dipole[1], dipole[2]))
+            else:
+                pickle.dump(forces, fd)
+                sys.stdout.write('Writing %s\n' % filename)
+            fd.close()
+        sys.stdout.flush()
+
+    def clean(self):
+        if isfile(self.name + '.eq.pckl'):
+            remove(self.name + '.eq.pckl')
+
+        for a in self.indices:
+            for i in 'xyz':
+                for sign in '-+':
+                    for ndis in range(1, self.nfree // 2 + 1):
+                        name = '%s.%d%s%s.pckl' % (self.name, a, i,
+                                                   ndis * sign)
+                        if isfile(name):
+                            remove(name)
+
+    def read(self, method='standard', direction='central'):
+        self.method = method.lower()
+        self.direction = direction.lower()
+        assert self.method in ['standard', 'frederiksen']
+        assert self.direction in ['central', 'forward', 'backward']
+
+        n = 3 * len(self.indices)
+        H = np.empty((n, n))
+        r = 0
+        if direction != 'central':
+            feq = pickle.load(open(self.name + '.eq.pckl'))
+        for a in self.indices:
+            for i in 'xyz':
+                name = '%s.%d%s' % (self.name, a, i)
+                fminus = pickle.load(open(name + '-.pckl'))
+                fplus = pickle.load(open(name + '+.pckl'))
+                if self.method == 'frederiksen':
+                    fminus[a] -= fminus.sum(0)
+                    fplus[a] -= fplus.sum(0)
+                if self.nfree == 4:
+                    fminusminus = pickle.load(open(name + '--.pckl'))
+                    fplusplus = pickle.load(open(name + '++.pckl'))
+                    if self.method == 'frederiksen':
+                        fminusminus[a] -= fminusminus.sum(0)
+                        fplusplus[a] -= fplusplus.sum(0)
+                if self.direction == 'central':
+                    if self.nfree == 2:
+                        H[r] = .5 * (fminus - fplus)[self.indices].ravel()
+                    else:
+                        H[r] = H[r] = (-fminusminus +
+                                       8 * fminus -
+                                       8 * fplus +
+                                       fplusplus)[self.indices].ravel() / 12.0
+                elif self.direction == 'forward':
+                    H[r] = (feq - fplus)[self.indices].ravel()
+                else:
+                    assert self.direction == 'backward'
+                    H[r] = (fminus - feq)[self.indices].ravel()
+                H[r] /= 2 * self.delta
+                r += 1
+        H += H.copy().T
+        self.H = H
+        m = self.atoms.get_masses()
+        if 0 in [m[index] for index in self.indices]:
+            raise RuntimeError('Zero mass encountered in one or more of '
+                               'the vibrated atoms. Use Atoms.set_masses()'
+                               ' to set all masses to non-zero values.')
+
+        self.im = np.repeat(m[self.indices] ** -0.5, 3)
+        omega2, modes = np.linalg.eigh(self.im[:, None] * H * self.im)
+        self.modes = modes.T.copy()
+
+        # Conversion factor:
+        s = units._hbar * 1e10 / sqrt(units._e * units._amu)
+        self.hnu = s * omega2.astype(complex) ** 0.5
+
+    def get_energies(self, method='standard', direction='central'):
+        """Get vibration energies in eV."""
+
+        if (self.H is None or method.lower() != self.method or
+            direction.lower() != self.direction):
+            self.read(method, direction)
+        return self.hnu
+
+    def get_frequencies(self, method='standard', direction='central'):
+        """Get vibration frequencies in cm^-1."""
+
+        s = 0.01 * units._e / units._c / units._hplanck
+        return s * self.get_energies(method, direction)
+
+    def summary(self, method='standard', direction='central', freq=None,
+                log=sys.stdout):
+        """Print a summary of the vibrational frequencies.
+
+        Parameters:
+
+        method : string
+            Can be 'standard'(default) or 'Frederiksen'.
+        direction: string
+            Direction for finite differences. Can be one of 'central'
+            (default), 'forward', 'backward'.
+        freq : numpy array
+            Optional. Can be used to create a summary on a set of known
+            frequencies.
+        log : if specified, write output to a different location than
+            stdout. Can be an object with a write() method or the name of a
+            file to create.
+        """
+
+        if isinstance(log, str):
+            log = paropen(log, 'a')
+        write = log.write
+
+        s = 0.01 * units._e / units._c / units._hplanck
+        if freq != None:
+            hnu = freq / s
+        else:
+            hnu = self.get_energies(method, direction)
+        write('---------------------\n')
+        write('  #    meV     cm^-1\n')
+        write('---------------------\n')
+        for n, e in enumerate(hnu):
+            if e.imag != 0:
+                c = 'i'
+                e = e.imag
+            else:
+                c = ' '
+                e = e.real
+            write('%3d %6.1f%s  %7.1f%s\n' % (n, 1000 * e, c, s * e, c))
+        write('---------------------\n')
+        write('Zero-point energy: %.3f eV\n' %
+              self.get_zero_point_energy(freq=freq))
+
+    def get_zero_point_energy(self, freq=None):
+        if freq is None:
+            return 0.5 * self.hnu.real.sum()
+        else:
+            s = 0.01 * units._e / units._c / units._hplanck
+            return 0.5 * freq.real.sum() / s
+
+    def get_mode(self, n):
+        mode = np.zeros((len(self.atoms), 3))
+        mode[self.indices] = (self.modes[n] * self.im).reshape((-1, 3))
+        return mode
+
+    def write_mode(self, n, kT=units.kB * 300, nimages=30):
+        """Write mode to trajectory file."""
+        mode = self.get_mode(n) * sqrt(kT / abs(self.hnu[n]))
+        p = self.atoms.positions.copy()
+        n %= 3 * len(self.indices)
+        traj = PickleTrajectory('%s.%d.traj' % (self.name, n), 'w')
+        calc = self.atoms.get_calculator()
+        self.atoms.set_calculator()
+        for x in np.linspace(0, 2 * pi, nimages, endpoint=False):
+            self.atoms.set_positions(p + sin(x) * mode)
+            traj.write(self.atoms)
+        self.atoms.set_positions(p)
+        self.atoms.set_calculator(calc)
+        traj.close()
+
+    def write_jmol(self):
+        """Writes file for viewing of the modes with jmol."""
+
+        fd = open(self.name + '.xyz', 'w')
+        symbols = self.atoms.get_chemical_symbols()
+        f = self.get_frequencies()
+        for n in range(3 * len(self.indices)):
+            fd.write('%6d\n' % len(self.atoms))
+            if f[n].imag != 0:
+                c = 'i'
+                f[n] = f[n].imag
+            else:
+                c = ' '
+            fd.write('Mode #%d, f = %.1f%s cm^-1' % (n, f[n], c))
+            if self.ir:
+                fd.write(', I = %.4f (D/Å)^2 amu^-1.\n' % self.intensities[n])
+            else:
+                fd.write('.\n')
+            mode = self.get_mode(n)
+            for i, pos in enumerate(self.atoms.positions):
+                fd.write('%2s %12.5f %12.5f %12.5f %12.5f %12.5f %12.5f \n' %
+                         (symbols[i], pos[0], pos[1], pos[2],
+                          mode[i, 0], mode[i, 1], mode[i, 2]))
+        fd.close()
diff --git a/ase/visualize/__init__.py b/ase/visualize/__init__.py
new file mode 100644
index 0000000..c656ac9
--- /dev/null
+++ b/ase/visualize/__init__.py
@@ -0,0 +1,61 @@
+import os
+import tempfile
+
+from ase.io import write
+import ase.parallel as parallel
+from ase.old import OldASEListOfAtomsWrapper
+
+def view(atoms, data=None, viewer='ag', repeat=None, block=False):
+    # Ignore for parallel calculations:
+    if parallel.size != 1:
+        return
+
+    if hasattr(atoms, 'GetUnitCell'):
+        # Convert old ASE ListOfAtoms to new style.
+        atoms = OldASEListOfAtomsWrapper(atoms).copy()
+
+    vwr = viewer.lower()
+    
+    if vwr == 'ag':
+        format = 'traj'
+        if repeat is None:
+            command = 'ag'
+        else:
+            command = 'ag --repeat=%d,%d,%d' % tuple(repeat)
+            repeat = None
+    elif vwr == 'vmd':
+        format = 'cube'
+        command = 'vmd'
+    elif vwr == 'rasmol':
+        format = 'pdb'
+        command = 'rasmol -pdb'
+    elif vwr == 'xmakemol':
+        format = 'xyz'
+        command = 'xmakemol -f'
+    elif vwr == 'gopenmol':
+        format = 'xyz'
+        command = 'rungOpenMol'
+    elif vwr == 'avogadro':
+        format = 'cube'
+        command = 'avogadro'
+    elif vwr == 'sage':
+        from ase.visualize.sage import view_sage_jmol
+        view_sage_jmol(atoms)
+        return
+    else:
+        raise RuntimeError('Unknown viewer: ' + viewer)
+
+    fd, filename = tempfile.mkstemp('.' + format, 'ase-')
+    fd = os.fdopen(fd, 'w')
+    if repeat is not None:
+        atoms = atoms.repeat()
+    if data is None:
+        write(fd, atoms, format=format)
+    else:
+        write(fd, atoms, format=format, data=data)
+    fd.close()
+    if block:
+        os.system('%s %s' % (command, filename))
+    else:
+        os.system('%s %s &' % (command, filename))
+    os.system('(sleep 60; rm %s) &' % filename)
diff --git a/ase/visualize/colortable.py b/ase/visualize/colortable.py
new file mode 100644
index 0000000..702dcb7
--- /dev/null
+++ b/ase/visualize/colortable.py
@@ -0,0 +1,797 @@
+"""Defines a table with X11 colors.
+
+The key to the dictionary is a string with an X11 name for the color,
+the value is either a number (a grayscale value) between 0.0 and 1.0,
+or an array of three numbers (RGB values).
+
+The table was generated by the _MakeColorTable function
+(defined in this module).
+"""
+
+from numpy import array
+
+color_table = {
+    'snow':              array([1.0000, 0.9804, 0.9804]),
+    'ghost white':       array([0.9725, 0.9725, 1.0000]),
+    'GhostWhite':        array([0.9725, 0.9725, 1.0000]),
+    'white smoke':       0.9608,
+    'WhiteSmoke':        0.9608,
+    'gainsboro':         0.8627,
+    'floral white':      array([1.0000, 0.9804, 0.9412]),
+    'FloralWhite':       array([1.0000, 0.9804, 0.9412]),
+    'old lace':          array([0.9922, 0.9608, 0.9020]),
+    'OldLace':           array([0.9922, 0.9608, 0.9020]),
+    'linen':             array([0.9804, 0.9412, 0.9020]),
+    'antique white':     array([0.9804, 0.9216, 0.8431]),
+    'AntiqueWhite':      array([0.9804, 0.9216, 0.8431]),
+    'papaya whip':       array([1.0000, 0.9373, 0.8353]),
+    'PapayaWhip':        array([1.0000, 0.9373, 0.8353]),
+    'blanched almond':   array([1.0000, 0.9216, 0.8039]),
+    'BlanchedAlmond':    array([1.0000, 0.9216, 0.8039]),
+    'bisque':            array([1.0000, 0.8941, 0.7686]),
+    'peach puff':        array([1.0000, 0.8549, 0.7255]),
+    'PeachPuff':         array([1.0000, 0.8549, 0.7255]),
+    'navajo white':      array([1.0000, 0.8706, 0.6784]),
+    'NavajoWhite':       array([1.0000, 0.8706, 0.6784]),
+    'moccasin':          array([1.0000, 0.8941, 0.7098]),
+    'cornsilk':          array([1.0000, 0.9725, 0.8627]),
+    'ivory':             array([1.0000, 1.0000, 0.9412]),
+    'lemon chiffon':     array([1.0000, 0.9804, 0.8039]),
+    'LemonChiffon':      array([1.0000, 0.9804, 0.8039]),
+    'seashell':          array([1.0000, 0.9608, 0.9333]),
+    'honeydew':          array([0.9412, 1.0000, 0.9412]),
+    'mint cream':        array([0.9608, 1.0000, 0.9804]),
+    'MintCream':         array([0.9608, 1.0000, 0.9804]),
+    'azure':             array([0.9412, 1.0000, 1.0000]),
+    'alice blue':        array([0.9412, 0.9725, 1.0000]),
+    'AliceBlue':         array([0.9412, 0.9725, 1.0000]),
+    'lavender':          array([0.9020, 0.9020, 0.9804]),
+    'lavender blush':    array([1.0000, 0.9412, 0.9608]),
+    'LavenderBlush':     array([1.0000, 0.9412, 0.9608]),
+    'misty rose':        array([1.0000, 0.8941, 0.8824]),
+    'MistyRose':         array([1.0000, 0.8941, 0.8824]),
+    'white':             1.0000,
+    'black':             0.0000,
+    'dark slate gray':   array([0.1843, 0.3098, 0.3098]),
+    'DarkSlateGray':     array([0.1843, 0.3098, 0.3098]),
+    'dark slate grey':   array([0.1843, 0.3098, 0.3098]),
+    'DarkSlateGrey':     array([0.1843, 0.3098, 0.3098]),
+    'dim gray':          0.4118,
+    'DimGray':           0.4118,
+    'dim grey':          0.4118,
+    'DimGrey':           0.4118,
+    'slate gray':        array([0.4392, 0.5020, 0.5647]),
+    'SlateGray':         array([0.4392, 0.5020, 0.5647]),
+    'slate grey':        array([0.4392, 0.5020, 0.5647]),
+    'SlateGrey':         array([0.4392, 0.5020, 0.5647]),
+    'light slate gray':  array([0.4667, 0.5333, 0.6000]),
+    'LightSlateGray':    array([0.4667, 0.5333, 0.6000]),
+    'light slate grey':  array([0.4667, 0.5333, 0.6000]),
+    'LightSlateGrey':    array([0.4667, 0.5333, 0.6000]),
+    'gray':              0.7451,
+    'grey':              0.7451,
+    'light grey':        0.8275,
+    'LightGrey':         0.8275,
+    'light gray':        0.8275,
+    'LightGray':         0.8275,
+    'midnight blue':     array([0.0980, 0.0980, 0.4392]),
+    'MidnightBlue':      array([0.0980, 0.0980, 0.4392]),
+    'navy':              array([0.0000, 0.0000, 0.5020]),
+    'navy blue':         array([0.0000, 0.0000, 0.5020]),
+    'NavyBlue':          array([0.0000, 0.0000, 0.5020]),
+    'cornflower blue':   array([0.3922, 0.5843, 0.9294]),
+    'CornflowerBlue':    array([0.3922, 0.5843, 0.9294]),
+    'dark slate blue':   array([0.2824, 0.2392, 0.5451]),
+    'DarkSlateBlue':     array([0.2824, 0.2392, 0.5451]),
+    'slate blue':        array([0.4157, 0.3529, 0.8039]),
+    'SlateBlue':         array([0.4157, 0.3529, 0.8039]),
+    'medium slate blue': array([0.4824, 0.4078, 0.9333]),
+    'MediumSlateBlue':   array([0.4824, 0.4078, 0.9333]),
+    'light slate blue':  array([0.5176, 0.4392, 1.0000]),
+    'LightSlateBlue':    array([0.5176, 0.4392, 1.0000]),
+    'medium blue':       array([0.0000, 0.0000, 0.8039]),
+    'MediumBlue':        array([0.0000, 0.0000, 0.8039]),
+    'royal blue':        array([0.2549, 0.4118, 0.8824]),
+    'RoyalBlue':         array([0.2549, 0.4118, 0.8824]),
+    'blue':              array([0.0000, 0.0000, 1.0000]),
+    'dodger blue':       array([0.1176, 0.5647, 1.0000]),
+    'DodgerBlue':        array([0.1176, 0.5647, 1.0000]),
+    'deep sky blue':     array([0.0000, 0.7490, 1.0000]),
+    'DeepSkyBlue':       array([0.0000, 0.7490, 1.0000]),
+    'sky blue':          array([0.5294, 0.8078, 0.9216]),
+    'SkyBlue':           array([0.5294, 0.8078, 0.9216]),
+    'light sky blue':    array([0.5294, 0.8078, 0.9804]),
+    'LightSkyBlue':      array([0.5294, 0.8078, 0.9804]),
+    'steel blue':        array([0.2745, 0.5098, 0.7059]),
+    'SteelBlue':         array([0.2745, 0.5098, 0.7059]),
+    'light steel blue':  array([0.6902, 0.7686, 0.8706]),
+    'LightSteelBlue':    array([0.6902, 0.7686, 0.8706]),
+    'light blue':        array([0.6784, 0.8471, 0.9020]),
+    'LightBlue':         array([0.6784, 0.8471, 0.9020]),
+    'powder blue':       array([0.6902, 0.8784, 0.9020]),
+    'PowderBlue':        array([0.6902, 0.8784, 0.9020]),
+    'pale turquoise':    array([0.6863, 0.9333, 0.9333]),
+    'PaleTurquoise':     array([0.6863, 0.9333, 0.9333]),
+    'dark turquoise':    array([0.0000, 0.8078, 0.8196]),
+    'DarkTurquoise':     array([0.0000, 0.8078, 0.8196]),
+    'medium turquoise':  array([0.2824, 0.8196, 0.8000]),
+    'MediumTurquoise':   array([0.2824, 0.8196, 0.8000]),
+    'turquoise':         array([0.2510, 0.8784, 0.8157]),
+    'cyan':              array([0.0000, 1.0000, 1.0000]),
+    'light cyan':        array([0.8784, 1.0000, 1.0000]),
+    'LightCyan':         array([0.8784, 1.0000, 1.0000]),
+    'cadet blue':        array([0.3725, 0.6196, 0.6275]),
+    'CadetBlue':         array([0.3725, 0.6196, 0.6275]),
+    'medium aquamarine': array([0.4000, 0.8039, 0.6667]),
+    'MediumAquamarine':  array([0.4000, 0.8039, 0.6667]),
+    'aquamarine':        array([0.4980, 1.0000, 0.8314]),
+    'dark green':        array([0.0000, 0.3922, 0.0000]),
+    'DarkGreen':         array([0.0000, 0.3922, 0.0000]),
+    'dark olive green':  array([0.3333, 0.4196, 0.1843]),
+    'DarkOliveGreen':    array([0.3333, 0.4196, 0.1843]),
+    'dark sea green':    array([0.5608, 0.7373, 0.5608]),
+    'DarkSeaGreen':      array([0.5608, 0.7373, 0.5608]),
+    'sea green':         array([0.1804, 0.5451, 0.3412]),
+    'SeaGreen':          array([0.1804, 0.5451, 0.3412]),
+    'medium sea green':  array([0.2353, 0.7020, 0.4431]),
+    'MediumSeaGreen':    array([0.2353, 0.7020, 0.4431]),
+    'light sea green':   array([0.1255, 0.6980, 0.6667]),
+    'LightSeaGreen':     array([0.1255, 0.6980, 0.6667]),
+    'pale green':        array([0.5961, 0.9843, 0.5961]),
+    'PaleGreen':         array([0.5961, 0.9843, 0.5961]),
+    'spring green':      array([0.0000, 1.0000, 0.4980]),
+    'SpringGreen':       array([0.0000, 1.0000, 0.4980]),
+    'lawn green':        array([0.4863, 0.9882, 0.0000]),
+    'LawnGreen':         array([0.4863, 0.9882, 0.0000]),
+    'green':             array([0.0000, 1.0000, 0.0000]),
+    'chartreuse':        array([0.4980, 1.0000, 0.0000]),
+    'medium spring green': array([0.0000, 0.9804, 0.6039]),
+    'MediumSpringGreen': array([0.0000, 0.9804, 0.6039]),
+    'green yellow':      array([0.6784, 1.0000, 0.1843]),
+    'GreenYellow':       array([0.6784, 1.0000, 0.1843]),
+    'lime green':        array([0.1961, 0.8039, 0.1961]),
+    'LimeGreen':         array([0.1961, 0.8039, 0.1961]),
+    'yellow green':      array([0.6039, 0.8039, 0.1961]),
+    'YellowGreen':       array([0.6039, 0.8039, 0.1961]),
+    'forest green':      array([0.1333, 0.5451, 0.1333]),
+    'ForestGreen':       array([0.1333, 0.5451, 0.1333]),
+    'olive drab':        array([0.4196, 0.5569, 0.1373]),
+    'OliveDrab':         array([0.4196, 0.5569, 0.1373]),
+    'dark khaki':        array([0.7412, 0.7176, 0.4196]),
+    'DarkKhaki':         array([0.7412, 0.7176, 0.4196]),
+    'khaki':             array([0.9412, 0.9020, 0.5490]),
+    'pale goldenrod':    array([0.9333, 0.9098, 0.6667]),
+    'PaleGoldenrod':     array([0.9333, 0.9098, 0.6667]),
+    'light goldenrod yellow': array([0.9804, 0.9804, 0.8235]),
+    'LightGoldenrodYellow': array([0.9804, 0.9804, 0.8235]),
+    'light yellow':      array([1.0000, 1.0000, 0.8784]),
+    'LightYellow':       array([1.0000, 1.0000, 0.8784]),
+    'yellow':            array([1.0000, 1.0000, 0.0000]),
+    'gold':              array([1.0000, 0.8431, 0.0000]),
+    'light goldenrod':   array([0.9333, 0.8667, 0.5098]),
+    'LightGoldenrod':    array([0.9333, 0.8667, 0.5098]),
+    'goldenrod':         array([0.8549, 0.6471, 0.1255]),
+    'dark goldenrod':    array([0.7216, 0.5255, 0.0431]),
+    'DarkGoldenrod':     array([0.7216, 0.5255, 0.0431]),
+    'rosy brown':        array([0.7373, 0.5608, 0.5608]),
+    'RosyBrown':         array([0.7373, 0.5608, 0.5608]),
+    'indian red':        array([0.8039, 0.3608, 0.3608]),
+    'IndianRed':         array([0.8039, 0.3608, 0.3608]),
+    'saddle brown':      array([0.5451, 0.2706, 0.0745]),
+    'SaddleBrown':       array([0.5451, 0.2706, 0.0745]),
+    'sienna':            array([0.6275, 0.3216, 0.1765]),
+    'peru':              array([0.8039, 0.5216, 0.2471]),
+    'burlywood':         array([0.8706, 0.7216, 0.5294]),
+    'beige':             array([0.9608, 0.9608, 0.8627]),
+    'wheat':             array([0.9608, 0.8706, 0.7020]),
+    'sandy brown':       array([0.9569, 0.6431, 0.3765]),
+    'SandyBrown':        array([0.9569, 0.6431, 0.3765]),
+    'tan':               array([0.8235, 0.7059, 0.5490]),
+    'chocolate':         array([0.8235, 0.4118, 0.1176]),
+    'firebrick':         array([0.6980, 0.1333, 0.1333]),
+    'brown':             array([0.6471, 0.1647, 0.1647]),
+    'dark salmon':       array([0.9137, 0.5882, 0.4784]),
+    'DarkSalmon':        array([0.9137, 0.5882, 0.4784]),
+    'salmon':            array([0.9804, 0.5020, 0.4471]),
+    'light salmon':      array([1.0000, 0.6275, 0.4784]),
+    'LightSalmon':       array([1.0000, 0.6275, 0.4784]),
+    'orange':            array([1.0000, 0.6471, 0.0000]),
+    'dark orange':       array([1.0000, 0.5490, 0.0000]),
+    'DarkOrange':        array([1.0000, 0.5490, 0.0000]),
+    'coral':             array([1.0000, 0.4980, 0.3137]),
+    'light coral':       array([0.9412, 0.5020, 0.5020]),
+    'LightCoral':        array([0.9412, 0.5020, 0.5020]),
+    'tomato':            array([1.0000, 0.3882, 0.2784]),
+    'orange red':        array([1.0000, 0.2706, 0.0000]),
+    'OrangeRed':         array([1.0000, 0.2706, 0.0000]),
+    'red':               array([1.0000, 0.0000, 0.0000]),
+    'hot pink':          array([1.0000, 0.4118, 0.7059]),
+    'HotPink':           array([1.0000, 0.4118, 0.7059]),
+    'deep pink':         array([1.0000, 0.0784, 0.5765]),
+    'DeepPink':          array([1.0000, 0.0784, 0.5765]),
+    'pink':              array([1.0000, 0.7529, 0.7961]),
+    'light pink':        array([1.0000, 0.7137, 0.7569]),
+    'LightPink':         array([1.0000, 0.7137, 0.7569]),
+    'pale violet red':   array([0.8588, 0.4392, 0.5765]),
+    'PaleVioletRed':     array([0.8588, 0.4392, 0.5765]),
+    'maroon':            array([0.6902, 0.1882, 0.3765]),
+    'medium violet red': array([0.7804, 0.0824, 0.5216]),
+    'MediumVioletRed':   array([0.7804, 0.0824, 0.5216]),
+    'violet red':        array([0.8157, 0.1255, 0.5647]),
+    'VioletRed':         array([0.8157, 0.1255, 0.5647]),
+    'magenta':           array([1.0000, 0.0000, 1.0000]),
+    'violet':            array([0.9333, 0.5098, 0.9333]),
+    'plum':              array([0.8667, 0.6275, 0.8667]),
+    'orchid':            array([0.8549, 0.4392, 0.8392]),
+    'medium orchid':     array([0.7294, 0.3333, 0.8275]),
+    'MediumOrchid':      array([0.7294, 0.3333, 0.8275]),
+    'dark orchid':       array([0.6000, 0.1961, 0.8000]),
+    'DarkOrchid':        array([0.6000, 0.1961, 0.8000]),
+    'dark violet':       array([0.5804, 0.0000, 0.8275]),
+    'DarkViolet':        array([0.5804, 0.0000, 0.8275]),
+    'blue violet':       array([0.5412, 0.1686, 0.8863]),
+    'BlueViolet':        array([0.5412, 0.1686, 0.8863]),
+    'purple':            array([0.6275, 0.1255, 0.9412]),
+    'medium purple':     array([0.5765, 0.4392, 0.8588]),
+    'MediumPurple':      array([0.5765, 0.4392, 0.8588]),
+    'thistle':           array([0.8471, 0.7490, 0.8471]),
+    'snow1':             array([1.0000, 0.9804, 0.9804]),
+    'snow2':             array([0.9333, 0.9137, 0.9137]),
+    'snow3':             array([0.8039, 0.7882, 0.7882]),
+    'snow4':             array([0.5451, 0.5373, 0.5373]),
+    'seashell1':         array([1.0000, 0.9608, 0.9333]),
+    'seashell2':         array([0.9333, 0.8980, 0.8706]),
+    'seashell3':         array([0.8039, 0.7725, 0.7490]),
+    'seashell4':         array([0.5451, 0.5255, 0.5098]),
+    'AntiqueWhite1':     array([1.0000, 0.9373, 0.8588]),
+    'AntiqueWhite2':     array([0.9333, 0.8745, 0.8000]),
+    'AntiqueWhite3':     array([0.8039, 0.7529, 0.6902]),
+    'AntiqueWhite4':     array([0.5451, 0.5137, 0.4706]),
+    'bisque1':           array([1.0000, 0.8941, 0.7686]),
+    'bisque2':           array([0.9333, 0.8353, 0.7176]),
+    'bisque3':           array([0.8039, 0.7176, 0.6196]),
+    'bisque4':           array([0.5451, 0.4902, 0.4196]),
+    'PeachPuff1':        array([1.0000, 0.8549, 0.7255]),
+    'PeachPuff2':        array([0.9333, 0.7961, 0.6784]),
+    'PeachPuff3':        array([0.8039, 0.6863, 0.5843]),
+    'PeachPuff4':        array([0.5451, 0.4667, 0.3961]),
+    'NavajoWhite1':      array([1.0000, 0.8706, 0.6784]),
+    'NavajoWhite2':      array([0.9333, 0.8118, 0.6314]),
+    'NavajoWhite3':      array([0.8039, 0.7020, 0.5451]),
+    'NavajoWhite4':      array([0.5451, 0.4745, 0.3686]),
+    'LemonChiffon1':     array([1.0000, 0.9804, 0.8039]),
+    'LemonChiffon2':     array([0.9333, 0.9137, 0.7490]),
+    'LemonChiffon3':     array([0.8039, 0.7882, 0.6471]),
+    'LemonChiffon4':     array([0.5451, 0.5373, 0.4392]),
+    'cornsilk1':         array([1.0000, 0.9725, 0.8627]),
+    'cornsilk2':         array([0.9333, 0.9098, 0.8039]),
+    'cornsilk3':         array([0.8039, 0.7843, 0.6941]),
+    'cornsilk4':         array([0.5451, 0.5333, 0.4706]),
+    'ivory1':            array([1.0000, 1.0000, 0.9412]),
+    'ivory2':            array([0.9333, 0.9333, 0.8784]),
+    'ivory3':            array([0.8039, 0.8039, 0.7569]),
+    'ivory4':            array([0.5451, 0.5451, 0.5137]),
+    'honeydew1':         array([0.9412, 1.0000, 0.9412]),
+    'honeydew2':         array([0.8784, 0.9333, 0.8784]),
+    'honeydew3':         array([0.7569, 0.8039, 0.7569]),
+    'honeydew4':         array([0.5137, 0.5451, 0.5137]),
+    'LavenderBlush1':    array([1.0000, 0.9412, 0.9608]),
+    'LavenderBlush2':    array([0.9333, 0.8784, 0.8980]),
+    'LavenderBlush3':    array([0.8039, 0.7569, 0.7725]),
+    'LavenderBlush4':    array([0.5451, 0.5137, 0.5255]),
+    'MistyRose1':        array([1.0000, 0.8941, 0.8824]),
+    'MistyRose2':        array([0.9333, 0.8353, 0.8235]),
+    'MistyRose3':        array([0.8039, 0.7176, 0.7098]),
+    'MistyRose4':        array([0.5451, 0.4902, 0.4824]),
+    'azure1':            array([0.9412, 1.0000, 1.0000]),
+    'azure2':            array([0.8784, 0.9333, 0.9333]),
+    'azure3':            array([0.7569, 0.8039, 0.8039]),
+    'azure4':            array([0.5137, 0.5451, 0.5451]),
+    'SlateBlue1':        array([0.5137, 0.4353, 1.0000]),
+    'SlateBlue2':        array([0.4784, 0.4039, 0.9333]),
+    'SlateBlue3':        array([0.4118, 0.3490, 0.8039]),
+    'SlateBlue4':        array([0.2784, 0.2353, 0.5451]),
+    'RoyalBlue1':        array([0.2824, 0.4627, 1.0000]),
+    'RoyalBlue2':        array([0.2627, 0.4314, 0.9333]),
+    'RoyalBlue3':        array([0.2275, 0.3725, 0.8039]),
+    'RoyalBlue4':        array([0.1529, 0.2510, 0.5451]),
+    'blue1':             array([0.0000, 0.0000, 1.0000]),
+    'blue2':             array([0.0000, 0.0000, 0.9333]),
+    'blue3':             array([0.0000, 0.0000, 0.8039]),
+    'blue4':             array([0.0000, 0.0000, 0.5451]),
+    'DodgerBlue1':       array([0.1176, 0.5647, 1.0000]),
+    'DodgerBlue2':       array([0.1098, 0.5255, 0.9333]),
+    'DodgerBlue3':       array([0.0941, 0.4549, 0.8039]),
+    'DodgerBlue4':       array([0.0627, 0.3059, 0.5451]),
+    'SteelBlue1':        array([0.3882, 0.7216, 1.0000]),
+    'SteelBlue2':        array([0.3608, 0.6745, 0.9333]),
+    'SteelBlue3':        array([0.3098, 0.5804, 0.8039]),
+    'SteelBlue4':        array([0.2118, 0.3922, 0.5451]),
+    'DeepSkyBlue1':      array([0.0000, 0.7490, 1.0000]),
+    'DeepSkyBlue2':      array([0.0000, 0.6980, 0.9333]),
+    'DeepSkyBlue3':      array([0.0000, 0.6039, 0.8039]),
+    'DeepSkyBlue4':      array([0.0000, 0.4078, 0.5451]),
+    'SkyBlue1':          array([0.5294, 0.8078, 1.0000]),
+    'SkyBlue2':          array([0.4941, 0.7529, 0.9333]),
+    'SkyBlue3':          array([0.4235, 0.6510, 0.8039]),
+    'SkyBlue4':          array([0.2902, 0.4392, 0.5451]),
+    'LightSkyBlue1':     array([0.6902, 0.8863, 1.0000]),
+    'LightSkyBlue2':     array([0.6431, 0.8275, 0.9333]),
+    'LightSkyBlue3':     array([0.5529, 0.7137, 0.8039]),
+    'LightSkyBlue4':     array([0.3765, 0.4824, 0.5451]),
+    'SlateGray1':        array([0.7765, 0.8863, 1.0000]),
+    'SlateGray2':        array([0.7255, 0.8275, 0.9333]),
+    'SlateGray3':        array([0.6235, 0.7137, 0.8039]),
+    'SlateGray4':        array([0.4235, 0.4824, 0.5451]),
+    'LightSteelBlue1':   array([0.7922, 0.8824, 1.0000]),
+    'LightSteelBlue2':   array([0.7373, 0.8235, 0.9333]),
+    'LightSteelBlue3':   array([0.6353, 0.7098, 0.8039]),
+    'LightSteelBlue4':   array([0.4314, 0.4824, 0.5451]),
+    'LightBlue1':        array([0.7490, 0.9373, 1.0000]),
+    'LightBlue2':        array([0.6980, 0.8745, 0.9333]),
+    'LightBlue3':        array([0.6039, 0.7529, 0.8039]),
+    'LightBlue4':        array([0.4078, 0.5137, 0.5451]),
+    'LightCyan1':        array([0.8784, 1.0000, 1.0000]),
+    'LightCyan2':        array([0.8196, 0.9333, 0.9333]),
+    'LightCyan3':        array([0.7059, 0.8039, 0.8039]),
+    'LightCyan4':        array([0.4784, 0.5451, 0.5451]),
+    'PaleTurquoise1':    array([0.7333, 1.0000, 1.0000]),
+    'PaleTurquoise2':    array([0.6824, 0.9333, 0.9333]),
+    'PaleTurquoise3':    array([0.5882, 0.8039, 0.8039]),
+    'PaleTurquoise4':    array([0.4000, 0.5451, 0.5451]),
+    'CadetBlue1':        array([0.5961, 0.9608, 1.0000]),
+    'CadetBlue2':        array([0.5569, 0.8980, 0.9333]),
+    'CadetBlue3':        array([0.4784, 0.7725, 0.8039]),
+    'CadetBlue4':        array([0.3255, 0.5255, 0.5451]),
+    'turquoise1':        array([0.0000, 0.9608, 1.0000]),
+    'turquoise2':        array([0.0000, 0.8980, 0.9333]),
+    'turquoise3':        array([0.0000, 0.7725, 0.8039]),
+    'turquoise4':        array([0.0000, 0.5255, 0.5451]),
+    'cyan1':             array([0.0000, 1.0000, 1.0000]),
+    'cyan2':             array([0.0000, 0.9333, 0.9333]),
+    'cyan3':             array([0.0000, 0.8039, 0.8039]),
+    'cyan4':             array([0.0000, 0.5451, 0.5451]),
+    'DarkSlateGray1':    array([0.5922, 1.0000, 1.0000]),
+    'DarkSlateGray2':    array([0.5529, 0.9333, 0.9333]),
+    'DarkSlateGray3':    array([0.4745, 0.8039, 0.8039]),
+    'DarkSlateGray4':    array([0.3216, 0.5451, 0.5451]),
+    'aquamarine1':       array([0.4980, 1.0000, 0.8314]),
+    'aquamarine2':       array([0.4627, 0.9333, 0.7765]),
+    'aquamarine3':       array([0.4000, 0.8039, 0.6667]),
+    'aquamarine4':       array([0.2706, 0.5451, 0.4549]),
+    'DarkSeaGreen1':     array([0.7569, 1.0000, 0.7569]),
+    'DarkSeaGreen2':     array([0.7059, 0.9333, 0.7059]),
+    'DarkSeaGreen3':     array([0.6078, 0.8039, 0.6078]),
+    'DarkSeaGreen4':     array([0.4118, 0.5451, 0.4118]),
+    'SeaGreen1':         array([0.3294, 1.0000, 0.6235]),
+    'SeaGreen2':         array([0.3059, 0.9333, 0.5804]),
+    'SeaGreen3':         array([0.2627, 0.8039, 0.5020]),
+    'SeaGreen4':         array([0.1804, 0.5451, 0.3412]),
+    'PaleGreen1':        array([0.6039, 1.0000, 0.6039]),
+    'PaleGreen2':        array([0.5647, 0.9333, 0.5647]),
+    'PaleGreen3':        array([0.4863, 0.8039, 0.4863]),
+    'PaleGreen4':        array([0.3294, 0.5451, 0.3294]),
+    'SpringGreen1':      array([0.0000, 1.0000, 0.4980]),
+    'SpringGreen2':      array([0.0000, 0.9333, 0.4627]),
+    'SpringGreen3':      array([0.0000, 0.8039, 0.4000]),
+    'SpringGreen4':      array([0.0000, 0.5451, 0.2706]),
+    'green1':            array([0.0000, 1.0000, 0.0000]),
+    'green2':            array([0.0000, 0.9333, 0.0000]),
+    'green3':            array([0.0000, 0.8039, 0.0000]),
+    'green4':            array([0.0000, 0.5451, 0.0000]),
+    'chartreuse1':       array([0.4980, 1.0000, 0.0000]),
+    'chartreuse2':       array([0.4627, 0.9333, 0.0000]),
+    'chartreuse3':       array([0.4000, 0.8039, 0.0000]),
+    'chartreuse4':       array([0.2706, 0.5451, 0.0000]),
+    'OliveDrab1':        array([0.7529, 1.0000, 0.2431]),
+    'OliveDrab2':        array([0.7020, 0.9333, 0.2275]),
+    'OliveDrab3':        array([0.6039, 0.8039, 0.1961]),
+    'OliveDrab4':        array([0.4118, 0.5451, 0.1333]),
+    'DarkOliveGreen1':   array([0.7922, 1.0000, 0.4392]),
+    'DarkOliveGreen2':   array([0.7373, 0.9333, 0.4078]),
+    'DarkOliveGreen3':   array([0.6353, 0.8039, 0.3529]),
+    'DarkOliveGreen4':   array([0.4314, 0.5451, 0.2392]),
+    'khaki1':            array([1.0000, 0.9647, 0.5608]),
+    'khaki2':            array([0.9333, 0.9020, 0.5216]),
+    'khaki3':            array([0.8039, 0.7765, 0.4510]),
+    'khaki4':            array([0.5451, 0.5255, 0.3059]),
+    'LightGoldenrod1':   array([1.0000, 0.9255, 0.5451]),
+    'LightGoldenrod2':   array([0.9333, 0.8627, 0.5098]),
+    'LightGoldenrod3':   array([0.8039, 0.7451, 0.4392]),
+    'LightGoldenrod4':   array([0.5451, 0.5059, 0.2980]),
+    'LightYellow1':      array([1.0000, 1.0000, 0.8784]),
+    'LightYellow2':      array([0.9333, 0.9333, 0.8196]),
+    'LightYellow3':      array([0.8039, 0.8039, 0.7059]),
+    'LightYellow4':      array([0.5451, 0.5451, 0.4784]),
+    'yellow1':           array([1.0000, 1.0000, 0.0000]),
+    'yellow2':           array([0.9333, 0.9333, 0.0000]),
+    'yellow3':           array([0.8039, 0.8039, 0.0000]),
+    'yellow4':           array([0.5451, 0.5451, 0.0000]),
+    'gold1':             array([1.0000, 0.8431, 0.0000]),
+    'gold2':             array([0.9333, 0.7882, 0.0000]),
+    'gold3':             array([0.8039, 0.6784, 0.0000]),
+    'gold4':             array([0.5451, 0.4588, 0.0000]),
+    'goldenrod1':        array([1.0000, 0.7569, 0.1451]),
+    'goldenrod2':        array([0.9333, 0.7059, 0.1333]),
+    'goldenrod3':        array([0.8039, 0.6078, 0.1137]),
+    'goldenrod4':        array([0.5451, 0.4118, 0.0784]),
+    'DarkGoldenrod1':    array([1.0000, 0.7255, 0.0588]),
+    'DarkGoldenrod2':    array([0.9333, 0.6784, 0.0549]),
+    'DarkGoldenrod3':    array([0.8039, 0.5843, 0.0471]),
+    'DarkGoldenrod4':    array([0.5451, 0.3961, 0.0314]),
+    'RosyBrown1':        array([1.0000, 0.7569, 0.7569]),
+    'RosyBrown2':        array([0.9333, 0.7059, 0.7059]),
+    'RosyBrown3':        array([0.8039, 0.6078, 0.6078]),
+    'RosyBrown4':        array([0.5451, 0.4118, 0.4118]),
+    'IndianRed1':        array([1.0000, 0.4157, 0.4157]),
+    'IndianRed2':        array([0.9333, 0.3882, 0.3882]),
+    'IndianRed3':        array([0.8039, 0.3333, 0.3333]),
+    'IndianRed4':        array([0.5451, 0.2275, 0.2275]),
+    'sienna1':           array([1.0000, 0.5098, 0.2784]),
+    'sienna2':           array([0.9333, 0.4745, 0.2588]),
+    'sienna3':           array([0.8039, 0.4078, 0.2235]),
+    'sienna4':           array([0.5451, 0.2784, 0.1490]),
+    'burlywood1':        array([1.0000, 0.8275, 0.6078]),
+    'burlywood2':        array([0.9333, 0.7725, 0.5686]),
+    'burlywood3':        array([0.8039, 0.6667, 0.4902]),
+    'burlywood4':        array([0.5451, 0.4510, 0.3333]),
+    'wheat1':            array([1.0000, 0.9059, 0.7294]),
+    'wheat2':            array([0.9333, 0.8471, 0.6824]),
+    'wheat3':            array([0.8039, 0.7294, 0.5882]),
+    'wheat4':            array([0.5451, 0.4941, 0.4000]),
+    'tan1':              array([1.0000, 0.6471, 0.3098]),
+    'tan2':              array([0.9333, 0.6039, 0.2863]),
+    'tan3':              array([0.8039, 0.5216, 0.2471]),
+    'tan4':              array([0.5451, 0.3529, 0.1686]),
+    'chocolate1':        array([1.0000, 0.4980, 0.1412]),
+    'chocolate2':        array([0.9333, 0.4627, 0.1294]),
+    'chocolate3':        array([0.8039, 0.4000, 0.1137]),
+    'chocolate4':        array([0.5451, 0.2706, 0.0745]),
+    'firebrick1':        array([1.0000, 0.1882, 0.1882]),
+    'firebrick2':        array([0.9333, 0.1725, 0.1725]),
+    'firebrick3':        array([0.8039, 0.1490, 0.1490]),
+    'firebrick4':        array([0.5451, 0.1020, 0.1020]),
+    'brown1':            array([1.0000, 0.2510, 0.2510]),
+    'brown2':            array([0.9333, 0.2314, 0.2314]),
+    'brown3':            array([0.8039, 0.2000, 0.2000]),
+    'brown4':            array([0.5451, 0.1373, 0.1373]),
+    'salmon1':           array([1.0000, 0.5490, 0.4118]),
+    'salmon2':           array([0.9333, 0.5098, 0.3843]),
+    'salmon3':           array([0.8039, 0.4392, 0.3294]),
+    'salmon4':           array([0.5451, 0.2980, 0.2235]),
+    'LightSalmon1':      array([1.0000, 0.6275, 0.4784]),
+    'LightSalmon2':      array([0.9333, 0.5843, 0.4471]),
+    'LightSalmon3':      array([0.8039, 0.5059, 0.3843]),
+    'LightSalmon4':      array([0.5451, 0.3412, 0.2588]),
+    'orange1':           array([1.0000, 0.6471, 0.0000]),
+    'orange2':           array([0.9333, 0.6039, 0.0000]),
+    'orange3':           array([0.8039, 0.5216, 0.0000]),
+    'orange4':           array([0.5451, 0.3529, 0.0000]),
+    'DarkOrange1':       array([1.0000, 0.4980, 0.0000]),
+    'DarkOrange2':       array([0.9333, 0.4627, 0.0000]),
+    'DarkOrange3':       array([0.8039, 0.4000, 0.0000]),
+    'DarkOrange4':       array([0.5451, 0.2706, 0.0000]),
+    'coral1':            array([1.0000, 0.4471, 0.3373]),
+    'coral2':            array([0.9333, 0.4157, 0.3137]),
+    'coral3':            array([0.8039, 0.3569, 0.2706]),
+    'coral4':            array([0.5451, 0.2431, 0.1843]),
+    'tomato1':           array([1.0000, 0.3882, 0.2784]),
+    'tomato2':           array([0.9333, 0.3608, 0.2588]),
+    'tomato3':           array([0.8039, 0.3098, 0.2235]),
+    'tomato4':           array([0.5451, 0.2118, 0.1490]),
+    'OrangeRed1':        array([1.0000, 0.2706, 0.0000]),
+    'OrangeRed2':        array([0.9333, 0.2510, 0.0000]),
+    'OrangeRed3':        array([0.8039, 0.2157, 0.0000]),
+    'OrangeRed4':        array([0.5451, 0.1451, 0.0000]),
+    'red1':              array([1.0000, 0.0000, 0.0000]),
+    'red2':              array([0.9333, 0.0000, 0.0000]),
+    'red3':              array([0.8039, 0.0000, 0.0000]),
+    'red4':              array([0.5451, 0.0000, 0.0000]),
+    'DeepPink1':         array([1.0000, 0.0784, 0.5765]),
+    'DeepPink2':         array([0.9333, 0.0706, 0.5373]),
+    'DeepPink3':         array([0.8039, 0.0627, 0.4627]),
+    'DeepPink4':         array([0.5451, 0.0392, 0.3137]),
+    'HotPink1':          array([1.0000, 0.4314, 0.7059]),
+    'HotPink2':          array([0.9333, 0.4157, 0.6549]),
+    'HotPink3':          array([0.8039, 0.3765, 0.5647]),
+    'HotPink4':          array([0.5451, 0.2275, 0.3843]),
+    'pink1':             array([1.0000, 0.7098, 0.7725]),
+    'pink2':             array([0.9333, 0.6627, 0.7216]),
+    'pink3':             array([0.8039, 0.5686, 0.6196]),
+    'pink4':             array([0.5451, 0.3882, 0.4235]),
+    'LightPink1':        array([1.0000, 0.6824, 0.7255]),
+    'LightPink2':        array([0.9333, 0.6353, 0.6784]),
+    'LightPink3':        array([0.8039, 0.5490, 0.5843]),
+    'LightPink4':        array([0.5451, 0.3725, 0.3961]),
+    'PaleVioletRed1':    array([1.0000, 0.5098, 0.6706]),
+    'PaleVioletRed2':    array([0.9333, 0.4745, 0.6235]),
+    'PaleVioletRed3':    array([0.8039, 0.4078, 0.5373]),
+    'PaleVioletRed4':    array([0.5451, 0.2784, 0.3647]),
+    'maroon1':           array([1.0000, 0.2039, 0.7020]),
+    'maroon2':           array([0.9333, 0.1882, 0.6549]),
+    'maroon3':           array([0.8039, 0.1608, 0.5647]),
+    'maroon4':           array([0.5451, 0.1098, 0.3843]),
+    'VioletRed1':        array([1.0000, 0.2431, 0.5882]),
+    'VioletRed2':        array([0.9333, 0.2275, 0.5490]),
+    'VioletRed3':        array([0.8039, 0.1961, 0.4706]),
+    'VioletRed4':        array([0.5451, 0.1333, 0.3216]),
+    'magenta1':          array([1.0000, 0.0000, 1.0000]),
+    'magenta2':          array([0.9333, 0.0000, 0.9333]),
+    'magenta3':          array([0.8039, 0.0000, 0.8039]),
+    'magenta4':          array([0.5451, 0.0000, 0.5451]),
+    'orchid1':           array([1.0000, 0.5137, 0.9804]),
+    'orchid2':           array([0.9333, 0.4784, 0.9137]),
+    'orchid3':           array([0.8039, 0.4118, 0.7882]),
+    'orchid4':           array([0.5451, 0.2784, 0.5373]),
+    'plum1':             array([1.0000, 0.7333, 1.0000]),
+    'plum2':             array([0.9333, 0.6824, 0.9333]),
+    'plum3':             array([0.8039, 0.5882, 0.8039]),
+    'plum4':             array([0.5451, 0.4000, 0.5451]),
+    'MediumOrchid1':     array([0.8784, 0.4000, 1.0000]),
+    'MediumOrchid2':     array([0.8196, 0.3725, 0.9333]),
+    'MediumOrchid3':     array([0.7059, 0.3216, 0.8039]),
+    'MediumOrchid4':     array([0.4784, 0.2157, 0.5451]),
+    'DarkOrchid1':       array([0.7490, 0.2431, 1.0000]),
+    'DarkOrchid2':       array([0.6980, 0.2275, 0.9333]),
+    'DarkOrchid3':       array([0.6039, 0.1961, 0.8039]),
+    'DarkOrchid4':       array([0.4078, 0.1333, 0.5451]),
+    'purple1':           array([0.6078, 0.1882, 1.0000]),
+    'purple2':           array([0.5686, 0.1725, 0.9333]),
+    'purple3':           array([0.4902, 0.1490, 0.8039]),
+    'purple4':           array([0.3333, 0.1020, 0.5451]),
+    'MediumPurple1':     array([0.6706, 0.5098, 1.0000]),
+    'MediumPurple2':     array([0.6235, 0.4745, 0.9333]),
+    'MediumPurple3':     array([0.5373, 0.4078, 0.8039]),
+    'MediumPurple4':     array([0.3647, 0.2784, 0.5451]),
+    'thistle1':          array([1.0000, 0.8824, 1.0000]),
+    'thistle2':          array([0.9333, 0.8235, 0.9333]),
+    'thistle3':          array([0.8039, 0.7098, 0.8039]),
+    'thistle4':          array([0.5451, 0.4824, 0.5451]),
+    'gray0':             0.0000,
+    'grey0':             0.0000,
+    'gray1':             0.0118,
+    'grey1':             0.0118,
+    'gray2':             0.0196,
+    'grey2':             0.0196,
+    'gray3':             0.0314,
+    'grey3':             0.0314,
+    'gray4':             0.0392,
+    'grey4':             0.0392,
+    'gray5':             0.0510,
+    'grey5':             0.0510,
+    'gray6':             0.0588,
+    'grey6':             0.0588,
+    'gray7':             0.0706,
+    'grey7':             0.0706,
+    'gray8':             0.0784,
+    'grey8':             0.0784,
+    'gray9':             0.0902,
+    'grey9':             0.0902,
+    'gray10':            0.1020,
+    'grey10':            0.1020,
+    'gray11':            0.1098,
+    'grey11':            0.1098,
+    'gray12':            0.1216,
+    'grey12':            0.1216,
+    'gray13':            0.1294,
+    'grey13':            0.1294,
+    'gray14':            0.1412,
+    'grey14':            0.1412,
+    'gray15':            0.1490,
+    'grey15':            0.1490,
+    'gray16':            0.1608,
+    'grey16':            0.1608,
+    'gray17':            0.1686,
+    'grey17':            0.1686,
+    'gray18':            0.1804,
+    'grey18':            0.1804,
+    'gray19':            0.1882,
+    'grey19':            0.1882,
+    'gray20':            0.2000,
+    'grey20':            0.2000,
+    'gray21':            0.2118,
+    'grey21':            0.2118,
+    'gray22':            0.2196,
+    'grey22':            0.2196,
+    'gray23':            0.2314,
+    'grey23':            0.2314,
+    'gray24':            0.2392,
+    'grey24':            0.2392,
+    'gray25':            0.2510,
+    'grey25':            0.2510,
+    'gray26':            0.2588,
+    'grey26':            0.2588,
+    'gray27':            0.2706,
+    'grey27':            0.2706,
+    'gray28':            0.2784,
+    'grey28':            0.2784,
+    'gray29':            0.2902,
+    'grey29':            0.2902,
+    'gray30':            0.3020,
+    'grey30':            0.3020,
+    'gray31':            0.3098,
+    'grey31':            0.3098,
+    'gray32':            0.3216,
+    'grey32':            0.3216,
+    'gray33':            0.3294,
+    'grey33':            0.3294,
+    'gray34':            0.3412,
+    'grey34':            0.3412,
+    'gray35':            0.3490,
+    'grey35':            0.3490,
+    'gray36':            0.3608,
+    'grey36':            0.3608,
+    'gray37':            0.3686,
+    'grey37':            0.3686,
+    'gray38':            0.3804,
+    'grey38':            0.3804,
+    'gray39':            0.3882,
+    'grey39':            0.3882,
+    'gray40':            0.4000,
+    'grey40':            0.4000,
+    'gray41':            0.4118,
+    'grey41':            0.4118,
+    'gray42':            0.4196,
+    'grey42':            0.4196,
+    'gray43':            0.4314,
+    'grey43':            0.4314,
+    'gray44':            0.4392,
+    'grey44':            0.4392,
+    'gray45':            0.4510,
+    'grey45':            0.4510,
+    'gray46':            0.4588,
+    'grey46':            0.4588,
+    'gray47':            0.4706,
+    'grey47':            0.4706,
+    'gray48':            0.4784,
+    'grey48':            0.4784,
+    'gray49':            0.4902,
+    'grey49':            0.4902,
+    'gray50':            0.4980,
+    'grey50':            0.4980,
+    'gray51':            0.5098,
+    'grey51':            0.5098,
+    'gray52':            0.5216,
+    'grey52':            0.5216,
+    'gray53':            0.5294,
+    'grey53':            0.5294,
+    'gray54':            0.5412,
+    'grey54':            0.5412,
+    'gray55':            0.5490,
+    'grey55':            0.5490,
+    'gray56':            0.5608,
+    'grey56':            0.5608,
+    'gray57':            0.5686,
+    'grey57':            0.5686,
+    'gray58':            0.5804,
+    'grey58':            0.5804,
+    'gray59':            0.5882,
+    'grey59':            0.5882,
+    'gray60':            0.6000,
+    'grey60':            0.6000,
+    'gray61':            0.6118,
+    'grey61':            0.6118,
+    'gray62':            0.6196,
+    'grey62':            0.6196,
+    'gray63':            0.6314,
+    'grey63':            0.6314,
+    'gray64':            0.6392,
+    'grey64':            0.6392,
+    'gray65':            0.6510,
+    'grey65':            0.6510,
+    'gray66':            0.6588,
+    'grey66':            0.6588,
+    'gray67':            0.6706,
+    'grey67':            0.6706,
+    'gray68':            0.6784,
+    'grey68':            0.6784,
+    'gray69':            0.6902,
+    'grey69':            0.6902,
+    'gray70':            0.7020,
+    'grey70':            0.7020,
+    'gray71':            0.7098,
+    'grey71':            0.7098,
+    'gray72':            0.7216,
+    'grey72':            0.7216,
+    'gray73':            0.7294,
+    'grey73':            0.7294,
+    'gray74':            0.7412,
+    'grey74':            0.7412,
+    'gray75':            0.7490,
+    'grey75':            0.7490,
+    'gray76':            0.7608,
+    'grey76':            0.7608,
+    'gray77':            0.7686,
+    'grey77':            0.7686,
+    'gray78':            0.7804,
+    'grey78':            0.7804,
+    'gray79':            0.7882,
+    'grey79':            0.7882,
+    'gray80':            0.8000,
+    'grey80':            0.8000,
+    'gray81':            0.8118,
+    'grey81':            0.8118,
+    'gray82':            0.8196,
+    'grey82':            0.8196,
+    'gray83':            0.8314,
+    'grey83':            0.8314,
+    'gray84':            0.8392,
+    'grey84':            0.8392,
+    'gray85':            0.8510,
+    'grey85':            0.8510,
+    'gray86':            0.8588,
+    'grey86':            0.8588,
+    'gray87':            0.8706,
+    'grey87':            0.8706,
+    'gray88':            0.8784,
+    'grey88':            0.8784,
+    'gray89':            0.8902,
+    'grey89':            0.8902,
+    'gray90':            0.8980,
+    'grey90':            0.8980,
+    'gray91':            0.9098,
+    'grey91':            0.9098,
+    'gray92':            0.9216,
+    'grey92':            0.9216,
+    'gray93':            0.9294,
+    'grey93':            0.9294,
+    'gray94':            0.9412,
+    'grey94':            0.9412,
+    'gray95':            0.9490,
+    'grey95':            0.9490,
+    'gray96':            0.9608,
+    'grey96':            0.9608,
+    'gray97':            0.9686,
+    'grey97':            0.9686,
+    'gray98':            0.9804,
+    'grey98':            0.9804,
+    'gray99':            0.9882,
+    'grey99':            0.9882,
+    'gray100':           1.0000,
+    'grey100':           1.0000,
+    'dark grey':         0.6627,
+    'DarkGrey':          0.6627,
+    'dark gray':         0.6627,
+    'DarkGray':          0.6627,
+    'dark blue':         array([0.0000, 0.0000, 0.5451]),
+    'DarkBlue':          array([0.0000, 0.0000, 0.5451]),
+    'dark cyan':         array([0.0000, 0.5451, 0.5451]),
+    'DarkCyan':          array([0.0000, 0.5451, 0.5451]),
+    'dark magenta':      array([0.5451, 0.0000, 0.5451]),
+    'DarkMagenta':       array([0.5451, 0.0000, 0.5451]),
+    'dark red':          array([0.5451, 0.0000, 0.0000]),
+    'DarkRed':           array([0.5451, 0.0000, 0.0000]),
+    'light green':       array([0.5647, 0.9333, 0.5647]),
+    'LightGreen':        array([0.5647, 0.9333, 0.5647]),
+}
+
+del array   # Prevent namespace pollution.
+
+def _MakeColorTable(outfile = None, infile = "/usr/lib/X11/rgb.txt"):
+    import string, sys
+    
+    if type(infile) == type("string"):
+        infile = open(infile)
+    if outfile is None:
+        outfile = sys.stdout
+    if type(outfile) == type("string"):
+        outfile = open(outfile, "w")
+        
+    outfile.write("ColorTable = {\n");
+    for line in infile.readlines():
+        line = string.strip(line)
+        if not line or (line[0] in "%#!;"):
+            continue
+        # This is not a comment or an empty line
+        words = string.split(line)
+        name = "'" + string.join(words[3:], " ") + "':"
+        if words[0] == words[1] and words[0] == words[2]:
+            # Gray color
+            clr = float(words[0])/255.0
+            outfile.write("    %-20s %6.4f,\n" % (name,clr))
+        else:
+            clr = (name, float(words[0])/255.0, float(words[1])/255.0,
+                   float(words[2])/255.0)
+            outfile.write("    %-20s array([%6.4f, %6.4f, %6.4f]),\n" % clr)
+    outfile.write("}\n")
+    outfile.flush()
diff --git a/ase/visualize/fieldplotter.py b/ase/visualize/fieldplotter.py
new file mode 100644
index 0000000..915defb
--- /dev/null
+++ b/ase/visualize/fieldplotter.py
@@ -0,0 +1,272 @@
+"""plotting fields defined on atoms during a simulation."""
+
+from ase.visualize.primiplotter import PostScriptFile, PnmFile, GifFile, JpegFile, X11Window
+from ase.visualize.primiplotter import PrimiPlotter as _PrimiPlotter
+import numpy
+import time
+
+class FieldPlotter(_PrimiPlotter):
+    def __init__(self, atoms, datasource=None, verbose=0, timing=0,
+                 interval=1, initframe=0):
+        _PrimiPlotter.__init__(self, atoms, verbose=verbose, timing=timing,
+                               interval=interval, initframe=initframe)
+        self.datasource = datasource
+        self.dims = (100,100)
+        self.set_plot_plane("xy")
+        self.set_data_range("plot")
+        self.set_background(0.0)
+        self.set_red_yellow_colors()
+        
+    def set_plot_plane(self, plane):
+        """Set the plotting plane to xy, xz or yz (default: xy)"""
+        if plane in ("xy", "xz", "yz"):
+            self.plane = plane
+        else:
+            raise ValueError, "The argument to plotPlane must be 'xy', 'xz' or 'yz'."
+
+    def set_data_range(self, range1, range2=None):
+        """Set the range of the data used when coloring.
+
+        This function sets the range of data values mapped unto colors
+        in the final plot.
+        
+        Three possibilities:
+
+        'data':        Autoscale using the data on visible atoms.
+                       The range goes from the lowest to the highest
+                       value present on the atoms.  If only a few atoms
+                       have extreme values, the entire color range may not
+                       be used on the plot, as many values may be averaged
+                       on each point in the plot.
+
+        'plot':        Autoscale using the data on the plot.  Unlike 'data'
+                       this guarantees that the entire color range is used.
+
+        min, max:      Use the range [min, max]
+                       
+        """
+        if (range1 == "data" or range1 == "plot") and range2 == None:
+            self.autorange = range1
+        elif range2 != None:
+            self.autorange = None
+            self.range = (range1, range2)
+        else:
+            raise ValueError, "Illegal argument(s) to set_data_range"
+
+    def set_background(self, value):
+        """Set the data value of the background.  See also set_background_color
+
+        Set the value of the background (parts of the plot without atoms) to
+        a specific value, or to 'min' or 'max' representing the minimal or
+        maximal data values on the atoms.
+
+        Calling set_background cancels previous calls to set_background_color.
+        """
+        self.background = value
+        self.backgroundcolor = None
+
+    def set_background_color(self, color):
+        """Set the background color.  See also set_background.
+
+        Set the background color.  Use a single value in the range [0, 1[
+        for gray values, or a tuple of three such values as an RGB color.
+
+        Calling set_background_color cancels previous calls to set_background.
+        """
+        self.background = None
+        self.backgroundcolor = color
+
+    def set_red_yellow_colors(self, reverse=False):
+        """Set colors to Black-Red-Yellow-White (a.k.a. STM colors)"""
+        self.set_colors([(0.0, 0, 0, 0),
+                        (0.33, 1, 0, 0),
+                        (0.66, 1, 1, 0),
+                        (1.0, 1, 1, 1)],
+                       reverse)
+        
+    def set_black_white_colors(self, reverse=False):
+        """Set the color to Black-White (greyscale)"""
+        self.set_colors([(0.0, 0),  (1.0, 1)], reverse)
+
+    def set_colors(self, colors, reverse=False):
+        colors = numpy.array(colors, numpy.float)
+        if len(colors.shape) != 2:
+            raise ValueError, "Colors must be a 2D array."
+        if reverse:
+            colors[:,0] = 1 - colors[:,0]
+            colors = numpy.array(colors[::-1,:])
+            #print colors
+        if colors[0,0] != 0.0 or colors[-1,0] != 1.0:
+            raise ValueError, "First row must define the value 0 and last row must define the value 1"
+        if colors.shape[1] == 2:
+            self.colormode = 1
+        elif colors.shape[1] == 4:
+            self.colormode = 3
+        else:
+            raise ValueError, "Color specification must be Nx2 (grey) or Nx4 (rgb) matrix."
+        self.colorfunction = InterpolatingFunction(colors[:,0], colors[:,1:])
+        
+    def plot(self, data=None):
+        """Create a plot now.  Does not respect the interval timer.
+
+        This method makes a plot unconditionally.  It does not look at
+        the interval variable, nor is this plot taken into account in
+        the counting done by the update() method if an interval
+        variable was specified.
+
+        If data is specified, it must be an array of numbers with the
+        same length as the atoms.  That data will then be plotted.  If
+        no data is given, the data source specified when creating the
+        plotter is used.
+        
+        """
+        if self.timing:
+            self._starttimer()
+        self.log("FieldPlotter: Starting plot at "
+                 + time.strftime("%a, %d %b %Y %H:%M:%S"))
+        if data is None:
+            data = self.datasource()
+        if len(data) != len(self.atoms):
+            raise ValueError, ("Data has wrong length: %d instead of %d."
+                               % (len(data), len(self.atoms)))
+        
+        invisible = self._getinvisible()
+        coords = self._rotate(self._getpositions())
+        radii = self._getradii()
+        if self.autoscale:
+            self._autoscale(coords,radii)
+        scale = self.scale * self.relativescale
+        coords = scale * coords
+        center = self._getcenter(coords)
+        offset = numpy.array(self.dims + (0.0,))/2.0 - center
+        coords = coords + offset
+        radii = radii * scale
+        self.log("Scale is %f and size is (%d, %d)"
+                 % (scale, self.dims[0], self.dims[1]))
+        self.log("Physical size of plot is %f Angstrom times %f Angstrom"
+                 % (self.dims[0] / scale, self.dims[1] / scale))
+
+        # Remove invisible atoms
+        selector = numpy.logical_not(invisible)
+        coords = numpy.compress(selector, coords, 0)
+        radii = numpy.compress(selector, radii)
+        data = numpy.compress(selector, data)
+
+        self.log("plotting data in the range [%f,%f]" %
+                   (data.min(), data.max()))
+        # Now create the output array
+        sumarray = numpy.zeros(self.dims, numpy.float)
+        weight = numpy.zeros(self.dims)
+
+        # Loop over all atoms, and plot them
+        nmiss = 0
+        if self.plane == "xy":
+            xy = coords[:,:2]
+        elif self.plane == "xz":
+            xy = coords[:,::2]
+        elif self.plane == "yz":
+            xy = coords[:,1:]
+        else:
+            raise RuntimeError, "self.plane is bogus: "+str(self.plane)
+        assert xy.shape[1] == 2
+
+        self.log("plotting %d atoms on %d * %d (= %d) grid" %
+                   (len(xy), sumarray.shape[0], sumarray.shape[1],
+                    len(sumarray.flat)))
+                                                            
+        xy = xy.astype(numpy.int)
+        for i in xrange(len(xy)):
+            (x, y) = xy[i]
+            d = data[i]
+            if (x >= 0 and x < self.dims[0] and y >= 0 and y < self.dims[1]):
+                sumarray[x,y] += d
+                weight[x,y] += 1
+            else:
+                nmiss += 1
+        print "... %d atoms fell outside plot." % (nmiss,)
+
+        datamap = self._makedatamap(sumarray, weight, data.min(), data.max())
+        self.log("Range of data map: [%f, %f]" %
+                   (datamap.min(), datamap.max()))
+        plot = self._makeplotmap(datamap, weight)
+        #self.log("Range of plot: [%f, %f]" %
+        #           (min(plot.flat), max(plot.flat)))
+        examinplot = plot[:]
+        examinplot.shape = (plot.shape[0] * plot.shape[1],) + plot.shape[2:]
+        self.log("Range of plot: %s -> %s" %
+                 (str(examinplot.min(0)), str(examinplot.max(0))))
+        del examinplot
+        for device in self.outputdevice:
+            device.inform_about_scale(scale)
+            device.plotArray(self.n, numpy.swapaxes(plot,0,1))
+        self.n = self.n + 1
+        self.log("FieldPlotter: Finished plotting at "
+                 + time.strftime("%a, %d %b %Y %H:%M:%S"))
+        self.log("\n\n")
+
+        
+    def _makedatamap(self, sumarray, weight, minimum, maximum):
+        background = numpy.equal(weight, 0)
+        print "Number of background points:", sum(background.flat)
+        datamap = sumarray / numpy.where(background, 1, weight)
+        
+        if self.background is not None:
+            if self.background == "min":
+                bg = minimum
+            elif self.background == "max":
+                bg = maximum
+            else:
+                bg = self.background
+            datamap = numpy.where(background, bg, datamap)
+            
+        if self.autorange == "data":
+            datamap = (datamap - minimum) / (maximum - minimum)
+            self.log("Autorange using data.  Data range is [%f, %f]"
+                     % (minimum, maximum))
+        elif self.autorange == "plot":
+            ma = numpy.where(background, minimum, datamap).max()
+            mi = numpy.where(background, maximum, datamap).min()
+            datamap = (datamap - mi) / (ma - mi)
+            self.log("Autorange using plot.  Data range is [%f, %f]"
+                     % (mi, ma))
+        else:
+            assert self.autorange == None
+            datamap = (datamap - self.range[0]) / (self.range[1]
+                                                   - self.range[0])
+            datamap = numpy.clip(datamap, 0.0, 1.0)
+            self.log("Data range specified by user: [%f, %f]" % self.range)
+        datamap = numpy.where(background, bg, datamap)
+        assert datamap.min() >= 0 and datamap.max() <= 1.0
+        
+        return datamap
+
+    def _makeplotmap(self, datamap, weight):
+        plot = numpy.zeros(self.dims + (self.colormode,), numpy.float)
+        for i in range(self.dims[0]):
+            for j in range(self.dims[1]):
+                if self.backgroundcolor is not None and weight[i,j] == 0:
+                    plot[i,j,:] = self.backgroundcolor
+                else:
+                    x = datamap[i,j]
+                    plot[i,j,:] = self.colorfunction(x)
+        return plot
+    
+class InterpolatingFunction:
+    def __init__(self, xpoints, ypoints):
+        if len(xpoints) != len(ypoints):
+            raise ValueError, "Length of x and y arrays should be the same."
+        idx = xpoints.argsort()
+        self.xpoints = xpoints[idx]
+        self.ypoints = ypoints[idx]
+    def __call__(self, x):
+        n = self.xpoints.searchsorted(x)
+        if n == 0:
+            return self.ypoints[0]
+        if n == len(self.xpoints):
+            return self.xpoints[-1]
+        x0 = self.xpoints[n-1]
+        x1 = self.xpoints[n]
+        y0 = self.ypoints[n-1]
+        y1 = self.ypoints[n]
+        return y0 + (y1 - y0) / (x1 - x0) * (x - x0)
+    
diff --git a/ase/visualize/primiplotter.py b/ase/visualize/primiplotter.py
new file mode 100644
index 0000000..16e1d08
--- /dev/null
+++ b/ase/visualize/primiplotter.py
@@ -0,0 +1,928 @@
+"""An experimental package for making plots during a simulation.
+
+A PrimiPlotter can plot a list of atoms on one or more output devices.
+"""
+
+from numpy import *
+from ase.visualize.colortable import color_table
+import ase.data
+import sys, os, time, weakref
+
+class PrimiPlotterBase:
+    "Base class for PrimiPlotter and Povrayplotter."
+    #def set_dimensions(self, dims):
+    #    "Set the size of the canvas (a 2-tuple)."
+    #    self.dims = dims
+        
+    def set_rotation(self, rotation):
+        "Set the rotation angles (in degrees)."
+        self.angles[:] = array(rotation) * (pi/180)
+        
+    def set_radii(self, radii):
+        """Set the atomic radii.  Give an array or a single number."""
+        self.radius = radii
+
+    def set_colors(self, colors):
+        """Explicitly set the colors of the atoms."""
+        self.colors = colors
+
+    def set_color_function(self, colors):
+        """Set a color function, to be used to color the atoms."""
+        if callable(colors):
+            self.colorfunction = colors
+        else:
+            raise TypeError, "The color function is not callable."
+
+    def set_invisible(self, inv):
+        """Choose invisible atoms."""
+        self.invisible = inv
+
+    def set_invisibility_function(self, invfunc):
+        """Set an invisibility function."""
+        if callable(invfunc):
+            self.invisibilityfunction = invfunc
+        else:
+            raise TypeError, "The invisibility function is not callable."
+
+    def set_cut(self, xmin=None, xmax=None, ymin=None, ymax=None,
+               zmin=None, zmax=None):
+        self.cut = {"xmin":xmin, "xmax":xmax, "ymin":ymin, "ymax":ymax,
+                    "zmin":zmin, "zmax":zmax}
+    
+    def update(self, newatoms = None):
+        """Cause a plot (respecting the interval setting).
+
+        update causes a plot to be made.  If the interval variable was
+        specified when the plotter was create, it will only produce a
+        plot with that interval.  update takes an optional argument,
+        newatoms, which can be used to replace the list of atoms with
+        a new one.
+        """
+        if newatoms is not None:
+            self.atoms = newatoms
+        if self.skipnext <= 0:
+            self.plot()
+            self.skipnext = self.interval
+        self.skipnext -= 1
+        
+    def set_log(self, log):
+        """Sets a file for logging.
+
+        log may be an open file or a filename.
+        """
+        if hasattr(log, "write"):
+            self.logfile = log
+            self.ownlogfile = False
+        else:
+            self.logfile = open(log, "w")
+            self.ownlogfile = True
+
+    def log(self, message):
+        """logs a message to the file set by set_log."""
+        if self.logfile is not None:
+            self.logfile.write(message+"\n")
+            self.logfile.flush()
+        self._verb(message)
+        
+    def _verb(self, txt):
+        if self.verbose:
+            sys.stderr.write(txt+"\n")
+    
+    def _starttimer(self):
+        self.starttime = time.time()
+
+    def _stoptimer(self):
+        elapsedtime = time.time() - self.starttime
+        self.totaltime = self.totaltime + elapsedtime
+        print "plotting time %s sec (total %s sec)" % (elapsedtime,
+                                                       self.totaltime)
+
+    def _getpositions(self):
+        return self.atoms.get_positions()
+
+    def _getradii(self):
+        if self.radius is not None:
+            if hasattr(self.radius, "shape"):
+                return self.radius   # User has specified an array
+            else:
+                return self.radius * ones(len(self.atoms), float)
+        # No radii specified.  Try getting them from the atoms.
+        try:
+            return self.atoms.get_atomic_radii()
+        except AttributeError:
+            try:
+                z = self._getatomicnumbers()
+            except AttributeError:
+                pass
+            else:
+                return ase.data.covalent_radii[z]
+        # No radius available.  Defaulting to 1.0
+        return ones(len(self.atoms), float)
+
+    def _getatomicnumbers(self):
+        return self.atoms.get_atomic_numbers()
+    
+    def _getcolors(self):
+        # Try any explicitly given colors
+        if self.colors is not None:
+            if type(self.colors) == type({}):
+                self.log("Explicit colors dictionary")
+                return _colorsfromdict(self.colors,
+                                       asarray(self.atoms.get_tags(),int))
+            else:
+                self.log("Explicit colors")
+                return self.colors
+        # Try the color function, if given
+        if self.colorfunction is not None:
+            self.log("Calling color function.")
+            return self.colorfunction(self.atoms)
+        # Maybe the atoms know their own colors
+        try:
+            c = self.atoms.get_colors()
+        except AttributeError:
+            c = None
+        if c is not None:
+            if type(c) == type({}):
+                self.log("Color dictionary from atoms.get_colors()")
+                return _colorsfromdict(c, asarray(self.atoms.get_tags(),int))
+            else:
+                self.log("Colors from atoms.get_colors()")
+                return c
+        # Default to white atoms
+        self.log("No colors: using white")
+        return ones(len(self.atoms), float)
+
+    def _getinvisible(self):
+        if self.invisible is not None:
+            inv = self.invisible
+        else:
+            inv = zeros(len(self.atoms))
+        if self.invisibilityfunction:
+            inv = logical_or(inv, self.invisibilityfunction(self.atoms))
+        r = self._getpositions()
+        if len(r) > len(inv):
+            # This will happen in parallel simulations due to ghost atoms.
+            # They are invisible.  Hmm, this may cause trouble.
+            i2 = ones(len(r))
+            i2[:len(inv)] = inv
+            inv = i2
+            del i2
+        if self.cut["xmin"] is not None:
+            inv = logical_or(inv, less(r[:,0], self.cut["xmin"]))
+        if self.cut["xmax"] is not None:
+            inv = logical_or(inv, greater(r[:,0], self.cut["xmax"]))
+        if self.cut["ymin"] is not None:
+            inv = logical_or(inv, less(r[:,1], self.cut["ymin"]))
+        if self.cut["ymax"] is not None:
+            inv = logical_or(inv, greater(r[:,1], self.cut["ymax"]))
+        if self.cut["zmin"] is not None:
+            inv = logical_or(inv, less(r[:,2], self.cut["zmin"]))
+        if self.cut["zmax"] is not None:
+            inv = logical_or(inv, greater(r[:,2], self.cut["zmax"]))
+        return inv        
+
+    def __del__(self):
+        if self.ownlogfile:
+            self.logfile.close()
+            
+class PrimiPlotter(PrimiPlotterBase):
+    """Primitive PostScript-based plots during a simulation.
+
+    The PrimiPlotter plots atoms during simulations, extracting the
+    relevant information from the list of atoms.  It is created using
+    the list of atoms as an argument to the constructor.  Then one or
+    more output devices must be attached using set_output(device).  The
+    list of supported output devices is at the end.
+
+    The atoms are plotted as circles.  The system is first rotated
+    using the angles specified by set_rotation([vx, vy, vz]).  The
+    rotation is vx degrees around the x axis (positive from the y
+    toward the z axis), then vy degrees around the y axis (from x
+    toward z), then vz degrees around the z axis (from x toward y).
+    The rotation matrix is the same as the one used by RasMol.
+
+    Per default, the system is scaled so it fits within the canvas
+    (autoscale mode).  Autoscale mode is enabled and disables using
+    autoscale("on") or autoscale("off").  A manual scale factor can be
+    set with set_scale(scale), this implies autoscale("off").  The
+    scale factor (from the last autoscale event or from set_scale) can
+    be obtained with get_scale().  Finally, an explicit autoscaling can
+    be triggered with autoscale("now"), this is mainly useful before
+    calling get_scale or before disabling further autoscaling.
+    Finally, a relative scaling factor can be set with
+    SetRelativeScaling(), it is multiplied to the usual scale factor
+    (from autoscale or from set_scale).  This is probably only useful in
+    connection with autoscaling.
+
+    The radii of the atoms are obtained from the first of the following
+    methods which work:
+    
+    1.  If the radii are specified using PrimiPlotter.set_radii(r),
+        they are used.  Must be an array, or a single number.
+
+    2.  If the atoms has a get_atomic_radii() method, it is used.  This is
+        unlikely.
+
+    3.  If the atoms has a get_atomic_numbers() method, the
+        corresponding covalent radii are extracted from the
+        ASE.ChemicalElements module.
+
+    4.  If all else fails, the radius is set to 1.0 Angstrom.
+
+    The atoms are colored using the first of the following methods
+    which work.
+
+    1.  If colors are explicitly set using PrimiPlotter.set_colors(),
+        they are used.
+
+    2.  If these colors are specified as a dictionary, the tags
+        (from atoms.get_tags()) are used as an index into the
+        dictionary to get the actual colors of the atoms.
+
+    3.  If a color function has been set using
+        PrimiPlotter.set_color_function(), it is called with the atoms
+        as an argument, and is expected to return an array of colors.
+
+    4.  If the atoms have a get_colors() method, it is used to get the
+        colors.
+
+    5.  If these colors are specified as a dictionary, the tags
+        (from atoms.get_tags()) are used as an index into the
+        dictionary to get the actual colors of the atoms.
+
+    6.  If all else fails, the atoms will be white.
+
+    The colors are specified as an array of colors, one color per
+    atom.  Each color is either a real number from 0.0 to 1.0,
+    specifying a grayscale (0.0 = black, 1.0 = white), or an array of
+    three numbers from 0.0 to 1.0, specifying RGB values.  The colors
+    of all atoms are thus a Numerical Python N-vector or a 3xN matrix.
+
+    In cases 1a and 3a above, the keys of the dictionary are integers,
+    and the values are either numbers (grayscales) or 3-vectors (RGB
+    values), or strings with X11 color names, which are then
+    translated to RGB values.  Only in case 1a and 3a are strings
+    recognized as colors.
+
+    Some atoms may be invisible, and thus left out of the plot.
+    Invisible atoms are determined from the following algorithm.
+    Unlike the radius or the coloring, all points below are tried and
+    if an atom is invisible by any criterion, it is left out of the plot.
+
+    1.  All atoms are visible.
+    
+    2.  If PrimiPlotter.set_invisible() has be used to specify invisible
+        atoms, any atoms for which the value is non-zero becomes invisible.
+
+    3.  If an invisiblility function has been set with
+        PrimiPlotter.set_invisibility_function(), it is called with the
+        atoms as argument.  It is expected to return an integer per
+        atom, any non-zero value makes that atom invisible.
+
+    4.  If a cut has been specified using set_cut, any atom outside the
+        cut is made invisible.
+
+    Note that invisible atoms are still included in the algorithm for
+    positioning and scaling the plot.
+
+    
+    The following output devices are implemented.
+    
+    PostScriptFile(prefix):  Create PS files names prefix0000.ps etc.
+
+    PnmFile(prefix):  Similar, but makes PNM files.
+
+    GifFile(prefix):  Similar, but makes GIF files.
+
+    JpegFile(prefix):  Similar, but makes JPEG files.
+
+    X11Window():  Show the plot in an X11 window using ghostscript.
+
+    Output devices writing to files take an extra optional argument to
+    the constructor, compress, specifying if the output file should be
+    gzipped.  This is not allowed for some (already compressed) file
+    formats.
+
+    Instead of a filename prefix, a filename containing a % can be
+    used.  In that case the filename is expected to expand to a real
+    filename when used with the Python string formatting operator (%)
+    with the frame number as argument.  Avoid generating spaces in the
+    file names: use e.g. %03d instead of %3d.  
+    """
+    def __init__(self, atoms, verbose=0, timing=0, interval=1, initframe=0):
+        """
+
+        Parameters to the constructor:
+
+        atoms: The atoms to be plottet.
+
+        verbose = 0:  Write progress information to stderr.
+
+        timing = 0:  Collect timing information.
+
+        interval = 1: If specified, a plot is only made every
+        interval'th time update() is called.  Deprecated, normally you
+        should use the interval argument when attaching the plotter to
+        e.g. the dynamics.
+
+        initframe = 0: Initial frame number, i.e. the number of the
+        first plot.
+        
+        """
+        self.atoms = atoms
+        self.outputdevice = []
+        self.angles = zeros(3, float)
+        self.dims = (512, 512)
+        self.verbose = verbose
+        self.timing = timing
+        self.totaltime = 0.0
+        self.radius = None
+        self.colors = None
+        self.colorfunction = None
+        self.n = initframe
+        self.interval = interval
+        self.skipnext = 0 # Number of calls to update before anything happens.
+        self.a_scale = 1
+        self.relativescale = 1.0
+        self.invisible = None
+        self.invisibilityfunction = None
+        self.set_cut()   # No cut
+        self.isparallel = 0
+        self.logfile = None
+        self.ownlogfile = False
+        
+    def set_output(self, device):
+        self.outputdevice.append(device)
+        device.set_dimensions(self.dims)
+        device.set_owner(weakref.proxy(self))
+
+    def set_dimensions(self, dims):
+        "Set the size of the canvas (a 2-tuple)."
+        if self.outputdevice:
+            raise RuntimeError("Cannot set dimensions after an output device has been specified.")
+        self.dims = dims
+        
+    def autoscale(self, mode):
+        if mode == "on":
+            self.a_scale = 1
+        elif mode == "off":
+            self.a_scale = 0
+        elif mode == "now":
+            coords = self._rotate(self.atoms.get_positions())
+            radii = self._getradii()
+            self._autoscale(coords, radii)
+        else:
+            raise ValueError, "Unknown autoscale mode: ",+str(mode)
+
+    def set_scale(self, scale):
+        self.autoscale("off")
+        self.scale = scale
+
+    def get_scale(self):
+        return self.scale
+
+    def set_relative_scale(self, rscale = 1.0):
+        self.relativescale = rscale
+
+    def plot(self):
+        """Create a plot now.  Does not respect the interval timer.
+
+        This method makes a plot unconditionally.  It does not look at
+        the interval variable, nor is this plot taken into account in
+        the counting done by the update() method if an interval
+        variable was specified.
+        """
+        if self.timing:
+            self._starttimer()
+        self.log("PrimiPlotter: Starting plot at "
+                 + time.strftime("%a, %d %b %Y %H:%M:%S"))
+        colors = self._getcolors()
+        invisible = self._getinvisible()
+        coords = self._rotate(self._getpositions())
+        radii = self._getradii()
+        if self.a_scale:
+            self._autoscale(coords,radii)
+        scale = self.scale * self.relativescale
+        coords = scale * coords
+        center = self._getcenter(coords)
+        offset = array(self.dims + (0.0,))/2.0 - center
+        coords = coords + offset
+        self.log("Scale is %f and size is (%d, %d)"
+                 % (scale, self.dims[0], self.dims[1]))
+        self.log("Physical size of plot is %f Angstrom times %f Angstrom"
+                 % (self.dims[0] / scale, self.dims[1] / scale))
+
+        self._verb("Sorting.")
+        order = argsort(coords[:,2])
+        coords = coords[order]  ### take(coords, order)
+        radii = radii[order]    ### take(radii, order)
+        colors = colors[order]  ### take(colors, order)
+        invisible = invisible[order]  ### take(invisible, order)
+        if self.isparallel:
+            id = arange(len(coords))[order] ### take(arange(len(coords)), order)
+        else:
+            id = None
+            
+        radii = radii * scale
+        selector = self._computevisibility(coords, radii, invisible, id)
+        coords = compress(selector, coords, 0)
+        radii = compress(selector, radii)
+        colors = compress(selector, colors, 0)
+        self._makeoutput(scale, coords, radii, colors)
+        self.log("PrimiPlotter: Finished plotting at "
+                 + time.strftime("%a, %d %b %Y %H:%M:%S"))
+        self.log("\n\n")
+        if self.timing:
+            self._stoptimer()
+
+    def _computevisibility(self, coords, rad, invisible, id, zoom = 1):
+        xy = coords[:,:2]
+        typradius = sum(rad) / len(rad)
+        if typradius < 4.0:
+            self.log("Refining visibility check.")
+            if zoom >= 16:
+                raise RuntimeError, "Cannot check visibility - too deep recursion."
+            return self._computevisibility(xy*2, rad*2, invisible, id, zoom*2)
+        else:
+            self.log("Visibility(r_typ = %.1f pixels)" % (typradius,))
+        dims = array(self.dims) * zoom
+        maxr = int(ceil(max(rad))) + 2
+        canvas = zeros((dims[0] + 4*maxr, dims[1] + 4*maxr), int8)
+        # Atoms are only invisible if they are within the canvas, or closer
+        # to its edge than their radius
+        visible = (greater(xy[:,0], -rad) * less(xy[:,0], dims[0]+rad)
+                   * greater(xy[:,1], -rad) * less(xy[:,1], dims[1]+rad)
+                   * logical_not(invisible))
+        # Atoms are visible if not hidden behind other atoms
+        xy = floor(xy + 2*maxr + 0.5).astype(int)
+        masks = {}
+        for i in xrange(len(rad)-1, -1, -1):
+            if (i % 100000) == 0 and i:
+                self._verb(str(i))
+            if not visible[i]:
+                continue
+            x, y = xy[i]
+            r = rad[i]
+            try:
+                mask, invmask, rn = masks[r]
+            except KeyError:
+                rn = int(ceil(r))
+                nmask = 2*rn+1
+                mask = (arange(nmask) - rn)**2
+                mask = less(mask[:,newaxis]+mask[newaxis,:], r*r).astype(int8)
+                invmask = equal(mask, 0).astype(int8)
+                masks[r] = (mask, invmask, rn)
+            window = logical_or(canvas[x-rn:x+rn+1, y-rn:y+rn+1], invmask)
+            hidden = alltrue(window.flat)
+            if hidden:
+                visible[i] = 0
+            else:
+                canvas[x-rn:x+rn+1, y-rn:y+rn+1] = logical_or(canvas[x-rn:x+rn+1, y-rn:y+rn+1], mask)
+        self.log("%d visible, %d hidden out of %d" %
+                   (sum(visible), len(visible) - sum(visible), len(visible)))
+        return visible
+        
+    def _rotate(self, positions):
+        self.log("Rotation angles: %f %f %f" % tuple(self.angles))
+        mat = dot(dot(_rot(self.angles[2], 2),
+                      _rot(self.angles[1], 1)),
+                  _rot(self.angles[0]+pi, 0))
+        return dot(positions, mat)
+
+    def _getcenter(self, coords):
+        return array((max(coords[:,0]) + min(coords[:,0]),
+                      max(coords[:,1]) + min(coords[:,1]), 0.0)) / 2.0
+
+    def _autoscale(self, coords, radii):
+        x = coords[:,0]
+        y = coords[:,1]
+        maxradius = max(radii)
+        deltax = max(x) - min(x) + 2*maxradius
+        deltay = max(y) - min(y) + 2*maxradius
+        scalex = self.dims[0] / deltax
+        scaley = self.dims[1] / deltay
+        self.scale = 0.95 * min(scalex, scaley)
+        self.log("Autoscale: %f" % self.scale)
+
+    def _makeoutput(self, scale, coords, radii, colors):
+        for device in self.outputdevice:
+            device.inform_about_scale(scale)
+            device.plot(self.n, coords, radii, colors)
+        self.n = self.n + 1
+
+
+class ParallelPrimiPlotter(PrimiPlotter):
+    """A version of PrimiPlotter for parallel ASAP simulations.
+
+    Used like PrimiPlotter, but only the output devices on the master
+    node are used.  Most of the processing is distributed on the
+    nodes, but the actual output is only done on the master.  See the
+    PrimiPlotter docstring for details.
+    """
+    def __init__(self, *args, **kwargs):
+        apply(PrimiPlotter.__init__, (self,)+args, kwargs)
+        self.isparallel = 1
+        import Scientific.MPI
+        self.MPI = Scientific.MPI
+        self.mpi = Scientific.MPI.world
+        if self.mpi is None:
+            raise RuntimeError, "MPI is not available."
+        self.master = self.mpi.rank == 0
+        self.mpitag = 42   # Reduce chance of collision with other modules.
+        
+    def set_output(self, device):
+        if self.master:
+            PrimiPlotter.set_output(self, device)
+
+    def set_log(self, log):
+        if self.master:
+            PrimiPlotter.set_log(self, log)
+
+    def _getpositions(self):
+        realpos = self.atoms.get_positions()
+        ghostpos = self.atoms.GetGhostCartesianPositions()
+        self.numberofrealatoms = len(realpos)
+        self.numberofghostatoms = len(ghostpos)
+        return concatenate((realpos, ghostpos))
+
+    def _getatomicnumbers(self):
+        realz = self.atoms.get_atomic_numbers()
+        ghostz = self.atoms.GetGhostAtomicNumbers()
+        return concatenate((realz, ghostz))
+
+    def _getradius(self):
+        r = PrimiPlotter._getradius(self)
+        if len(r) == self.numberofrealatoms + self.numberofghostatoms:
+            # Must have calculated radii from atomic numbers
+            return r
+        else:
+            assert len(r) == self.numberofrealatoms
+            # Heuristic: use minimum r for the ghosts
+            ghostr = min(r) * ones(self.numberofghostatoms, float)
+            return concatenate((r, ghostr))
+
+    def _getcenter(self, coords):
+        # max(x) and min(x) only works for rank-1 arrays in Numeric version 17.
+        maximal = maximum.reduce(coords[:,0:2])
+        minimal = minimum.reduce(coords[:,0:2])
+        recvmax = zeros(2, maximal.typecode())
+        recvmin = zeros(2, minimal.typecode())
+        self.mpi.allreduce(maximal, recvmax, self.MPI.max)
+        self.mpi.allreduce(minimal, recvmin, self.MPI.min)
+        maxx, maxy = recvmax
+        minx, miny = recvmin
+        return array([maxx + minx, maxy + miny, 0.0]) / 2.0
+
+    def _computevisibility(self, xy, rad, invisible, id, zoom = 1):
+        # Find visible atoms, allowing ghost atoms to hide real atoms.
+        v = PrimiPlotter._computevisibility(self, xy, rad, invisible, id, zoom)
+        # Then remove ghost atoms
+        return v * less(id, self.numberofrealatoms)
+
+    def _autoscale(self, coords, radii):
+        self._verb("Autoscale")
+        n = len(self.atoms)
+        x = coords[:n,0]
+        y = coords[:n,1]
+        assert len(x) == len(self.atoms)
+        maximal = array([max(x), max(y), max(radii[:n])])
+        minimal = array([min(x), min(y)])
+        recvmax = zeros(3, maximal.typecode())
+        recvmin = zeros(2, minimal.typecode())
+        self.mpi.allreduce(maximal, recvmax, self.MPI.max)
+        self.mpi.allreduce(minimal, recvmin, self.MPI.min)
+        maxx, maxy, maxradius = recvmax
+        minx, miny = recvmin
+        deltax = maxx - minx + 2*maxradius
+        deltay = maxy - miny + 2*maxradius
+        scalex = self.dims[0] / deltax
+        scaley = self.dims[1] / deltay
+        self.scale = 0.95 * min(scalex, scaley)
+        self.log("Autoscale: %f" % self.scale)
+
+    def _getcolors(self):
+        col = PrimiPlotter._getcolors(self)
+        nghost = len(self.atoms.GetGhostCartesianPositions())
+        newcolshape = (nghost + col.shape[0],) + col.shape[1:]
+        newcol = zeros(newcolshape, col.typecode())
+        newcol[:len(col)] = col
+        return newcol
+    
+    def _makeoutput(self, scale, coords, radii, colors):
+        if len(colors.shape) == 1:
+            # Greyscales
+            ncol = 1
+        else:
+            ncol = colors.shape[1]  # 1 or 3.
+            assert ncol == 3  # RGB values
+        # If one processor says RGB, all must convert
+        ncolthis = array([ncol])
+        ncolmax = zeros((1,), ncolthis.typecode())
+        self.mpi.allreduce(ncolthis, ncolmax, self.MPI.max)
+        ncolmax = ncolmax[0]
+        if ncolmax > ncol:
+            assert ncol == 1
+            colors = colors[:,newaxis] + zeros(ncolmax)[newaxis,:]
+            ncol = ncolmax
+            assert colors.shape == (len(coords), ncol)
+        # Now send data from slaves to master
+        data = zeros((len(coords)+1, 4+ncol), float)
+        data[:-1,:3] = coords
+        data[:-1,3] = radii
+        data[-1,-1] = 4+ncol  # Used to communicate shape
+        if ncol == 1:
+            data[:-1,4] = colors
+        else:
+            data[:-1,4:] = colors
+        if not self.master:
+            self.mpi.send(data, 0, self.mpitag)
+        else:
+            total = [data[:-1]]  # Last row is the dimensions.
+            n = len(coords)
+            colsmin = colsmax = 4+ncol
+            for proc in range(1, self.mpi.size):
+                self._verb("Receiving from processor "+str(proc))
+                fdat = self.mpi.receive(float, proc, self.mpitag)[0]
+                fdat.shape = (-1, fdat[-1])
+                fdat = fdat[:-1]  # Last row is the dimensions.
+                total.append(fdat)
+                n = n + len(fdat)
+                if fdat.shape[1] < colsmin:
+                    colsmin = fdat.shape[1]
+                if fdat.shape[1] > colsmax:
+                    colsmax = fdat.shape[1]
+            self._verb("Merging data")
+            # Some processors may have only greyscales whereas others
+            # may have RGB.  That will cause difficulties.
+            trouble = colsmax != colsmin
+            data = zeros((n, colsmax), float)
+            if trouble:
+                assert data.shape[1] == 7
+            else:
+                assert data.shape[1] == 7 or data.shape[1] == 5
+            i = 0
+            for d in total:
+                if not trouble or d.shape[1] == 7:
+                    data[i:i+len(d)] = d
+                else:
+                    assert d.shape[1] == 5
+                    data[i:i+len(d), :5] = d
+                    data[i:i+len(d), 5] = d[4]
+                    data[i:i+len(d), 6] = d[4]
+                i = i + len(d)
+            assert i == len(data)
+            # Now all data is on the master
+            self._verb("Sorting merged data")
+            order = argsort(data[:,2])
+            data = data[order]   ### take(data, order)
+            coords = data[:,:3]
+            radii = data[:,3]
+            if data.shape[1] == 5:
+                colors = data[:,4]
+            else:
+                colors = data[:,4:]
+            PrimiPlotter._makeoutput(self, scale, coords, radii, colors)
+    
+class _PostScriptDevice:
+    """PostScript based output device."""
+    offset = (0,0)   # Will be changed by some classes
+    def __init__(self):
+        self.scale = 1
+        self.linewidth = 1
+        self.outline = 1
+        
+    def set_dimensions(self, dims):
+        self.dims = dims
+
+    def set_owner(self, owner):
+        self.owner = owner
+        
+    def inform_about_scale(self, scale):
+        self.linewidth = 0.1 * scale
+
+    def set_outline(self, value):
+        self.outline = value
+        return self   # Can chain these calls in set_output()
+        
+    def plot(self, *args, **kargs):
+        self.Doplot(self.PSplot, *args, **kargs)
+        
+    def plotArray(self, *args, **kargs):
+        self.Doplot(self.PSplotArray, *args, **kargs)
+        
+    def PSplot(self, file, n, coords, r, colors, noshowpage=0):
+        xy = coords[:,:2]
+        assert(len(xy) == len(r) and len(xy) == len(colors))
+        if len(colors.shape) == 1:
+            gray = 1
+        else:
+            gray = 0
+            assert(colors.shape[1] == 3)
+        file.write("%!PS-Adobe-2.0\n")
+        file.write("%%Creator: Primiplot\n")
+        file.write("%%Pages: 1\n")        
+        file.write("%%%%BoundingBox: %d %d %d %d\n" %
+                   (self.offset + (self.offset[0] + self.dims[0],
+                                   self.offset[1] + self.dims[1])))
+        file.write("%%EndComments\n")
+        file.write("\n")
+        file.write("% Enforce BoundingBox\n")
+        file.write("%d %d moveto %d 0 rlineto 0 %d rlineto -%d 0 rlineto\n" %
+                   ((self.offset + self.dims + (self.dims[0],))))
+        file.write("closepath clip newpath\n\n")
+        file.write("%f %f scale\n" % (2*(1.0/self.scale,)))
+        file.write("%d %d translate\n" % (self.scale * self.offset[0],
+                                          self.scale * self.offset[1]))
+        file.write("\n")
+        if gray:
+            if self.outline:
+                file.write("/circ { 0 360 arc gsave setgray fill grestore stroke } def\n")
+            else:
+                file.write("/circ { 0 360 arc setgray fill } def\n")
+        else:
+            if self.outline:
+                file.write("/circ { 0 360 arc gsave setrgbcolor fill grestore stroke } def\n")
+            else:
+                file.write("/circ { 0 360 arc setrgbcolor fill } def\n")
+        file.write("%f setlinewidth 0.0 setgray\n" %
+                   (self.linewidth * self.scale,))
+        
+        if gray:
+            data = zeros((len(xy), 4), float)
+            data[:,0] = colors
+            data[:,1:3] = (self.scale * xy)
+            data[:,3] = (self.scale * r)
+            for point in data:
+                file.write("%.3f %.2f %.2f %.2f circ\n" % tuple(point))
+        else:
+            data = zeros((len(xy), 6), float)
+            data[:,0:3] = colors
+            data[:,3:5] = (self.scale * xy)
+            data[:,5] = (self.scale * r)
+            for point in data:
+                file.write("%.3f %.3f %.3f %.2f %.2f %.2f circ\n" % tuple(point))
+        if not noshowpage:
+            file.write("showpage\n")
+            
+    def PSplotArray(self, file, n, data, noshowpage=0):
+        assert(len(data.shape) == 3)
+        assert(data.shape[0] == self.dims[1] and data.shape[1] == self.dims[0])
+        data = clip((256*data).astype(int), 0, 255)
+        file.write("%!PS-Adobe-2.0\n")
+        file.write("%%Creator: Fieldplotter\n")
+        file.write("%%Pages: 1\n")        
+        file.write("%%%%BoundingBox: %d %d %d %d\n" %
+                   (self.offset + (self.offset[0] + self.dims[0],
+                                   self.offset[1] + self.dims[1])))
+        file.write("%%EndComments\n")
+        file.write("\n")
+        file.write("%d %d translate\n" % self.offset)
+        file.write("%f %f scale\n" % self.dims)
+        file.write("\n")
+        file.write("% String holding a single line\n")
+        file.write("/pictline %d string def\n" %(data.shape[1]*data.shape[2],))
+        file.write("\n")
+        file.write("%d %d 8\n" % self.dims)
+        file.write("[%d 0 0 %d 0 0]\n" % self.dims)
+        file.write("{currentfile pictline readhexstring pop}\n")
+        file.write("false %d colorimage\n" % (data.shape[2],))
+        file.write("\n")
+        s = ""
+        for d in data.flat:
+            s += ("%02X" % d)
+            if len(s) >= 72:
+                file.write(s+"\n")
+                s = ""
+        file.write(s+"\n")
+        file.write("\n")
+        if not noshowpage:
+            file.write("showpage\n")
+            
+class _PostScriptToFile(_PostScriptDevice):
+    """Output device for PS files."""
+    compr_suffix = None
+    def __init__(self, prefix, compress = 0):
+        self.compress = compress
+        if "'" in prefix:
+            raise ValueError, "Filename may not contain a quote ('): "+prefix
+        if "%" in prefix:
+            # Assume the user knows what (s)he is doing
+            self.filenames = prefix
+        else:
+            self.filenames = prefix + "%04d" + self.suffix
+            if compress:
+                if self.compr_suffix is None:
+                    raise RuntimeError, "Compression not supported."
+                self.filenames = self.filenames + self.compr_suffix
+        _PostScriptDevice.__init__(self)
+
+class PostScriptFile(_PostScriptToFile):
+    suffix = ".ps"
+    compr_suffix = ".gz"
+    offset = (50,50)
+    # Inherits __init__
+
+    def Doplot(self, plotmethod, n, *args, **kargs):
+        filename = self.filenames % (n,)
+        self.owner.log("Output to PostScript file "+filename)
+        if self.compress:
+            file = os.popen("gzip > '"+filename+"'", "w")
+        else:
+            file = open(filename, "w")
+        apply(plotmethod, (file, n)+args, kargs)
+        file.close()
+
+class _PS_via_PnmFile(_PostScriptToFile):
+    gscmd = "gs -q -sDEVICE=pnmraw -sOutputFile=- -dDEVICEWIDTH=%d -dDEVICEHEIGHT=%d - "
+    # Inherits __init__
+
+    def Doplot(self, plotmethod, n, *args, **kargs):
+        filename = self.filenames % (n,)
+        self.owner.log("Output to bitmapped file " + filename)
+        cmd = self.gscmd + self.converter
+        if self.compress:
+            cmd = cmd + "| gzip "
+            
+        cmd = (cmd+" > '%s'") % (self.dims[0], self.dims[1], filename)
+        file = os.popen(cmd, "w")
+        apply(plotmethod, (file, n)+args, kargs)
+        file.close()
+
+class PnmFile(_PS_via_PnmFile):
+    suffix = ".pnm"
+    compr_suffix = ".gz"
+    converter = ""
+
+class GifFile(_PS_via_PnmFile):
+    suffix = ".gif"
+    converter = "| ppmquant -floyd 256 2>/dev/null | ppmtogif 2>/dev/null"
+
+class JpegFile(_PS_via_PnmFile):
+    suffix = ".jpeg"
+    converter = "| ppmtojpeg --smooth=5"
+    
+class X11Window(_PostScriptDevice):
+    """Shows the plot in an X11 window."""
+    #Inherits __init__
+    gscmd = "gs -q -sDEVICE=x11 -dDEVICEWIDTH=%d -dDEVICEHEIGHT=%d -r72x72 -"
+    def Doplot(self, plotmethod, n, *args, **kargs):
+        self.owner.log("Output to X11 window")
+        try:
+            file = self.pipe
+            self.pipe.write("showpage\n")
+        except AttributeError:
+            filename = self.gscmd % tuple(self.dims)
+            file = os.popen(filename, "w")
+            self.pipe = file
+        kargs["noshowpage"] = 1
+        apply(plotmethod, (file, n)+args, kargs)
+        file.write("flushpage\n")
+        file.flush()
+
+# Helper functions
+def _rot(v, axis):
+    ax1, ax2 = ((1, 2), (0, 2), (0, 1))[axis]
+    c, s = cos(v), sin(v)
+    m = zeros((3,3), float)
+    m[axis,axis] = 1.0
+    m[ax1,ax1] = c
+    m[ax2,ax2] = c
+    m[ax1,ax2] = s
+    m[ax2,ax1] = -s
+    return m
+
+def _colorsfromdict(dict, cls):
+    """Extract colors from dictionary using cls as key."""
+    assert(type(dict) == type({}))
+    # Allow local modifications, to replace strings with rgb values.
+    dict = dict.copy()  
+    isgray, isrgb = 0, 0
+    for k in dict.keys():
+        v = dict[k]
+        if type(v) == type("string"):
+            v = color_table[v]
+            dict[k] = v
+        try:
+            if len(v) == 3:
+                isrgb = 1 # Assume it is an RGB value
+                if not hasattr(v, "shape"):
+                    dict[k] = array(v)   # Convert to array
+            else:
+                raise RuntimeError, "Unrecognized color object "+repr(v)
+        except TypeError:
+            isgray = 1 # Assume it is a number
+    if isgray and isrgb:
+        # Convert all to RGB
+        for k in dict.keys():
+            v = dict[k]
+            if not hasattr(v, "shape"):
+                dict[k] = v * ones(3, float)
+    # Now the dictionary is ready
+    if isrgb:
+        colors = zeros((len(cls),3), float)
+    else:
+        colors = zeros((len(cls),), float)
+    for i in xrange(len(cls)):
+        colors[i] = dict[cls[i]]
+    return colors
+
diff --git a/ase/visualize/sage.py b/ase/visualize/sage.py
new file mode 100644
index 0000000..fe3d940
--- /dev/null
+++ b/ase/visualize/sage.py
@@ -0,0 +1,20 @@
+from ase.data.colors import jmol_colors
+from ase.data import covalent_radii, atomic_numbers
+
+
+def view_sage_jmol(atoms):
+    try:
+        from sage.plot.plot3d.shapes import ColorCube, Sphere
+    except:
+        raise ImportError(
+            'view_sage_jmol requires sage (http://www.sagemath.org/) ' +
+            'and is intended to be used directly in the browser')
+    cell = atoms.cell.diagonal() / 2
+    model = ColorCube(list(cell), ['blue', 'blue', 'blue'], opacity=0.1)
+    for atom in atoms:
+        atomic_number = atom.number
+        color = tuple(jmol_colors[atomic_number])
+        radius = covalent_radii[atomic_number]
+        model += Sphere(radius, color=color).translate(
+            *(atom.position - atoms.cell.diagonal() / 2))
+    model.show(aspect_ratio=1, frame=False)
diff --git a/ase/visualize/vtk/__init__.py b/ase/visualize/vtk/__init__.py
new file mode 100644
index 0000000..76f411e
--- /dev/null
+++ b/ase/visualize/vtk/__init__.py
@@ -0,0 +1,36 @@
+try:
+    import vtk
+    hasvtk = True
+    hasmpi = hasattr(vtk, 'vtkMPIController')
+except ImportError:
+    hasvtk = False
+    hasmpi = False
+
+def requirevtk(code=0, parallel=False):
+    from ase.test import NotAvailable
+    if not hasvtk:
+        # VTK required but not installed, force termination
+        # with exit status determined by the code argument.
+        raise NotAvailable('VTK is not installed.', code)
+    if parallel and not hasmpi:
+        # VTK MPI required but not installed, force termination
+        # with exit status determined by the code argument.
+        raise NotAvailable('VTK is not MPI compatible.', code)
+
+def probe_vtk_kilobyte(default=None):
+    if not hasvtk:
+        return default
+
+    from vtk import vtkCharArray
+    vtk_da = vtkCharArray()
+    vtk_da.SetNumberOfComponents(1)
+    vtk_da.SetNumberOfTuples(1024**2)
+
+    # Size of 1 MB = 1024**2 bytes in "VTK kilobytes"
+    size = vtk_da.GetActualMemorySize()
+    if size == 1024:
+        return 1024
+    elif round(abs(size-1024**2/1e3)) == 0:
+        return 1e3
+    else:
+        return default
diff --git a/ase/visualize/vtk/atoms.py b/ase/visualize/vtk/atoms.py
new file mode 100644
index 0000000..1401593
--- /dev/null
+++ b/ase/visualize/vtk/atoms.py
@@ -0,0 +1,127 @@
+
+import numpy as np
+
+from ase import Atoms
+
+from ase.visualize.vtk.sources import vtkAtomSource, vtkForceSource, \
+                                      vtkVelocitySource
+from ase.visualize.vtk.cell import vtkUnitCellModule, vtkAxesModule
+from ase.visualize.vtk.grid import vtkAtomicPositions
+from ase.visualize.vtk.module import vtkModuleAnchor, vtkGlyphModule
+
+# -------------------------------------------------------------------
+
+class vtkAtoms(vtkModuleAnchor, vtkAtomicPositions):
+    """Provides fundamental representation for ``Atoms``-specific data in VTK.
+
+    The ``vtkAtoms`` class plots atoms during simulations, extracting the
+    relevant information from the list of atoms. It is created using
+    the list of atoms as an argument to the constructor. Then one or more
+    visualization modules can be attached using add_module(name, module).
+
+    Example:
+
+    >>> va = vtkAtoms(atoms)
+    >>> va.add_forces()
+    >>> va.add_axes()
+    >>> XXX va.add_to_renderer(vtk_ren)
+
+    """
+    def __init__(self, atoms, scale=1):
+        """Construct a fundamental VTK-representation of atoms.
+
+        atoms: Atoms object or list of Atoms objects
+            The atoms to be plotted.
+
+        scale = 1: float or int
+            Relative scaling of all Atoms-specific visualization.
+
+        """
+        assert isinstance(atoms, Atoms)
+        self.atoms = atoms
+
+        self.scale = scale
+
+        vtkModuleAnchor.__init__(self)
+        vtkAtomicPositions.__init__(self, self.atoms.get_positions(),
+                                    vtkUnitCellModule(self.atoms))
+
+        self.force = None
+        self.velocity = None
+
+        symbols = self.atoms.get_chemical_symbols()
+        for symbol in np.unique(symbols):
+            # Construct mask for all atoms with this symbol
+            mask = np.array(symbols) == symbol
+            if mask.all():
+                subset = None
+            else:
+                subset = np.argwhere(mask).ravel()
+
+            # Get relevant VTK unstructured grid
+            vtk_ugd = self.get_unstructured_grid(subset)
+
+            # Create atomic glyph source for this symbol
+            glyph_source = vtkAtomSource(symbol, self.scale)
+
+            # Create glyph module and anchor it
+            self.add_module(symbol, vtkGlyphModule(vtk_ugd, glyph_source))
+
+    def has_forces(self):
+        return self.force is not None
+
+    def has_velocities(self):
+        return self.velocity is not None
+
+    def add_cell(self):
+        """Add a box outline of the cell using atoms.get_cell(). The existing
+        ``vtkUnitCellModule`` is added to the module anchor under ``cell``."""
+        self.add_module('cell', self.cell)
+
+    def add_axes(self):
+        """Add an orientation indicator for the cartesian axes. An appropriate
+        ``vtkAxesModule`` is added to the module anchor under ``axes``."""
+        self.add_module('axes', vtkAxesModule(self.cell))
+
+    def add_forces(self):
+        """Add force vectors for the atoms using atoms.get_forces(). A
+        ``vtkGlyphModule`` is added to the module anchor under ``force``."""
+        if self.has_forces():
+            raise RuntimeError('Forces already present.')
+        elif self.has_velocities():
+            raise NotImplementedError('Can\'t add forces due to velocities.')
+
+        # Add forces to VTK unstructured grid as vector data
+        vtk_fda = self.add_vector_property(self.atoms.get_forces(), 'force')
+
+        # Calculate max norm of the forces
+        fmax = vtk_fda.GetMaxNorm()
+
+        # Get relevant VTK unstructured grid
+        vtk_ugd = self.get_unstructured_grid()
+
+        self.force = vtkGlyphModule(vtk_ugd, vtkForceSource(fmax, self.scale),
+                                    scalemode='vector', colormode=None)
+        self.add_module('force', self.force)
+
+    def add_velocities(self):
+        """Add velocity vectors for the atoms using atoms.get_velocities(). A
+        ``vtkGlyphModule`` is added to the module anchor under ``velocity``."""
+        if self.has_velocities():
+            raise RuntimeError('Velocities already present.')
+        elif self.has_forces():
+            raise NotImplementedError('Can\'t add velocities due to forces.')
+
+        # Add velocities to VTK unstructured grid as vector data
+        vtk_vda = self.add_vector_property(self.atoms.get_velocities(), 'velocity')
+
+        # Calculate max norm of the velocities
+        vmax = vtk_vda.GetMaxNorm()
+
+        # Get relevant VTK unstructured grid
+        vtk_ugd = self.get_unstructured_grid()
+
+        self.velocity = vtkGlyphModule(vtk_ugd, vtkVelocitySource(vmax, self.scale),
+                                       scalemode='vector', colormode=None)
+        self.add_module('velocity', self.velocity)
+
diff --git a/ase/visualize/vtk/cell.py b/ase/visualize/vtk/cell.py
new file mode 100644
index 0000000..8a68d7d
--- /dev/null
+++ b/ase/visualize/vtk/cell.py
@@ -0,0 +1,116 @@
+
+import numpy as np
+
+from ase import Atoms
+
+from vtk import vtkOutlineSource, vtkAxesActor, vtkProperty2D, vtkTextProperty
+from ase.visualize.vtk.module import vtkModule, vtkPolyDataModule
+
+# -------------------------------------------------------------------
+
+class vtkUnitCellModule(vtkPolyDataModule):
+    def __init__(self, atoms):
+
+        assert isinstance(atoms, Atoms)
+        self.pbc = atoms.get_pbc()
+
+        cell = atoms.get_cell()
+
+        """
+        if not isinstance(cell, np.ndarray):
+            cell = np.array(cell)
+
+        if cell.shape == (3,):
+            cell = np.diag(cell)
+
+        assert cell.dtype == float and cell.shape == (3, 3)
+        """
+
+        self.vtk_outline = vtkOutlineSource()
+
+        if (cell - np.diag(cell.diagonal())).any():
+            corners = np.empty((8,3), dtype=float)
+            # edges = [map(int,[(i-1)%2==0,i%4>=2,i>=4]) for i in range(8)]
+            for c,a in enumerate([(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0), \
+                                  (0, 0, 1), (1, 0, 1), (0, 1, 1), (1, 1, 1)]):
+                corners[c] = np.dot(a, cell)
+            self.bbox = np.array(zip(np.min(corners, axis=0), \
+                                     np.max(corners, axis=0))).ravel()
+            self.vtk_outline.SetCorners(corners.ravel())
+            self.vtk_outline.SetBoxTypeToOriented()
+        else:
+            self.bbox = np.array(zip(np.zeros(3),cell.diagonal())).ravel()
+        self.vtk_outline.SetBounds(self.bbox)
+
+        vtkPolyDataModule.__init__(self, self.vtk_outline)
+
+    def get_bounding_box(self):
+        return self.bbox
+
+    def get_size(self):
+        return self.bbox[1::2]-self.bbox[0::2]
+
+    def get_pbc(self):
+        return self.pbc
+
+    def get_characteristic_length(self):
+        return np.prod(self.get_size())**(1.0/3.0)
+
+# -------------------------------------------------------------------
+
+class vtkAxesModule(vtkModule):
+    def __init__(self, cell):
+
+        assert isinstance(cell, vtkUnitCellModule)
+        self.cell = cell
+
+        l0 = self.cell.get_characteristic_length()
+
+        # Create VTK axes actor (not really a VTK actor though)
+        self.vtk_ax = vtkAxesActor()
+        self.vtk_ax.SetTipTypeToCone()
+        self.vtk_ax.SetConeRadius(5e-2*l0)
+        self.vtk_ax.SetShaftTypeToCylinder()
+        self.vtk_ax.SetCylinderRadius(5e-3*l0)
+
+        # Create VTK two-dimensional property
+        p2d = vtkProperty2D()
+        p2d.SetDisplayLocationToBackground()
+
+        vtkModule.__init__(self, self.vtk_ax, p2d)
+
+        # Create VTK text property and apply to axes
+        vtk_textproperty = vtkTextProperty()
+        vtk_textproperty.SetFontSize(14)
+        vtk_textproperty.SetBold(True)
+        vtk_textproperty.SetItalic(True)
+        vtk_textproperty.SetShadow(True)
+        vtk_textproperty.SetJustificationToRight()
+        vtk_textproperty.SetVerticalJustificationToCentered()
+
+        self.set_text_property(vtk_textproperty)
+
+    def set_actor(self, vtk_act):
+        assert isinstance(vtk_act, vtkAxesActor) #fix for non-vtkActor actor
+        self.vtk_act = vtk_act
+
+    def set_property(self, vtk_property):
+        assert isinstance(vtk_property, vtkProperty2D)
+        for vtk_cap in [self.vtk_ax.GetXAxisCaptionActor2D(),
+                        self.vtk_ax.GetYAxisCaptionActor2D(),
+                        self.vtk_ax.GetZAxisCaptionActor2D()]:
+            #vtk_cap.ThreeDimensionalLeaderOn()
+            #vtk_cap.LeaderOn()
+            vtk_cap.SetProperty(vtk_property)
+            vtk_txt = vtk_cap.GetTextActor()
+            vtk_txt.SetProperty(vtk_property)
+
+    def set_text_property(self, vtk_textproperty, scaled=False):
+        assert isinstance(vtk_textproperty, vtkTextProperty)
+        for vtk_cap in [self.vtk_ax.GetXAxisCaptionActor2D(),
+                        self.vtk_ax.GetYAxisCaptionActor2D(),
+                        self.vtk_ax.GetZAxisCaptionActor2D()]:
+            vtk_txt = vtk_cap.GetTextActor()
+            vtk_txt.SetScaledText(scaled)
+            vtk_txt.SetTextProperty(vtk_textproperty)
+
diff --git a/ase/visualize/vtk/data.py b/ase/visualize/vtk/data.py
new file mode 100644
index 0000000..fcd75ae
--- /dev/null
+++ b/ase/visualize/vtk/data.py
@@ -0,0 +1,194 @@
+
+import numpy as np
+from numpy.ctypeslib import ctypes
+
+from vtk import vtkDataArray, vtkFloatArray, vtkDoubleArray
+
+if ctypes is None:
+    class CTypesEmulator:
+        def __init__(self):
+            self._SimpleCData = np.number
+            self.c_float = np.float32
+            self.c_double = np.float64
+    try:
+        import ctypes
+    except ImportError:
+        ctypes = CTypesEmulator()
+
+# -------------------------------------------------------------------
+
+class vtkNumPyBuffer:
+    def __init__(self, data):
+        self.strbuf = data.tostring()
+        self.nitems = len(data.flat)
+
+    def __len__(self):
+        return self.nitems
+
+    def get_pointer(self):
+        # Any C/C++ method that requires a void * can be passed a Python
+        # string. No check is done to ensure that the string is the correct
+        # size, and the string's reference count is not incremented. Extreme
+        # caution should be applied when using this feature.
+        return self.strbuf
+
+    def notify(self, obj, event):
+        if event == 'DeleteEvent':
+            del self.strbuf
+        else:
+            raise RuntimeError('Event not recognized.')
+
+class vtkDataArrayFromNumPyBuffer:
+    def __init__(self, vtk_class, ctype, data=None):
+
+        assert issubclass(ctype, ctypes._SimpleCData)
+        self.ctype = ctype
+
+        self.vtk_da = vtk_class()
+        assert isinstance(self.vtk_da, vtkDataArray)
+        assert self.vtk_da.GetDataTypeSize() == np.nbytes[np.dtype(self.ctype)]
+
+        if data is not None:
+            self.read_numpy_array(data)
+
+    def read_numpy_array(self, data):
+
+        if not isinstance(data, np.ndarray):
+            data = np.array(data, dtype=self.ctype)
+
+        if data.dtype != self.ctype: # NB: "is not" gets it wrong
+            data = data.astype(self.ctype)
+
+        self.vtk_da.SetNumberOfComponents(data.shape[-1])
+
+        # Passing the void* buffer to the C interface does not increase
+        # its reference count, hence the buffer is deleted by Python when
+        # the reference count of the string from tostring reaches zero.
+        # Also, the boolean True tells VTK to save (not delete) the buffer
+        # when the VTK data array is deleted - we want Python to do this.
+        npybuf = vtkNumPyBuffer(data)
+        self.vtk_da.SetVoidArray(npybuf.get_pointer(), len(npybuf), True)
+        self.vtk_da.AddObserver('DeleteEvent', npybuf.notify)
+
+    def get_output(self):
+        return self.vtk_da
+
+    def copy(self):
+        vtk_da_copy = self.vtk_da.NewInstance()
+        vtk_da_copy.SetNumberOfComponents(self.vtk_da.GetNumberOfComponents())
+        vtk_da_copy.SetNumberOfTuples(self.vtk_da.GetNumberOfTuples())
+
+        assert vtk_da_copy.GetSize() == self.vtk_da.GetSize()
+
+        vtk_da_copy.DeepCopy(self.vtk_da)
+
+        return vtk_da_copy
+
+# -------------------------------------------------------------------
+
+class vtkDataArrayFromNumPyArray(vtkDataArrayFromNumPyBuffer):
+    """Class for reading vtkDataArray from 1D or 2D NumPy array.
+
+    This class can be used to generate a vtkDataArray from a NumPy array.
+    The NumPy array should be of the form <entries> x <number of components>
+    where 'number of components' indicates the number of components in 
+    each entry in the vtkDataArray. Note that this form is also expected
+    even in the case of only a single component.
+    """
+    def __init__(self, vtk_class, ctype, data=None, buffered=True):
+
+        self.buffered = buffered
+
+        vtkDataArrayFromNumPyBuffer.__init__(self, vtk_class, ctype, data)
+
+    def read_numpy_array(self, data):
+        """Read vtkDataArray from NumPy array"""
+
+        if not isinstance(data, np.ndarray):
+            data = np.array(data, dtype=self.ctype)
+
+        if data.dtype != self.ctype: # NB: "is not" gets it wrong
+            data = data.astype(self.ctype)
+
+        if data.ndim == 1:
+            data = data[:, np.newaxis]
+        elif data.ndim != 2:
+            raise ValueError('Data must be a 1D or 2D NumPy array.')
+
+        if self.buffered:
+            vtkDataArrayFromNumPyBuffer.read_numpy_array(self, data)
+        else:
+            self.vtk_da.SetNumberOfComponents(data.shape[-1])
+            self.vtk_da.SetNumberOfTuples(data.shape[0])
+
+            for i, d_c in enumerate(data):
+                for c, d in enumerate(d_c):
+                    self.vtk_da.SetComponent(i, c, d)
+
+class vtkFloatArrayFromNumPyArray(vtkDataArrayFromNumPyArray):
+    def __init__(self, data):
+        vtkDataArrayFromNumPyArray.__init__(self, vtkFloatArray,
+                                            ctypes.c_float, data)
+
+class vtkDoubleArrayFromNumPyArray(vtkDataArrayFromNumPyArray):
+    def __init__(self, data):
+        vtkDataArrayFromNumPyArray.__init__(self, vtkDoubleArray,
+                                            ctypes.c_double, data)
+
+# -------------------------------------------------------------------
+
+class vtkDataArrayFromNumPyMultiArray(vtkDataArrayFromNumPyBuffer):
+    """Class for reading vtkDataArray from a multi-dimensional NumPy array.
+
+    This class can be used to generate a vtkDataArray from a NumPy array.
+    The NumPy array should be of the form <gridsize> x <number of components>
+    where 'number of components' indicates the number of components in 
+    each gridpoint in the vtkDataArray. Note that this form is also expected
+    even in the case of only a single component.
+    """
+    def __init__(self, vtk_class, ctype, data=None, buffered=True):
+
+        self.buffered = buffered
+
+        vtkDataArrayFromNumPyBuffer.__init__(self, vtk_class, ctype, data)
+
+    def read_numpy_array(self, data):
+        """Read vtkDataArray from NumPy array"""
+
+        if not isinstance(data, np.ndarray):
+            data = np.array(data, dtype=self.ctype)
+
+        if data.dtype != self.ctype: # NB: "is not" gets it wrong
+            data = data.astype(self.ctype)
+
+        if data.ndim <=2:
+            raise Warning('This is inefficient for 1D and 2D NumPy arrays. ' +
+                          'Use a vtkDataArrayFromNumPyArray subclass instead.')
+
+        if self.buffered:
+            # This is less than ideal, but will not copy data (uses views).
+            # To get the correct ordering, the grid dimensions have to be
+            # transposed without moving the last dimension (the components).
+            n = data.ndim-1
+            for c in range(n//2):
+                data = data.swapaxes(c,n-1-c)
+
+            vtkDataArrayFromNumPyBuffer.read_numpy_array(self, data)
+        else:
+            self.vtk_da.SetNumberOfComponents(data.shape[-1])
+            self.vtk_da.SetNumberOfTuples(np.prod(data.shape[:-1]))
+
+            for c, d_T in enumerate(data.T):
+                for i, d in enumerate(d_T.flat):
+                    self.vtk_da.SetComponent(i, c, d)
+
+class vtkFloatArrayFromNumPyMultiArray(vtkDataArrayFromNumPyMultiArray):
+    def __init__(self, data):
+        vtkDataArrayFromNumPyMultiArray.__init__(self, vtkFloatArray,
+                                                 ctypes.c_float, data)
+
+class vtkDoubleArrayFromNumPyMultiArray(vtkDataArrayFromNumPyMultiArray):
+    def __init__(self, data):
+        vtkDataArrayFromNumPyMultiArray.__init__(self, vtkDoubleArray,
+                                                 ctypes.c_double, data)
+
diff --git a/ase/visualize/vtk/grid.py b/ase/visualize/vtk/grid.py
new file mode 100644
index 0000000..dedf85c
--- /dev/null
+++ b/ase/visualize/vtk/grid.py
@@ -0,0 +1,315 @@
+
+import numpy as np
+
+from vtk import vtkPointData, vtkDataArray, vtkUnstructuredGrid, vtkPoints, \
+                vtkIdList, vtkStructuredPoints
+from ase.visualize.vtk.cell import vtkUnitCellModule
+from ase.visualize.vtk.data import vtkDataArrayFromNumPyBuffer, \
+                                   vtkDoubleArrayFromNumPyArray, \
+                                   vtkDoubleArrayFromNumPyMultiArray
+
+# -------------------------------------------------------------------
+
+class vtkBaseGrid:
+    def __init__(self, npoints, cell):
+        self.npoints = npoints
+
+        # Make sure cell argument is correct type
+        assert isinstance(cell, vtkUnitCellModule)
+        self.cell = cell
+
+        self.vtk_pointdata = None
+
+    def set_point_data(self, vtk_pointdata):
+        if self.vtk_pointdata is not None:
+            raise RuntimeError('VTK point data already present.')
+
+        assert isinstance(vtk_pointdata, vtkPointData)
+        self.vtk_pointdata = vtk_pointdata
+        #self.vtk_pointdata.SetCopyScalars(False)
+        #self.vtk_pointdata.SetCopyVectors(False)
+        #self.vtk_pointdata.SetCopyNormals(False)
+
+    def get_point_data(self):
+        if self.vtk_pointdata is None:
+            raise RuntimeError('VTK point data missing.')
+
+        return self.vtk_pointdata
+
+    def get_number_of_points(self):
+        return self.npoints
+
+    def add_scalar_data_array(self, data, name=None, active=True):
+
+        # Are we converting from NumPy buffer to VTK array?
+        if isinstance(data, vtkDataArray):
+            vtk_sda = data
+        elif isinstance(data, vtkDataArrayFromNumPyBuffer):
+            vtk_sda = data.get_output()
+        else:
+            raise ValueError('Data is not a valid scalar data array.')
+
+        del data
+
+        assert vtk_sda.GetNumberOfComponents() == 1
+        assert vtk_sda.GetNumberOfTuples() == self.npoints
+
+        if name is not None:
+            vtk_sda.SetName(name)
+
+        # Add VTK array to VTK point data
+        self.vtk_pointdata.AddArray(vtk_sda)
+
+        if active:
+            self.vtk_pointdata.SetActiveScalars(name)
+
+        return vtk_sda
+
+    def add_vector_data_array(self, data, name=None, active=True):
+
+        # Are we converting from NumPy buffer to VTK array?
+        if isinstance(data, vtkDataArray):
+            vtk_vda = data
+        elif isinstance(data, vtkDataArrayFromNumPyBuffer):
+            vtk_vda = data.get_output()
+        else:
+            raise ValueError('Data is not a valid vector data array.')
+
+        del data
+
+        assert vtk_vda.GetNumberOfComponents() == 3
+        assert vtk_vda.GetNumberOfTuples() == self.npoints
+
+        if name is not None:
+            vtk_vda.SetName(name)
+
+        # Add VTK array to VTK point data
+        self.vtk_pointdata.AddArray(vtk_vda)
+
+        if active:
+            self.vtk_pointdata.SetActiveVectors(name)
+
+        return vtk_vda
+
+# -------------------------------------------------------------------
+
+class vtkAtomicPositions(vtkBaseGrid):
+    """Provides an interface for adding ``Atoms``-centered data to VTK
+    modules. Atomic positions, e.g. obtained using atoms.get_positions(),
+    constitute an unstructured grid in VTK, to which scalar and vector
+    can be added as point data sets.
+
+    Just like ``Atoms``, instances of ``vtkAtomicPositions`` can be divided
+    into subsets, which makes it easy to select atoms and add properties.
+
+    Example:
+
+    >>> cell = vtkUnitCellModule(atoms)
+    >>> apos = vtkAtomicPositions(atoms.get_positions(), cell)
+    >>> apos.add_scalar_property(atoms.get_charges(), 'charges')
+    >>> apos.add_vector_property(atoms.get_forces(), 'forces')
+
+    """
+    def __init__(self, pos, cell):
+        """Construct basic VTK-representation of a set of atomic positions.
+
+        pos: NumPy array of dtype float and shape ``(n,3)``
+            Cartesian positions of the atoms.
+        cell: Instance of vtkUnitCellModule of subclass thereof
+            Holds information equivalent to that of atoms.get_cell().
+
+        """
+        # Make sure position argument is a valid array
+        if not isinstance(pos, np.ndarray):
+            pos = np.array(pos)
+
+        assert pos.dtype == float and pos.shape[1:] == (3,)
+
+        vtkBaseGrid.__init__(self, len(pos), cell)
+
+        # Convert positions to VTK array
+        npy2da = vtkDoubleArrayFromNumPyArray(pos)
+        vtk_pda = npy2da.get_output()
+        del npy2da
+
+        # Transfer atomic positions to VTK points
+        self.vtk_pts = vtkPoints()
+        self.vtk_pts.SetData(vtk_pda)
+
+        # Create a VTK unstructured grid of these points
+        self.vtk_ugd = vtkUnstructuredGrid()
+        self.vtk_ugd.SetWholeBoundingBox(self.cell.get_bounding_box())
+        self.vtk_ugd.SetPoints(self.vtk_pts)
+
+        # Extract the VTK point data set
+        self.set_point_data(self.vtk_ugd.GetPointData())
+
+    def get_points(self, subset=None):
+        """Return (subset of) vtkPoints containing atomic positions.
+
+        subset=None: list of int
+            A list of indices into the atomic positions; ignored if None.
+
+        """
+        if subset is None:
+            return self.vtk_pts
+
+        # Create a list of indices from the subset
+        vtk_il = vtkIdList()
+        for i in subset:
+            vtk_il.InsertNextId(i)
+
+        # Allocate VTK points for subset
+        vtk_subpts = vtkPoints()
+        vtk_subpts.SetDataType(self.vtk_pts.GetDataType())
+        vtk_subpts.SetNumberOfPoints(vtk_il.GetNumberOfIds())
+
+        # Transfer subset of VTK points
+        self.vtk_pts.GetPoints(vtk_il, vtk_subpts)
+
+        return vtk_subpts
+
+    def get_unstructured_grid(self, subset=None):
+        """Return (subset of) an unstructured grid of the atomic positions.
+
+        subset=None: list of int
+            A list of indices into the atomic positions; ignored if None.
+
+        """
+        if subset is None:
+            return self.vtk_ugd
+
+        # Get subset of VTK points
+        vtk_subpts = self.get_points(subset)
+
+        # Create a VTK unstructured grid of these points
+        vtk_subugd = vtkUnstructuredGrid()
+        vtk_subugd.SetWholeBoundingBox(self.cell.get_bounding_box())
+        vtk_subugd.SetPoints(vtk_subpts)
+
+        return vtk_subugd
+
+    def add_scalar_property(self, data, name=None, active=True):
+        """Add VTK-representation of scalar data at the atomic positions.
+
+        data: NumPy array of dtype float and shape ``(n,)``
+            Scalar values corresponding to the atomic positions.
+        name=None: str
+            Unique identifier for the scalar data.
+        active=True: bool
+            Flag indicating whether to use as active scalar data.
+
+        """
+        # Make sure data argument is a valid array
+        if not isinstance(data, np.ndarray):
+            data = np.array(data)
+
+        assert data.dtype == float and data.shape == (self.npoints,)
+
+        # Convert scalar properties to VTK array
+        npa2da = vtkDoubleArrayFromNumPyArray(data)
+        return vtkBaseGrid.add_scalar_data_array(self, npa2da, name, active)
+
+    def add_vector_property(self, data, name=None, active=True):
+        """Add VTK-representation of vector data at the atomic positions.
+
+        data: NumPy array of dtype float and shape ``(n,3)``
+            Vector components corresponding to the atomic positions.
+        name=None: str
+            Unique identifier for the vector data.
+        active=True: bool
+            Flag indicating whether to use as active vector data.
+
+        """
+        # Make sure data argument is a valid array
+        if not isinstance(data, np.ndarray):
+            data = np.array(data)
+
+        assert data.dtype == float and data.shape == (self.npoints,3,)
+
+        # Convert vector properties to VTK array
+        npa2da = vtkDoubleArrayFromNumPyArray(data)
+        return vtkBaseGrid.add_vector_data_array(self, npa2da, name, active)
+
+# -------------------------------------------------------------------
+
+class vtkVolumeGrid(vtkBaseGrid):
+    def __init__(self, elements, cell, origin=None):
+
+        # Make sure element argument is a valid array
+        if not isinstance(elements, np.ndarray):
+            elements = np.array(elements)
+
+        assert elements.dtype == int and elements.shape == (3,)
+        self.elements = elements
+
+        vtkBaseGrid.__init__(self, np.prod(self.elements), cell)
+
+        # Create a VTK grid of structured points
+        self.vtk_spts = vtkStructuredPoints()
+        self.vtk_spts.SetWholeBoundingBox(self.cell.get_bounding_box())
+        self.vtk_spts.SetDimensions(self.elements)
+        self.vtk_spts.SetSpacing(self.get_grid_spacing())
+
+        if origin is not None:
+            self.vtk_spts.SetOrigin(origin)
+
+        # Extract the VTK point data set
+        self.set_point_data(self.vtk_spts.GetPointData())
+
+    def get_grid_spacing(self):
+        # Periodic boundary conditions leave out one boundary along an axis
+        # Zero/fixed boundary conditions leave out both boundaries of an axis
+        return self.cell.get_size()/(self.elements+1.0-self.cell.get_pbc())
+
+    def get_relaxation_factor(self):
+        # The relaxation factor is a floating point value between zero and one.
+        # It expresses the need for smoothening (relaxation) e.g. of isosurfaces
+        # due to coarse grid spacings. Larger grid spacing -> larger relaxation.
+        x = self.get_grid_spacing().mean()/self.cell.get_characteristic_length()
+
+        # The relaxation function f(x) satisfies the following requirements
+        # f(x) -> 0 for x -> 0+   and   f(x) -> b for x -> inf
+        # f'(x) -> a for x -> 0+  and   f'(x) -> 0 for x -> inf
+
+        # Furthermore, it is a rescaling of arctan, hence we know
+        # f(x) = 2 b arctan(a pi x / 2 b) / pi
+
+        # Our reference point is x = r for which medium relaxion is needed
+        # f(r) = b/2   <=>   r = 2 b / a pi   <=>   a = 2 b / r pi
+        r = 0.025 # corresponding to 0.2 Ang grid spacing in 8 Ang cell
+        b = 0.5
+        f = 2*b*np.arctan(x/r)/np.pi
+
+        if f > 0.1:
+           return f.round(1)
+        else:
+           return None
+
+    def get_structured_points(self):
+        return self.vtk_spts
+
+    def add_scalar_field(self, data, name=None, active=True):
+
+        # Make sure data argument is a valid array
+        if not isinstance(data, np.ndarray):
+            data = np.array(data)
+
+        assert data.dtype == float and data.shape == tuple(self.elements)
+
+        # Convert scalar field to VTK array
+        npa2da = vtkDoubleArrayFromNumPyMultiArray(data[...,np.newaxis])
+        return vtkBaseGrid.add_scalar_data_array(self, npa2da, name, active)
+
+    def add_vector_field(self, data, name=None, active=True):
+
+        # Make sure data argument is a valid array
+        if not isinstance(data, np.ndarray):
+            data = np.array(data)
+
+        assert data.dtype == float and data.shape == tuple(self.elements)+(3,)
+
+        # Convert vector field to VTK array
+        npa2da = vtkDoubleArrayFromNumPyMultiArray(data)
+        return vtkBaseGrid.add_vector_data_array(self, npa2da, name, active)
+
diff --git a/ase/visualize/vtk/module.py b/ase/visualize/vtk/module.py
new file mode 100644
index 0000000..455d463
--- /dev/null
+++ b/ase/visualize/vtk/module.py
@@ -0,0 +1,211 @@
+
+import numpy as np
+
+from vtk import vtkProp3D, vtkPolyDataMapper, vtkActor, vtkLODActor, \
+                vtkPointSet, vtkGlyph3D, vtkRenderer
+from ase.visualize.vtk.sources import vtkCustomGlyphSource, \
+                                      vtkClampedGlyphSource
+
+# -------------------------------------------------------------------
+
+class vtkModule:
+    """Modules represent a unified collection of VTK objects needed for
+    introducing basic visualization concepts such as surfaces or shapes.
+
+    A common trait of all modules is the need for an actor representation and
+    corresponding generic properties such as lighting, color and transparency.
+
+    """ 
+    def __init__(self, vtk_act, vtk_property=None):
+        """Construct basic VTK-representation of a module.
+
+        vtk_act: Instance of vtkActor or subclass thereof
+            A vtkActor represents an entity in a rendering scene.
+
+        vtk_property = None: Instance of vtkProperty or subclass thereof
+            A vtkProperty represents e.g. lighting and other surface
+            properties of a geometric object, in this case the actor.
+
+        """
+        self.vtk_act = None
+        self.set_actor(vtk_act)
+
+        if vtk_property is not None:
+            self.set_property(vtk_property)
+
+    def set_actor(self, vtk_act):
+        """Set the actor representing this module in a rendering scene."""
+        assert isinstance(vtk_act, vtkActor)
+        self.vtk_act = vtk_act
+
+    def set_property(self, vtk_property):
+        """Set the property of the actor representing this module."""
+        self.vtk_act.SetProperty(vtk_property)
+
+    def get_actor(self):
+        """Return the actor representing this module in a rendering scene."""
+        return self.vtk_act
+
+# -------------------------------------------------------------------
+
+class vtkLODModule(vtkModule):
+    _vtk_actor_class = vtkLODActor
+
+    def get_lod(self):
+        return 100
+
+    def set_actor(self, vtk_act):
+        vtkModule.set_actor(self, vtk_act)
+
+        if isinstance(vtk_act, vtkLODActor):
+            vtk_act.SetNumberOfCloudPoints(self.get_lod())
+
+class vtkPolyDataModule(vtkModule):
+    _vtk_actor_class = vtkActor
+
+    __doc__ = vtkModule.__doc__ + """
+    Poly data modules are based on polygonal data sets, which can be mapped
+    into graphics primitives suitable for rendering within the VTK framework.
+
+    """
+    def __init__(self, vtk_polydata, vtk_property=None):
+        """Construct VTK-representation of a module containing polygonals.
+
+        vtk_polydata: Instance of vtkPolyData, subclass thereof or
+            vtkPolyDataAlgorithm, which produces vtkPolyData as output.
+            A vtkPolyData represents a polygonal data set consisting of
+            point and cell attributes, which can be mapped to graphics
+            primitives for subsequent rendering.
+
+        vtk_property = None: Instance of vtkProperty or subclass thereof
+            A vtkProperty represents e.g. lighting and other surface
+            properties of a geometric object, in this case the polydata.
+
+        """
+        vtkModule.__init__(self, self._vtk_actor_class(), vtk_property)
+
+        self.vtk_dmap = vtkPolyDataMapper()
+        self.vtk_dmap.SetInputConnection(vtk_polydata.GetOutputPort())
+        self.vtk_act.SetMapper(self.vtk_dmap)
+
+class vtkGlyphModule(vtkPolyDataModule):
+    __doc__ = vtkPolyDataModule.__doc__ + """
+    Glyph modules construct these polygonal data sets by replicating a glyph
+    source across a specific set of points, using available scalar or vector
+    point data to scale and orientate the glyph source if desired.
+
+    Example:
+
+    >>> atoms = molecule('C60')
+    >>> cell = vtkUnitCellModule(atoms)
+    >>> apos = vtkAtomicPositions(atoms.get_positions(), cell)
+    >>> vtk_ugd = apos.get_unstructured_grid()
+    >>> glyph_source = vtkAtomSource('C')
+    >>> glyph_module = vtkGlyphModule(vtk_ugd, glyph_source)
+
+    """
+    def __init__(self, vtk_pointset, glyph_source,
+                 scalemode=None, colormode=None):
+        """Construct VTK-representation of a module containing glyphs.
+        These glyphs share a common source, defining their geometrical
+        shape, which is cloned and oriented according to the input data.
+
+        vtk_pointset: Instance of vtkPointSet or subclass thereof
+            A vtkPointSet defines a set of positions, which may then be
+            assigned specific scalar of vector data across the entire set.
+
+        glyph_source: Instance of ~vtk.vtkCustomGlyphSource or subclass thereof
+            Provides the basic shape to distribute over the point set.
+
+        """
+        assert isinstance(vtk_pointset, vtkPointSet)
+        assert isinstance(glyph_source, vtkCustomGlyphSource)
+
+        # Create VTK Glyph3D based on unstructured grid
+        self.vtk_g3d = vtkGlyph3D()
+        self.vtk_g3d.SetInput(vtk_pointset)
+        self.vtk_g3d.SetSource(glyph_source.get_output())
+        self.vtk_g3d.SetScaleFactor(glyph_source.get_scale())
+
+        # Clamping normalizes the glyph scaling to within the range [0,1].
+        # Setting a scale factor will then scale these by the given factor.
+        if isinstance(glyph_source, vtkClampedGlyphSource):
+            self.vtk_g3d.SetClamping(True)
+            self.vtk_g3d.SetRange(glyph_source.get_range())
+
+        if scalemode is 'off':
+            self.vtk_g3d.SetScaleModeToDataScalingOff()
+        elif scalemode is 'scalar':
+            self.vtk_g3d.SetScaleModeToScaleByScalar()
+        elif scalemode is 'vector':
+            self.vtk_g3d.SetScaleModeToScaleByVector()
+        elif scalemode is not None:
+            raise ValueError('Unrecognized scale mode \'%s\'.' % scalemode)
+
+        if colormode is 'scale':
+            self.vtk_g3d.SetColorModeToColorByScale()
+        elif colormode is 'scalar':
+            self.vtk_g3d.SetColorModeToColorByScalar()
+        elif colormode is 'vector':
+            self.vtk_g3d.SetColorModeToColorByVector()
+        elif colormode is not None:
+            raise ValueError('Unrecognized scale mode \'%s\'.' % scalemode)
+
+        vtkPolyDataModule.__init__(self, self.vtk_g3d, glyph_source.get_property())
+
+# -------------------------------------------------------------------
+
+class vtkLabelModule(vtkModule):
+    def __init__(self, vtk_pointset, vtk_property=None):
+
+        vtk_Module.__init__(self, vtkActor(), vtk_property)
+
+        assert isinstance(vtk_pointset, vtkPointSet)
+
+        self.vtk_dmap = vtkLabeledDataMapper()
+        #self.vtk_dmap.SetLabelModeToLabelIds() #TODO XXX strings!!!
+        self.vtk_dmap.GetLabelTextProperty().SetFontSize(12)
+        self.vtk_dmap.GetLabelTextProperty().SetJustificationToRight()
+        self.vtk_dmap.SetInputConnection(vtk_pointset.GetOutputPort())
+        self.vtk_act.SetMapper(self.vtk_dmap)
+
+        #vtk_g3d.GetPointIdsName...
+
+# -------------------------------------------------------------------
+
+class vtkModuleAnchor:
+    """
+    TODO XXX
+    """
+    def __init__(self):
+        """Construct an anchoring point for various VTK modules.
+
+        """
+        self.modules = {}
+
+    def get_module(self, name):
+        if name not in self.modules.keys():
+            raise RuntimeError('Module \'%s\' does not exists.' % name)
+
+        return self.modules[name]
+
+    def add_module(self, name, module):
+        if not isinstance(module, vtkModule):
+            raise ValueError('Module must be instance of vtkModule.')
+
+        if name in self.modules.keys():
+            raise RuntimeError('Module \'%s\' already exists.' % name)
+
+        self.modules[name] = module
+
+    def get_actor(self, name):
+        return self.get_module(name).get_actor()
+
+    def add_actors_to_renderer(self, vtk_renderer, name=None):
+        assert isinstance(vtk_renderer, vtkRenderer)
+        if name is None:
+            for module in self.modules.values():
+                vtk_renderer.AddActor(module.get_actor())
+        else:
+            vtk_renderer.AddActor(self.get_actor(name))
+
diff --git a/ase/visualize/vtk/pipeline.py b/ase/visualize/vtk/pipeline.py
new file mode 100644
index 0000000..a001a8d
--- /dev/null
+++ b/ase/visualize/vtk/pipeline.py
@@ -0,0 +1,213 @@
+
+import numpy as np
+
+from vtk import vtkPolyDataMapper, vtkPolyDataNormals, \
+                vtkLinearSubdivisionFilter, vtkSmoothPolyDataFilter, \
+                vtkDepthSortPolyData, vtkAlgorithm, vtkAlgorithmOutput
+from ase.visualize.vtk.grid import vtkVolumeGrid
+
+# -------------------------------------------------------------------
+
+class vtkPipeline:
+    _branchable = False
+
+    def __init__(self, vtkish_data=None):
+
+        self.vtkish_data = None
+        self.closed = False
+        self.pending = False
+        self.filters = tuple()
+
+        if vtkish_data is not None:
+            self.set_data(vtkish_data)
+
+    def hasdata(self):
+        return (self.vtkish_data is not None)
+
+    def hasfilters(self):
+        return len(self.filters)>0
+
+    def set_data(self, vtkish_data):
+
+        assert vtkish_data is not None
+
+        if self.hasdata():
+            raise RuntimeError('Pipeline already has input.')
+        elif vtkish_data in self:
+            raise ValueError('Pipeline loop detected.')
+
+        if isinstance(vtkish_data, vtkPipeline):
+            # The embedded pipeline takes over
+            vtkish_data.signal_close()
+
+        self.vtkish_data = vtkish_data
+
+        if not self.hasfilters():
+            return
+
+        vtkish_inner = self.filters[0]
+        
+        if isinstance(vtkish_inner, vtkPipeline):
+            vtkish_inner.set_data(vtkish_data) #TODO does this work?
+        else:
+            assert isinstance(vtkish_inner, vtkAlgorithm)
+
+            if isinstance(vtkish_data, vtkPipeline):
+                # The embedded pipeline takes over
+                vtkish_data.connect(vtkish_inner)
+            else:
+                assert isinstance(vtkish_data, vtkAlgorithm)
+                vtkish_inner.SetInputConnection(vtkish_data.GetOutputPort())
+
+    def isempty(self):
+        return not self.hasdata() and not self.hasfilters()
+
+    def isclosed(self):
+        if self.pending:
+            raise RuntimeError('Pipeline output port state is pending.')
+        return self.closed
+
+    def signal_close(self):
+        if self.closed:
+            raise RuntimeError('Pipeline output port is already closed.')
+        elif not self._branchable:
+            self.pending = True
+
+    def get_output_port(self):
+        if self.closed:
+            raise RuntimeError('Pipeline output port is closed.')
+        elif self.pending:
+            self.closed = True
+            self.pending = False
+
+        if self.hasfilters():
+            vtkish_outer = self.filters[-1]
+        elif self.hasdata():
+            vtkish_outer = self.vtkish_data
+        else:
+            raise RuntimeError('Pipeline output port unavailable.')
+
+        if isinstance(vtkish_outer, vtkPipeline):
+            return vtkish_outer.get_output_port()
+        else:
+            assert isinstance(vtkish_outer, vtkAlgorithm)
+            return vtkish_outer.GetOutputPort()
+
+    def set_input_connection(self, vtk_port):
+        assert isinstance(vtk_port, vtkAlgorithmOutput)
+
+        vtkish_inner = self.filters[0]
+
+        if isinstance(vtkish_inner, vtkPipeline):
+            # Connect must be passed down
+            vtkish_inner.set_input_connection(vtk_port)
+        else:
+            vtkish_inner.SetInputConnection(vtk_port)
+
+    def connect(self, vtkish_filter):
+        if vtkish_filter in self:
+            raise ValueError('Pipeline loop detected.')
+
+        if isinstance(vtkish_filter, vtkPipeline):
+            # Connection must be passed down
+            if not self.isempty():
+                vtkish_filter.set_input_connection(self.get_output_port())
+
+            # The containing pipeline takes over
+            vtkish_filter.signal_close()
+
+        elif not self.isempty():
+            assert isinstance(vtkish_filter, vtkAlgorithm)
+            vtkish_filter.SetInputConnection(self.get_output_port())
+
+    def append(self, vtkish_filter):
+        self.connect(vtkish_filter)
+        self.filters += (vtkish_filter,)
+
+    def extend(self, vtkish_filters):
+        map(self.append, vtkish_filters)
+
+    def __contains__(self, vtkish_candidate):
+        if vtkish_candidate == self:
+            return True
+
+        if self.hasdata():
+            if isinstance(self.vtkish_data, vtkPipeline):
+                if vtkish_candidate in self.vtkish_data:
+                    return True
+            else:
+                if vtkish_candidate == self.vtkish_data:
+                    return True
+
+        if vtkish_candidate in self.filters:
+            return True
+
+        for vtkish_filter in self.filters:
+            if isinstance(vtkish_filter, vtkPipeline) \
+                and vtkish_candidate in vtkish_filter:
+                return True
+
+        return False
+
+    def __getitem__(self, i): #TODO XXX this is a hack
+        return self.filters[i]
+
+# -------------------------------------------------------------------
+
+class vtkPolyDataPipeline(vtkPipeline):
+    def __init__(self, vtkish_polydata=None):
+        vtkPipeline.__init__(self, vtkish_polydata)
+
+    def connect(self, vtkish_filter):
+        if isinstance(vtkish_filter, vtkPolyDataMapper):
+            self.signal_close()
+        vtkPipeline.connect(self, vtkish_filter)
+
+class vtkSurfaceSmootherPipeline(vtkPolyDataPipeline):
+    def __init__(self, grid, vtkish_polydata=None, angle=15):
+
+        vtkPolyDataPipeline.__init__(self, vtkish_polydata)
+
+        # Make sure grid argument is correct type
+        assert isinstance(grid, vtkVolumeGrid)
+        self.grid = grid
+
+        # Split polys with intersection angles greater than angle
+        vtk_dnorm = vtkPolyDataNormals()
+        vtk_dnorm.SetFeatureAngle(angle)
+        vtk_dnorm.SplittingOn()
+        vtk_dnorm.ComputeCellNormalsOff()
+        vtk_dnorm.ComputePointNormalsOff()
+        self.append(vtk_dnorm)
+
+        relax = self.grid.get_relaxation_factor()
+
+        if relax is not None:
+            print 'relax=',relax
+            #vtk_subdiv = vtkButterflySubdivisionFilter()
+            vtk_subdiv = vtkLinearSubdivisionFilter()
+            self.append(vtk_subdiv)
+    
+            # Smooth out some of the sharp points.
+            vtk_smooth = vtkSmoothPolyDataFilter()
+            vtk_smooth.SetRelaxationFactor(relax)
+            self.append(vtk_smooth)
+
+class vtkDepthSortPipeline(vtkPolyDataPipeline):
+    def __init__(self, vtk_renderer, vtkish_polydata=None):
+
+        vtkPolyDataPipeline.__init__(self, vtkish_polydata)
+
+        # The depht sort object is set up to generate scalars representing
+        # the sort depth. A camera is assigned for the sorting. The camera
+        # defines the sort vector (position and focal point).
+        vtk_ds = vtkDepthSortPolyData()
+        vtk_ds.SetCamera(vtk_renderer.GetActiveCamera())
+        vtk_ds.SetDirectionToBackToFront()
+        #vtk_ds.SetVector(1, 1, 1)
+        #vtk_ds.SortScalarsOn()
+        #vtk_ds.Update()
+        self.append(vtk_ds)
+
+        vtk_renderer.ResetCamera()
+
diff --git a/ase/visualize/vtk/sources.py b/ase/visualize/vtk/sources.py
new file mode 100644
index 0000000..a37803f
--- /dev/null
+++ b/ase/visualize/vtk/sources.py
@@ -0,0 +1,104 @@
+
+import numpy as np
+
+from vtk import vtkProperty, vtkSphereSource, vtkArrowSource, vtkConeSource
+
+from ase.data import atomic_numbers
+from ase.data import covalent_radii as atomic_radii
+from ase.data.colors import jmol_colors as atomic_colors
+#from ase.data.colors import cpk_colors as atomic_colors
+
+avg_radius = np.mean(atomic_radii[np.isfinite(atomic_radii)])
+
+# -------------------------------------------------------------------
+
+class vtkCustomGlyphSource:
+    def __init__(self, scale, vtk_glyph_source):
+        self.scale = scale
+        self.vtk_glyph_source = vtk_glyph_source
+        self.vtk_property = vtkProperty()
+
+    def get_scale(self):
+        return self.scale
+
+    def get_property(self):
+        return self.vtk_property
+
+    def get_output(self):
+        return self.vtk_glyph_source.GetOutput()
+
+class vtkAtomSource(vtkCustomGlyphSource):
+    def __init__(self, name, scale=1, fraction=0.25):
+        vtkCustomGlyphSource.__init__(self, scale, vtkSphereSource())
+
+        self.number = atomic_numbers[name]
+        self.radius = atomic_radii[self.number]
+        self.color = atomic_colors[self.number]
+
+        self.vtk_property.SetColor(self.color[0],self.color[1],self.color[2])
+        self.vtk_property.SetInterpolationToPhong()
+        self.vtk_property.SetDiffuse(0.7)
+        self.vtk_property.SetSpecular(0.4)
+        self.vtk_property.SetSpecularPower(20)
+
+        self.vtk_glyph_source.SetPhiResolution(16)
+        self.vtk_glyph_source.SetThetaResolution(16)
+        self.vtk_glyph_source.SetRadius(fraction*self.radius)
+
+# -------------------------------------------------------------------
+
+class vtkClampedGlyphSource(vtkCustomGlyphSource):
+    def __init__(self, scale, vtk_glyph_source, range_min, range_max):
+        vtkCustomGlyphSource.__init__(self, scale, vtk_glyph_source)
+        self.range = (range_min, range_max,)
+
+    def get_range(self):
+        return self.range
+
+class vtkForceSource(vtkClampedGlyphSource):
+    def __init__(self, maxnorm, scale=1):
+        vtkClampedGlyphSource.__init__(self, scale, vtkArrowSource(),
+                                       range_min=0.0, range_max=maxnorm)
+
+        self.vtk_property.SetColor(1.0, 0.25, 0.25) # forces are red
+        self.vtk_property.SetInterpolationToPhong()
+        self.vtk_property.SetDiffuse(0.7)
+        self.vtk_property.SetSpecular(0.4)
+        self.vtk_property.SetSpecularPower(20)
+
+        self.vtk_glyph_source.SetShaftResolution(12)
+        self.vtk_glyph_source.SetShaftRadius(0.03*avg_radius) #default 0.03
+        self.vtk_glyph_source.SetTipResolution(20)
+        self.vtk_glyph_source.SetTipLength(0.3*avg_radius) #default 0.35
+        self.vtk_glyph_source.SetTipRadius(0.1*avg_radius) #default 0.1
+
+class vtkVelocitySource(vtkClampedGlyphSource):
+    def __init__(self, maxnorm, scale=1):
+        vtkClampedGlyphSource.__init__(self, scale, vtkConeSource(),
+                                       range_min=0.0, range_max=maxnorm)
+
+        self.vtk_property.SetColor(0.25, 0.25, 1.0) # velocities blue
+        self.vtk_property.SetInterpolationToPhong()
+        self.vtk_property.SetDiffuse(0.9)
+        self.vtk_property.SetSpecular(1.0)
+        self.vtk_property.SetSpecularPower(50)
+
+        self.vtk_glyph_source.SetAngle(6)
+        self.vtk_glyph_source.SetHeight(avg_radius)
+        self.vtk_glyph_source.SetResolution(16)
+        self.vtk_glyph_source.SetCenter(0.05*avg_radius, 0.0, 0.0)
+
+# -------------------------------------------------------------------
+
+class vtkBondSource(vtkCustomGlyphSource):
+    def __init__(self, width, scale=1):
+        vtkCustomGlyphSource.__init__(self, scale, vtkCylinderSource())
+
+        self.width = width
+
+        self.vtk_property.SetColor(0.25, 1.0, 0.25) # bonds are green
+                                                    # (and so are you)
+
+        self.vtk_glyph_source.SetRadius(self.scale*self.width)
+        self.vtk_glyph_source.SetResolution(16)
+
diff --git a/ase/visualize/vtk/volume.py b/ase/visualize/vtk/volume.py
new file mode 100644
index 0000000..1cd19bb
--- /dev/null
+++ b/ase/visualize/vtk/volume.py
@@ -0,0 +1,62 @@
+
+import numpy as np
+
+from vtk import vtkContourFilter, vtkDepthSortPolyData
+from ase.visualize.vtk.grid import vtkVolumeGrid
+from ase.visualize.vtk.module import vtkPolyDataModule
+from ase.visualize.vtk.pipeline import vtkSurfaceSmootherPipeline, \
+                                       vtkDepthSortPipeline
+
+# -------------------------------------------------------------------
+
+class vtkIsoSurfaceModule(vtkVolumeGrid, vtkPolyDataModule):
+    def __init__(self, data, cell, vtk_renderer, contours=1, depthsort=True):
+        #TODO don't require vtk_renderer... implement vtkScene
+        #TODO contour values from list or autocontours if int
+
+        # Make sure data argument is a valid array
+        if not isinstance(data, np.ndarray):
+            data = np.array(data)
+
+        vtkVolumeGrid.__init__(self, data.shape, cell)
+
+        self.vtk_iso = vtkContourFilter() #vtkMarchingContourFilter?
+        self.vtk_iso.SetInput(self.get_structured_points()) #TODO non-orthogonal
+
+        self.vtk_iso.SetValue(0, 0.25) 
+        self.vtk_iso.SetValue(1, -0.25)
+
+        self.smoothpipe = vtkSurfaceSmootherPipeline(self, vtk_iso)
+
+        #TODO use vtkDepthSortPipeline - but vtkPolyDataModule isn't a pipeline
+
+        self.depthsort = depthsort
+
+        if self.depthsort:
+            # The depht sort object is set up to generate scalars representing
+            # the sort depth. A camera is assigned for the sorting. The camera
+            # defines the sort vector (position and focal point).
+            self.vtk_ds = vtkDepthSortPolyData()
+            self.vtk_ds.SetCamera(vtk_renderer.GetActiveCamera())
+            self.vtk_ds.SetInputConnection(self.vtk_iso.GetOutputPort())
+            self.vtk_ds.SetDirectionToBackToFront()
+            #vtk_ds.SetVector(1, 1, 1)
+            #vtk_ds.SortScalarsOn()
+            #vtk_ds.Update()
+
+            vtk_renderer.ResetCamera()
+
+            vtkPolyDataModule.__init__(self, self.vtk_ds)
+        else:
+            vtkPolyDataModule.__init__(self, self.vtk_iso)
+
+        #TODO add color function
+        """
+        vtk_dmap = vtk.vtkPolyDataMapper()
+        vtk_dmap.ScalarVisibilityOn()
+        vtk_dmap.SetScalarRange(0, vtk_sda_x.GetMaxNorm()) #TODO GetMinNorm non-existing!
+        vtk_dmap.SetScalarModeToUsePointFieldData()
+        vtk_dmap.SelectColorArray("x")
+        #vtk_dmap.ColorByArrayComponent("x", 0)
+        """
+
diff --git a/ase/xrdebye.py b/ase/xrdebye.py
new file mode 100644
index 0000000..bb6c6c9
--- /dev/null
+++ b/ase/xrdebye.py
@@ -0,0 +1,101 @@
+from math import exp, pi, sin, sqrt, cos, acos
+import numpy as np
+
+from ase.data import atomic_numbers
+
+# Table (1) of
+# D. WAASMAIER AND A. KIRFEL, Acta Cryst. (1995). A51, 416-431
+waasmaier = {
+    #      a1        b1         a2        b2        a3        b3          a4         b4         a5         b5        c
+    'C' : [2.657506, 14.780758, 1.078079, 0.776775, 1.490909, 42.086843,  -4.241070, -0.000294, 0.713791, 0.239535, 4.297983],
+    'S' : [6.372157, 1.514347, 5.154568, 22.092528, 1.473732, 0.061373,   1.635073,  55.445176, 1.209372, 0.646925, 0.154722],
+    'Pd': [6.121511, 0.062549,  4.784063, 0.784031, 16.631683, 8.751391,  4.318258, 34.489983, 13.246773, 0.784031, 0.883099],
+    'Ag': [6.073874, 0.055333, 17.155437, 7.896512, 4.173344, 28.443739,  0.852238, 110.376108, 17.988685, 0.716809, 0.756603],
+    'Au': [16.777389, 0.122737, 19.317156, 8.621570, 32.979682, 1.256902, 5.595453, 38.008821, 10.576854, 0.000601, -6.279078],
+    'P' : [1.950541, 0.908139, 4.146930, 27.044953, 1.494560, 0.071280, 1.522042, 67.520190, 5.729711, 1.981173, 0.155233],
+    'Cl': [1.446071, 0.052357, 6.870609, 1.193165, 6.151801, 18.343416, 1.750347, 46.398394, 0.634168, 0.401005, 0.146773],
+}
+
+class XrDebye:
+    def __init__(self, wavelength, alpha=1.01, damping=0.04, warn=True,
+                 method='Iwasa'):
+        """
+        Obtain powder x-ray spectra.
+
+        wavelength in Angstrom
+        damping in Angstrom**2
+        """
+        self.wavelength = wavelength
+        self.damping = damping
+        self.alpha = alpha
+        self.warn = warn
+        self.method = method
+
+    def set_damping(self, damping):
+        self.damping = damping
+
+    def get(self, atoms, s):
+        """Get the powder x-ray (XRD) pattern using the Debye-Formula.
+
+        After: T. Iwasa and K. Nobusada, J. Phys. Chem. C 111 (2007) 45
+               s is assumed to be in 1/Angstrom
+        """
+
+        sinth = self.wavelength * s / 2.
+        costh = sqrt(1. - sinth**2)
+        cos2th = cos(2. * acos(costh))
+        pre = exp(- self.damping * s**2 / 2)
+ 
+        if self.method == 'Iwasa':
+            pre *= costh / (1. + self.alpha * cos2th**2)
+
+        f = {}
+        def atomic(symbol):
+            if not f.has_key(symbol):
+                if self.method == 'Iwasa':
+                    f[symbol] = self.get_waasmaier(symbol, s)
+                else:
+                    f[symbol] = atomic_numbers[symbol]
+            return f[symbol]
+
+        def sinc(x):
+            if x < 1.e-6:
+                x2 = x * x
+                return 1 - x2 / 6. + x2 * x2 / 120.
+            else:
+                return sin(x) / x
+
+        I = 0.
+        for a in atoms:
+            fa = atomic(a.symbol)
+#            print a.symbol, fa
+            for b in atoms:
+                fb = atomic(b.symbol)
+
+                if a == b:
+                    twopis = 0.
+                else:
+                    vrij = a.position - b.position
+                    rij = np.sqrt(np.dot(vrij, vrij))
+                    twopisr = 2 * pi * s * rij
+
+                I += fa * fb * sinc(twopisr)
+                    
+        return pre * I
+
+    def get_waasmaier(self, symbol, s):
+        """Scattering factor for free atoms."""
+        if symbol == 'H':
+            # XXXX implement analytical H
+            return 0
+        elif waasmaier.has_key(symbol):
+            abc = waasmaier[symbol]
+            f = abc[10]
+            s2 = s*s
+            for i in range(5):
+                f += abc[2 * i] * exp(-abc[2 * i + 1] * s2)
+            return f
+        if self.warn:
+            print '<xrdebye::get_atomic> Element', symbol, 'not available'
+        return 0
+        
diff --git a/doc/ASE.bib b/doc/ASE.bib
new file mode 100644
index 0000000..b342e85
--- /dev/null
+++ b/doc/ASE.bib
@@ -0,0 +1,30 @@
+ at Article{ISI:000175131400009,
+Author = {S. R. Bahn and K. W. Jacobsen},
+Title = {An object-oriented scripting interface to a legacy electronic structure code},
+JournalFull = {COMPUTING IN SCIENCE \& ENGINEERING},
+Year = {2002},
+Volume = {4},
+Number = {3},
+Pages = {56-66},
+Month = {MAY-JUN},
+Abstract = {The authors have created an object-oriented scripting interface to a mature density functional theory
+code. The interface gives users a high-level, flexible handle on the code without rewriting the
+underlying number-crunching code. The authors also discuss the design issues and advantages of
+homogeneous interfaces},
+Publisher = {IEEE COMPUTER SOC},
+Address = {10662 LOS VAQUEROS CIRCLE, PO BOX 3014, LOS ALAMITOS, CA 90720-1314 USA},
+Type = {Article},
+Language = {English},
+Affiliation = {Bahn, SR (Reprint Author), Tech Univ Denmark, Dept Phys, CAMP, Bldg 307, DK-2800 Lyngby, Denmark.
+Tech Univ Denmark, Dept Phys, CAMP, DK-2800 Lyngby, Denmark.},
+ISSN = {1521-9615},
+Keywords-Plus = {MULTISCALE SIMULATION; GOLD ATOMS},
+Subject-Category = {Computer Science, Interdisciplinary Applications},
+Author-Email = {bahn at fysik.dtu.dk kwj at fysik.dtu.dk},
+Number-of-Cited-References = {19},
+Journal-ISO = {Comput. Sci. Eng.},
+Journal = {Comput. Sci. Eng.},
+Doc-Delivery-Number = {543YL},
+Unique-ID = {ISI:000175131400009},
+DOI = {10.1109/5992.998641},
+}
diff --git a/doc/_static/044482-glossy-black-icon-sports-hobbies-film-clapper.png b/doc/_static/044482-glossy-black-icon-sports-hobbies-film-clapper.png
new file mode 100644
index 0000000..b776398
Binary files /dev/null and b/doc/_static/044482-glossy-black-icon-sports-hobbies-film-clapper.png differ
diff --git a/doc/_static/China.png b/doc/_static/China.png
new file mode 100644
index 0000000..de61348
Binary files /dev/null and b/doc/_static/China.png differ
diff --git a/doc/_static/United_States_of_America.png b/doc/_static/United_States_of_America.png
new file mode 100644
index 0000000..c9ff90c
Binary files /dev/null and b/doc/_static/United_States_of_America.png differ
diff --git a/doc/_static/abinit.png b/doc/_static/abinit.png
new file mode 100644
index 0000000..ca1bca6
Binary files /dev/null and b/doc/_static/abinit.png differ
diff --git a/doc/_static/asap.png b/doc/_static/asap.png
new file mode 100644
index 0000000..36a902d
Binary files /dev/null and b/doc/_static/asap.png differ
diff --git a/doc/_static/ase.css b/doc/_static/ase.css
new file mode 100644
index 0000000..aedc6ad
--- /dev/null
+++ b/doc/_static/ase.css
@@ -0,0 +1,5 @@
+ at import "default.css";
+
+img {border: none;}
+img.center{display:block; margin-left:auto; margin-right:auto; text-align: center;}
+
diff --git a/doc/_static/ase.ico b/doc/_static/ase.ico
new file mode 100644
index 0000000..68b68cd
Binary files /dev/null and b/doc/_static/ase.ico differ
diff --git a/doc/_static/ase_trac.png b/doc/_static/ase_trac.png
new file mode 100644
index 0000000..990f42c
Binary files /dev/null and b/doc/_static/ase_trac.png differ
diff --git a/doc/_static/castep.png b/doc/_static/castep.png
new file mode 100644
index 0000000..fb2f88d
Binary files /dev/null and b/doc/_static/castep.png differ
diff --git a/doc/_static/dftb.png b/doc/_static/dftb.png
new file mode 100644
index 0000000..d2cefec
Binary files /dev/null and b/doc/_static/dftb.png differ
diff --git a/doc/_static/elk.png b/doc/_static/elk.png
new file mode 100644
index 0000000..16e9e7c
Binary files /dev/null and b/doc/_static/elk.png differ
diff --git a/doc/_static/emt.png b/doc/_static/emt.png
new file mode 100644
index 0000000..041f55d
Binary files /dev/null and b/doc/_static/emt.png differ
diff --git a/doc/_static/exciting.png b/doc/_static/exciting.png
new file mode 100644
index 0000000..002d766
Binary files /dev/null and b/doc/_static/exciting.png differ
diff --git a/doc/_static/fhi-aims.png b/doc/_static/fhi-aims.png
new file mode 100644
index 0000000..fee1501
Binary files /dev/null and b/doc/_static/fhi-aims.png differ
diff --git a/doc/_static/fleur.png b/doc/_static/fleur.png
new file mode 100644
index 0000000..30f57fb
Binary files /dev/null and b/doc/_static/fleur.png differ
diff --git a/doc/_static/gpaw.png b/doc/_static/gpaw.png
new file mode 100644
index 0000000..16fe63c
Binary files /dev/null and b/doc/_static/gpaw.png differ
diff --git a/doc/_static/hotbit.png b/doc/_static/hotbit.png
new file mode 100644
index 0000000..0f32885
Binary files /dev/null and b/doc/_static/hotbit.png differ
diff --git a/doc/_static/jacapo.png b/doc/_static/jacapo.png
new file mode 100644
index 0000000..c9ceb24
Binary files /dev/null and b/doc/_static/jacapo.png differ
diff --git a/doc/_static/lammps.png b/doc/_static/lammps.png
new file mode 100644
index 0000000..1eac0ef
Binary files /dev/null and b/doc/_static/lammps.png differ
diff --git a/doc/_static/siesta.png b/doc/_static/siesta.png
new file mode 100644
index 0000000..5c9c789
Binary files /dev/null and b/doc/_static/siesta.png differ
diff --git a/doc/_static/tm_logo_l.png b/doc/_static/tm_logo_l.png
new file mode 100644
index 0000000..cbe8af5
Binary files /dev/null and b/doc/_static/tm_logo_l.png differ
diff --git a/doc/_static/vasp.png b/doc/_static/vasp.png
new file mode 100644
index 0000000..69e6424
Binary files /dev/null and b/doc/_static/vasp.png differ
diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html
new file mode 100644
index 0000000..a28d9f8
--- /dev/null
+++ b/doc/_templates/layout.html
@@ -0,0 +1,234 @@
+{%- block doctype -%}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+{%- endblock %}
+{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %}
+{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
+{%- macro relbar() %}
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+
+
+
+        {%- for rellink in rellinks[:2] %}
+
+
+
+        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
+          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
+             accesskey="{{ rellink[2] }}">{{ rellink[3] }}</a>
+          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
+        {%- endfor %}
+
+
+       <li class="right"><a href="{{ pathto('faq') }}" title="Frequently Asked Questions">FAQ</a> |</li>
+       <li class="right"><a href="{{ pathto('ase/ase') }}" title="Documentation">documentation</a> |</li>
+        <li class="right"><a href="{{ pathto('tutorials/tutorials') }}" title="Tutorials">tutorials</a> |</li>
+        <li class="right"><a href="{{ pathto('download') }}" title="Download and installation">download</a> |</li>
+
+        {%- block rootrellink %}
+        <a href="{{ pathto('index') }}"><img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo" align="absmiddle"/></a>
+        {%- endblock %}
+
+
+        {%- for parent in parents %}
+          <li><a href="{{ parent.link|e }}" accesskey="U">{{ parent.title }}</a>{{ reldelim1 }}</li>
+        {%- endfor %}
+        {%- block relbaritems %}{% endblock %}
+      </ul>
+    </div>
+{%- endmacro %}
+{%- macro sidebar() %}
+      {%- if builder != 'htmlhelp' %}
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+
+          <h3>ASE</h3>
+          <ul class="this-page-menu">
+            <li><a href="{{ pathto('overview') }}">
+	      Overview</a></li>
+            <li><a href="{{ pathto('download') }}">
+              Download</a></li>
+            <li><a href="{{ pathto('tutorials/tutorials') }}">
+	      Tutorials</em></a></li>
+            <li><a href="{{ pathto('ase/ase') }}">
+              Documentation</a></li>
+            <li><a href="{{ pathto('faq') }}">FAQ</a></li>
+            <li><a href="{{ pathto('glossary') }}">Glossary</a></li>
+            <li><a href="{{ pathto('mailinglists') }}">Mailing lists</a></li>
+            <li><a href="{{ pathto('tutorials/tutorials') }}#videos">
+		Video tutorials</a></li>
+            <li><a><p></a></li>
+            <li><a href="{{ pathto('ase-manual')[:-4] + 'pdf' }}">
+              Printable manual (pdf)</a></li>
+           </ul>
+           <h3>Development</h3>
+           <ul class="this-page-menu">
+            <li><a href="{{ pathto('development/development') }}">
+              Development</a></li>
+            <li><a href="http://wiki.fysik.dtu.dk/ase/epydoc/ase-module.html">
+              Epydoc</a></li>
+	    <li><a href="http://trac.fysik.dtu.dk/projects/ase/browser/trunk/">
+              Source code (svn)</a></li>
+            <li><a href="{{ pathto('bugs') }}">Bugs!</a></li>
+	    <li><a href="http://trac.fysik.dtu.dk/projects/ase/report/1">
+              Bug Tracker</a></li>
+          </ul>
+          
+          {%- block sidebartoc %}
+          {%- if display_toc and current_page_name not in ['index', 'contents'] %}
+            <h3>This Page</h3>
+            {{ toc }}
+          {%- endif %}
+
+          {%- endblock %}
+
+
+          {%- block sidebarrel %}
+          {%- if prev %}
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="{{ prev.link|e }}" title="previous chapter">{{ prev.title }}</a></p>
+          {%- endif %}
+          {%- if next %}
+            <h4>Next topic</h4>
+            <p class="topless"><a href="{{ next.link|e }}" title="next chapter">{{ next.title }}</a></p>
+          {%- endif %}
+          {%- endblock %}
+          {%- if sourcename %}
+            <ul class="this-page-menu">
+            {%- if builder == 'web' %}
+              <li><a href="#comments">Comments ({{ comments|length }} so far)</a></li>
+              <li><a href="{{ pathto('@edit/' + sourcename)|e }}">Suggest Change</a></li>
+              <li><a href="{{ pathto('@source/' + sourcename)|e }}">Show Source</a></li>
+            {%- elif builder == 'html' %}
+              <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">Show Source</a></li>
+            {%- endif %}
+            </ul>
+          {%- endif %}
+          {%- if customsidebar %}
+          {{ rendertemplate(customsidebar) }}
+          {%- endif %}
+          {%- block sidebarsearch %}
+          {%- if pagename != "search" %}
+            <h3>{{ builder == 'web' and 'Keyword' or 'Quick' }} search</h3>
+            <form class="search" action="{{ pathto('search') }}" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+            {%- if builder == 'web' %}
+            <p style="font-size: 90%">Enter a module, class or function name.</p>
+            {%- endif %}
+          {%- endif %}
+          {%- endblock %}
+        </div>
+      </div>
+      {%- endif %}
+{%- endmacro -%}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    {%- if builder != 'htmlhelp' %}
+      {%- set titlesuffix = " — " + docstitle %}
+    {%- endif %}
+    <title>{{ title|striptags }}{{ titlesuffix }}</title>
+    {%- if builder == 'web' %}
+    <link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{%
+      if in_admin_panel %}&admin=yes{% endif %}" type="text/css" />
+    {%- for link, type, title in page_links %}
+    <link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}" />
+    {%- endfor %}
+    {%- else %}
+    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
+    {%- endif %}
+    {%- if builder != 'htmlhelp' %}
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '{{ pathto("", 1) }}',
+          VERSION:     '{{ release }}',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: '{{ file_suffix }}'
+      };
+    </script>
+    {%- for scriptfile in script_files %}
+    <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
+    {%- endfor %}
+    {%- if use_opensearch %}
+    <link rel="search" type="application/opensearchdescription+xml"
+          title="Search within {{ docstitle }}"
+          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
+    {%- endif %}
+    {%- if favicon %}
+    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
+    {%- endif %}
+    {%- endif %}
+{%- block rellinks %}
+    {%- if hasdoc('about') %}
+    <link rel="author" title="About these documents" href="{{ pathto('about') }}" />
+    {%- endif %}
+    <link rel="contents" title="Global table of contents" href="{{ pathto('contents') }}" />
+    <link rel="index" title="Global index" href="{{ pathto('genindex') }}" />
+    <link rel="search" title="Search" href="{{ pathto('search') }}" />
+    {%- if hasdoc('copyright') %}
+    <link rel="copyright" title="Copyright" href="{{ pathto('copyright') }}" />
+    {%- endif %}
+    <link rel="top" title="{{ docstitle }}" href="{{ pathto('index') }}" />
+    {%- if parents %}
+    <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}" />
+    {%- endif %}
+    {%- if next %}
+    <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}" />
+    {%- endif %}
+    {%- if prev %}
+    <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}" />
+    {%- endif %}
+{%- endblock %}
+{%- block extrahead %}{% endblock %}
+  </head>
+  <body>
+
+{%- block relbar1 %}{{ relbar() }}{% endblock %}
+
+{%- block sidebar1 %}{# possible location for sidebar #}{% endblock %}
+
+{%- block document %}
+    <div class="document">
+      <div class="documentwrapper">
+      {%- if builder != 'htmlhelp' %}
+        <div class="bodywrapper">
+      {%- endif %}
+          <div class="body">
+            {% block body %}{% endblock %}
+          </div>
+      {%- if builder != 'htmlhelp' %}
+        </div>
+      {%- endif %}
+      </div>
+{%- endblock %}
+
+{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
+      <div class="clearer"></div>
+    </div>
+
+{%- block relbar2 %}{{ relbar() }}{% endblock %}
+
+{%- block footer %}
+    <div class="footer">
+    {%- if hasdoc('copyright') %}
+      © <a href="{{ pathto('copyright') }}">Copyright</a> {{ copyright }}.
+    {%- else %}
+      © Copyright {{ copyright }}.
+    {%- endif %}
+    {%- if last_updated %}
+      Last updated on {{ last_updated }}.
+    {%- endif %}
+    {%- if show_sphinx %}
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.
+    {%- endif %}
+    </div>
+{%- endblock %}
+  </body>
+</html>
diff --git a/doc/ase/ase.rst b/doc/ase/ase.rst
new file mode 100644
index 0000000..89df422
--- /dev/null
+++ b/doc/ase/ase.rst
@@ -0,0 +1,77 @@
+.. module:: ase
+   :synopsis: ASE module
+
+.. _ase:
+
+================================
+Documentation for modules in ASE
+================================
+
+Quick links:
+
+.. list-table::
+
+  * - :mod:`~ase.atom`
+    - :mod:`atoms`
+    - :mod:`calculators`
+    - :mod:`constraints`
+    - :mod:`dft`
+  * - :mod:`data`
+    - :mod:`gui`
+    - :mod:`infrared`
+    - :mod:`io`
+    - :mod:`lattice`
+  * - :mod:`md`
+    - :mod:`neb`
+    - :mod:`optimize`
+    - :mod:`parallel`
+    - :mod:`phonons`
+  * - :mod:`~lattice.spacegroup`
+    - :mod:`structure`
+    - :mod:`~ase.lattice.surface`
+    - :mod:`transport`
+    - :mod:`thermochemistry`
+  * - :mod:`units`
+    - :mod:`utils`
+    - :mod:`vibrations`
+    - :mod:`visualize`
+    - :mod:`vtk`
+
+
+.. seealso::
+
+   * :ref:`tutorials`
+   * :epydoc:`Automatically generated documentation <ase>` (:term:`API`)
+   * :trac:`Source code <>`
+
+
+
+List of all modules:
+
+.. toctree::
+   :maxdepth: 2
+
+   atoms
+   atom
+   units
+   io
+   gui/gui
+   cmdline
+   setup-overview
+   optimize
+   parallel
+   visualize
+   visualize/vtk
+   calculators/calculators
+   constraints
+   neb
+   vibrations
+   phonons
+   infrared
+   md
+   dft/dft
+   transport/transport
+   data
+   trajectory
+   utils
+   thermochemistry
diff --git a/doc/ase/atom.rst b/doc/ase/atom.rst
new file mode 100644
index 0000000..3126c63
--- /dev/null
+++ b/doc/ase/atom.rst
@@ -0,0 +1,120 @@
+.. module:: ase.atom
+
+The Atom object
+===============
+
+ASE defines a python class called :class:`Atom` to setup and handle atoms
+in electronic structure and molecular simulations. From a python
+script, atoms can be created like this:
+
+>>> from ase import Atom
+>>> a1 = Atom('Si', (0, 0, 0))
+>>> a2 = Atom('H', (1.3, 0, 0), mass=2)
+>>> a3 = Atom(position=(0, 0, 0), Z=14)  # same is a1
+
+.. autoclass:: Atom
+
+The first argument to the constructor of an :class:`Atom` object is
+the chemical symbol, and the second argument is the position in Å
+units (see :mod:`units`).  The position can be any numerical sequence
+of length three.  The properties of an atom can also be set using
+keywords like it is done in the *a2* and *a3* examples above.
+
+More examples:
+
+>>> a = Atom('O', charge=-2)
+>>> b = Atom(8, charge=-2)
+>>> c = Atom('H', (1, 2, 3), magmom=1)
+>>> print a.charge, a.position
+-2 [ 0. 0. 0.]
+>>> c.x = 0.0
+>>> c.position
+array([ 0.,  2.,  3.])
+>>> b.symbol
+'O'
+>>> c.tag = 42
+>>> c.number
+1
+>>> c.symbol = 'Li'
+>>> c.number
+3
+
+If the atom object belongs to an Atoms object, then assigning
+values to the atom attributes will change the corresponding
+arrays of the atoms object:
+
+>>> OH = Atoms('OH')
+>>> OH[0].charge = -1
+>>> OH.get_charges()
+array([-1.,  0.])
+
+Another example:
+
+>>> for atom in bulk:
+...     if atom.symbol = 'Ni':
+...         atom.magmom = 0.7  # set initial magnetic moment
+    
+
+The different properties of an atom can be obtained and changed via
+attributes (``position``, ``number``, ``tag``, ``momentum``, ``mass``,
+``magmom``, ``charge``, ``x``, ``y``, ``z``):
+
+>>> a1.position = [1, 0, 0]
+>>> a1.position
+array([ 1.,  0.,  0.])
+>>> a1.z = 2.5
+>>> a1.position
+array([ 1. ,  0. ,  2.5])
+>>> a2.magmom = 1.0
+
+That last line will set the initial magnetic moment that some
+calulators use (similar to the
+:meth:`~ase.atoms.Atoms.set_initial_magnetic_moments` method).
+
+
+.. note::
+
+   The ``position`` and ``momentum`` attributes refer to mutable
+   objects, so in some cases, you may want to use
+   ``a1.position.copy()`` in order to avoid changing the position of
+   ``a1`` by accident.
+
+
+
+Getting an Atom from an Atoms object
+------------------------------------
+
+Indexing an :class:`Atoms` object returns an :class:`Atom` object
+still remembering that it belongs to the collective :class:`Atoms`:
+Modifying it will also change the atoms object:
+
+>>> atoms = ase.data.molecules.molecule('CH4')
+>>> atoms.get_positions()
+array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.629118,  0.629118,  0.629118],
+       [-0.629118, -0.629118,  0.629118],
+       [ 0.629118, -0.629118, -0.629118],
+       [-0.629118,  0.629118, -0.629118]])
+>>> a = atoms[2]
+>>> a
+Atom('H', [-0.62911799999999996, -0.62911799999999996, 0.62911799999999996], index=2)
+>>> a.x = 0
+>>> atoms.get_positions()
+array([[ 0.      ,  0.      ,  0.      ],
+       [ 0.629118,  0.629118,  0.629118],
+       [ 0.      , -0.629118,  0.629118],
+       [ 0.629118, -0.629118, -0.629118],
+       [-0.629118,  0.629118, -0.629118]])
+                                                   
+
+.. seealso::
+
+   :epydoc:`atom.Atom`:
+     All the details!
+
+   :mod:`atoms`:
+     More information about how to use collections of atoms.
+
+   :mod:`calculators`:
+     Information about how to calculate forces and energies of atoms.
+
diff --git a/doc/ase/atoms.py b/doc/ase/atoms.py
new file mode 100644
index 0000000..9ec31b4
--- /dev/null
+++ b/doc/ase/atoms.py
@@ -0,0 +1,19 @@
+# creates: Au-wire.png
+
+from ase import Atoms
+from ase.io import write
+
+d = 2.9
+L = 10.0
+wire = Atoms('Au',
+             positions=[(0, L / 2, L / 2)],
+             cell=(d, L, L),
+             pbc=(1, 0, 0))
+wire *= (6, 1, 1)
+wire.positions[:, 0] -= 2 * d
+wire.cell[0, 0] = d
+#view(wire, block=1)
+write('Au-wire.pov', wire,
+      show_unit_cell=2,
+      rotation='12x,6y',
+      transparent=False, display=False, run_povray=True)
diff --git a/doc/ase/atoms.rst b/doc/ase/atoms.rst
new file mode 100644
index 0000000..fe2dbe0
--- /dev/null
+++ b/doc/ase/atoms.rst
@@ -0,0 +1,333 @@
+.. module:: atoms
+
+================
+The Atoms object
+================
+
+The :class:`~ase.atoms.Atoms` object is a collection of atoms.  Here
+is how to define a CO molecule::
+
+  from ase import *
+  d = 1.1
+  co = Atoms('CO', positions=[(0, 0, 0), (0, 0, d)])
+
+Here, the first argument specifies the type of the atoms and we used
+the ``positions`` keywords to specify their positions.  Other
+possible keywords are: ``numbers``, ``tags``, ``momenta``, ``masses``,
+``magmoms`` and ``charges``.
+
+Here is how you could make an infinite gold wire with a bond length of
+2.9 Å::
+
+  from ase import *
+  d = 2.9
+  L = 10.0
+  wire = Atoms('Au',
+               positions=[(0, L / 2, L / 2)],
+               cell=(d, L, L),
+               pbc=(1, 0, 0))
+
+.. image:: Au-wire.png
+
+Here, two more optional keyword arguments were used:
+
+``cell``: Unit cell size
+  This can be a sequence of three numbers for
+  an orthorhombic unit cell or three by three numbers for a general
+  unit cell (a sequence of three sequences of three numbers).  The
+  default value is *[1.0, 1.0, 1.0]*.
+
+``pbc``: Periodic boundary conditions
+  The default value is *False* - a value of *True* would give
+  periodic boundary conditions along all three axes.  It is possible
+  to give a sequence of three booleans to specify periodicity along
+  specific axes.
+
+You can also use the following methods to work with the unit cell and
+the boundary conditions: :meth:`~ase.atoms.Atoms.set_pbc`,
+:meth:`~ase.atoms.Atoms.set_cell`, :meth:`~ase.atoms.Atoms.get_cell`,
+and :meth:`~ase.atoms.Atoms.get_pbc`.
+
+
+Working with the array methods of Atoms objects
+===============================================
+
+Like with a single :class:`~atom.Atom` the properties of a collection of atoms
+can be accessed and changed with get- and set-methods. For example
+the positions of the atoms can be addressed as
+
+>>> a = Atoms('N3', [(0, 0, 0), (1, 0, 0), (0, 0, 1)])
+>>> a.get_positions()
+array([[ 0.,  0.,  0.],
+       [ 1.,  0.,  0.],
+       [ 0.,  0.,  1.]])
+>>> a.set_positions([(2, 0, 0), (0, 2, 2), (2, 2, 0)])
+>>> a.get_positions()
+array([[ 2.,  0.,  0.],
+       [ 0.,  2.,  2.],
+       [ 2.,  2.,  0.]])
+
+Here is the full list of the get/set methods operating on all the
+atoms at once.  The get methods return an array of quantities, one for
+each atom; the set methods take similar arrays.
+E.g. :meth:`~ase.atoms.Atoms.get_positions` return N * 3 numbers,
+:meth:`~ase.atoms.Atoms.get_atomic_numbers` return N integers.
+
+*These methods return copies of the internal arrays, it is thus safe
+to modify the returned arrays.*
+
+.. list-table::
+
+  * - :meth:`~ase.atoms.Atoms.get_atomic_numbers`
+    - :meth:`~ase.atoms.Atoms.set_atomic_numbers`
+  * - :meth:`~ase.atoms.Atoms.get_charges`
+    - :meth:`~ase.atoms.Atoms.set_charges`
+  * - :meth:`~ase.atoms.Atoms.get_chemical_symbols`
+    - :meth:`~ase.atoms.Atoms.set_chemical_symbols`
+  * - :meth:`~ase.atoms.Atoms.get_initial_magnetic_moments`
+    - :meth:`~ase.atoms.Atoms.set_initial_magnetic_moments`
+  * - :meth:`~ase.atoms.Atoms.get_magnetic_moments`
+    -
+  * - :meth:`~ase.atoms.Atoms.get_masses`
+    - :meth:`~ase.atoms.Atoms.set_masses`
+  * - :meth:`~ase.atoms.Atoms.get_momenta`
+    - :meth:`~ase.atoms.Atoms.set_momenta`
+  * - :meth:`~ase.atoms.Atoms.get_forces`
+    -
+  * - :meth:`~ase.atoms.Atoms.get_positions`
+    - :meth:`~ase.atoms.Atoms.set_positions`
+  * - :meth:`~ase.atoms.Atoms.get_potential_energies`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_scaled_positions`
+    - :meth:`~ase.atoms.Atoms.set_scaled_positions`
+  * - :meth:`~ase.atoms.Atoms.get_stresses`
+    -
+  * - :meth:`~ase.atoms.Atoms.get_tags`
+    - :meth:`~ase.atoms.Atoms.set_tags`
+  * - :meth:`~ase.atoms.Atoms.get_velocities`
+    - :meth:`~ase.atoms.Atoms.set_velocities`
+
+There are also a number of get/set methods that operate on quantities
+common to all the atoms or defined for the collection of atoms:
+
+.. list-table::
+
+  * - :meth:`~ase.atoms.Atoms.get_calculator`
+    - :meth:`~ase.atoms.Atoms.set_calculator`
+  * - :meth:`~ase.atoms.Atoms.get_cell`
+    - :meth:`~ase.atoms.Atoms.set_cell`
+  * - :meth:`~ase.atoms.Atoms.get_center_of_mass`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_kinetic_energy`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_magnetic_moment`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_name`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_number_of_atoms`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_pbc`
+    - :meth:`~ase.atoms.Atoms.set_pbc`
+  * - :meth:`~ase.atoms.Atoms.get_potential_energy`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_stress`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_total_energy`
+    - 
+  * - :meth:`~ase.atoms.Atoms.get_volume`
+    - 
+
+
+Unit cell and boundary conditions
+=================================
+
+The :class:`~ase.atoms.Atoms` object holds a unit cell which by
+default is the 3x3 unit matrix as can be seen from
+
+>>> a.get_cell()
+array([[ 1.,  0.,  0.],
+       [ 0.,  1.,  0.],
+       [ 0.,  0.,  1.]])
+
+
+The cell can be defined or changed using the
+:meth:`~ase.atoms.Atoms.set_cell` method. Changing the unit cell
+does per default not move the atoms:
+
+>>> a.set_cell(2 * identity(3))
+>>> a.get_cell()
+array([[ 2.,  0.,  0.],
+       [ 0.,  2.,  0.],
+       [ 0.,  0.,  2.]])
+>>> a.get_positions()
+array([[ 2.,  0.,  0.],
+       [ 1.,  1.,  0.],
+       [ 2.,  2.,  0.]])
+
+However if we set ``scale_atoms=True`` the atomic positions are scaled with
+the unit cell:
+
+>>> a.set_cell(identity(3), scale_atoms=True)
+>>> a.get_positions()
+array([[ 1. ,  0. ,  0. ],
+       [ 0.5,  0.5,  0. ],
+       [ 1. ,  1. ,  0. ]])
+
+The :meth:`~ase.atoms.Atoms.set_pbc` method specifies whether
+periodic boundary conditions are to be used in the directions of the
+three vectors of the unit cell.  A slab calculation with periodic
+boundary conditions in *x* and *y* directions and free boundary
+conditions in the *z* direction is obtained through
+
+>>> a.set_pbc((True, True, False))
+
+
+Special attributes
+==================
+
+It is also possible to work directly with the attributes
+:attr:`~ase.atoms.Atoms.positions`, :attr:`~ase.atoms.Atoms.numbers`,
+:attr:`~ase.atoms.Atoms.pbc` and :attr:`~ase.atoms.Atoms.cell`.  Here
+we change the position of the 2nd atom (which has count number 1
+because Python starts counting at zero) and the type of the first
+atom:
+
+>>> a.positions[1] = (1, 1, 0)
+>>> a.get_positions()
+array([[2., 0., 0.],
+      [1., 1., 0.],
+      [2., 2., 0.]])
+>>> a.positions
+array([[2., 0., 0.],
+       [1., 1., 0.],
+       [2., 2., 0.]])
+>>> a.numbers
+array([7, 7, 7])
+>>> a.numbers[0] = 13
+>>> a.get_chemical_symbols()
+['Al', 'N', 'N']
+
+Check for periodic boundary conditions:
+
+>>> a.pbc  # equivalent to a.get_pbc()
+array([False, False, False], dtype=bool)
+>>> a.pbc.any()
+False
+>>> a.pbc[2] = 1
+>>> a.pbc
+array([False, False,  True], dtype=bool)
+
+
+Adding a calculator
+===================
+
+A calculator can be attached to the atoms with the purpose
+of calculating energies and forces on the atoms. ASE works with many
+different :mod:`calculators`.
+
+A calculator object *calc* is attached to the atoms like this:
+
+>>> a.set_calculator(calc)
+
+After the calculator has been appropriately setup the energy of the
+atoms can be obtained through
+
+>>> a.get_potential_energy()
+
+The term "potential energy" here means for example the total energy of
+a DFT calculation, which includes both kinetic, electrostatic, and
+exchange-correlation energy for the electrons. The reason it is called
+potential energy is that the atoms might also have a kinetic energy
+(from the moving nuclei) and that is obtained with
+
+>>> a.get_kinetic_energy()
+
+In case of a DFT calculator, it is up to the user to check exactly what
+the :meth:`~ase.atoms.Atoms.get_potential_energy` method returns. For
+example it may be the result of a calculation with a finite
+temperature smearing of the occupation numbers extrapolated to zero
+temperature.  More about this can be found for the different
+:mod:`calculators`.
+
+The following methods can only be called if a calculator is present:
+
+* :meth:`~ase.atoms.Atoms.get_potential_energy`
+* :meth:`~ase.atoms.Atoms.get_potential_energies`
+* :meth:`~ase.atoms.Atoms.get_forces`
+* :meth:`~ase.atoms.Atoms.get_stress`
+* :meth:`~ase.atoms.Atoms.get_stresses`
+* :meth:`~ase.atoms.Atoms.get_total_energy`
+* :meth:`~ase.atoms.Atoms.get_magnetic_moments`
+* :meth:`~ase.atoms.Atoms.get_magnetic_moment`
+
+Not all of these methods are supported by all calculators.
+
+
+List-methods
+============
+
+.. list-table::
+
+  * - method
+    - example
+  * - ``+``
+    - ``wire2 = wire + co``
+  * - ``+=``, :meth:`~ase.atoms.Atoms.extend`
+    - ``wire += co``
+
+      ``wire.extend(co)``
+  * - :meth:`~ase.atoms.Atoms.append`
+    - ``wire.append(Atom('H'))``
+  * - ``*``
+    - ``wire3 = wire * (3, 1, 1)``
+  * - ``*=``, :meth:`~ase.atoms.Atoms.repeat`
+    - ``wire *= (3, 1, 1)``
+
+      ``wire.repeat((3, 1, 1))``
+  * - ``len``
+    - ``len(co)``
+  * - ``del``
+    - ``del wire3[0]``
+
+      ``del wire3[[1,3]]``
+  * - :meth:`~ase.atoms.Atoms.pop`
+    - ``oxygen = wire2.pop()``
+
+
+Note that the ``del`` method can be used with the more powerful numpy-style indexing, as in the second example above. This can be combined with python list comprehension in order to selectively delete atoms within an ASE Atoms object. For example, the below code creates an ethanol molecule and subsequently strips all the hydrogen atoms from it::
+
+  from ase.data.molecules import molecule
+  atoms = molecule('CH3CH2OH')
+  del atoms[[atom.index for atom in atoms if atom.symbol=='H']]
+
+
+Other methods
+=============
+
+* :meth:`~ase.atoms.Atoms.center`
+* :meth:`~ase.atoms.Atoms.translate`
+* :meth:`~ase.atoms.Atoms.rotate`
+* :meth:`~ase.atoms.Atoms.rotate_euler`
+* :meth:`~ase.atoms.Atoms.get_dihedral`
+* :meth:`~ase.atoms.Atoms.set_dihedral`
+* :meth:`~ase.atoms.Atoms.rotate_dihedral`
+* :meth:`~ase.atoms.Atoms.rattle`
+* :meth:`~ase.atoms.Atoms.set_constraint`
+* :meth:`~ase.atoms.Atoms.set_distance`
+* :meth:`~ase.atoms.Atoms.copy`
+* :meth:`~ase.atoms.Atoms.get_center_of_mass`
+* :meth:`~ase.atoms.Atoms.get_distance`
+* :meth:`~ase.atoms.Atoms.get_volume`
+* :meth:`~ase.atoms.Atoms.has`
+* :meth:`~ase.atoms.Atoms.edit`
+* :meth:`~ase.atoms.Atoms.identical_to`
+
+
+
+List of all Methods
+===================
+
+.. autoclass:: ase.atoms.Atoms
+   :members:
+
+
diff --git a/doc/ase/calculators/FHI-aims.rst b/doc/ase/calculators/FHI-aims.rst
new file mode 100644
index 0000000..531f890
--- /dev/null
+++ b/doc/ase/calculators/FHI-aims.rst
@@ -0,0 +1,191 @@
+.. module:: FHI-aims
+
+========
+FHI-aims
+========
+
+Introduction
+============
+
+FHI-aims_ is a all-electron full-potential density functional theory 
+code using a numeric local orbital basis set. This interface provides
+all that should be required to run FHI-aims_ from within ASE.
+
+.. _FHI-aims: http://www.fhi-berlin.mpg.de/aims/
+
+Running the Calculator
+====================== 
+
+The default initialization command for the FHI-aims calculator is 
+
+.. class:: Aims(output_template = 'aims', track_output = False)
+
+In order to run a calculation, you have to ensure that at least the 
+following ``str`` variables are specified, either in the initialization 
+or as shell variables:
+
+===============  ====================================================
+keyword          description
+===============  ====================================================
+``run_command``   The full command required to run FHI-aims from 
+		  a shell, including anything to do with an MPI
+		  wrapper script and the number of tasks.
+		  An alternative way to set this command is via the 
+		  shell variable ``AIMS_COMMAND``, which is checked
+		  upon initialization and when starting a run. 
+``species_dir``   Directory where the species defaults are located 
+		  that should be used. Can also be specified with 
+		  the system variable ``AIMS_SPECIES_DIR``.
+``xc``            The minimal physical specification: what kind of 
+		  calculation should be done. 
+===============  ====================================================
+
+In addition, you might want to specify at least one of self-consistency 
+accuracy commands (see below) in order to avoid an excessively long 
+calculation. 
+
+Two general options might come in useful to postprocess the output:
+
+===================  ====================================================
+keyword              description
+===================  ====================================================
+``output_template``  Base name for the output, in case the calculator
+		     is called multiple times within a single script. 
+``track_output``     ``True/False`` - if ``True`` all the output files
+		     will be kept, while the number of calls to the 
+		     calculator is encoded in the output file name. 
+===================  ====================================================
+
+List of keywords
+================
+
+This is a non-exclusive list of keywords for the ``control.in`` file 
+that can be addresses from within ASE. The meaning for these keywords is 
+exactly the same as in FHI-aims, please refer to its manual for help on 
+their use. 
+
+One thing that should be mentioned is that keywords with more than
+one option have been implemented as lists, eg. 
+``k_grid=(12,12,12)`` or ``relativistic=('atomic_zora','scalar')``. 
+In those cases, specifying a single string containing all the options is also possible. 
+
+None of the keywords have any default within ASE,but do check the defaults
+set by FHI-aims. If there is a keyword that you would 
+like to set and that is not yet implemented here, it is trivial to add 
+to the first few lines of the aims calculator in the file 
+ASE/ase/calculators/aims.py .
+
+Describing the basic physics of the system:
+
+============================  ======
+keyword                       type 
+============================  ======
+``xc``			      str   
+``charge``                    float
+``spin``		      str
+``relativistic``	      list
+``use_dipole_correction``     bool
+``vdw_correction_hirshfeld``  str
+``k_grid``		      list
+============================  ======
+
+Driving relaxations and molecular dynamics:
+
+============================  ======
+keyword                       type 
+============================  ======
+``relax_geometry``	      list
+``max_relaxation_steps``      int
+``n_max_pulay``  	      int
+``sc_iter_limit``	      int
+``restart_relaxations``	      bool
+``MD_run``		      list
+``MD_schedule``		      list
+``MD_segment``		      list
+============================  ======
+
+Output options:
+
+============================  ========
+keyword                       type 
+============================  ========
+``output_level``	      str
+``output``		      list
+``cubes``                     AimsCube
+============================  ========
+
+See below for a description of the volumetric cube file output
+interface AimsCube
+
+Keywords for accuracy settings:
+
+============================  ======
+keyword                       type 
+============================  ======
+``sc_accuracy_eev``	      exp
+``sc_accuracy_etot``	      exp
+``sc_accuracy_forces``	      exp
+``sc_accuracy_rho``	      exp
+``compute_forces``	      bool
+============================  ======
+
+Keywords to adjust the SCF-cycle
+
+============================  ======
+keyword                       type 
+============================  ======
+``charge_mix_param``	      float	
+``prec_mix_param``	      float
+``spin_mix_param``	      float
+``KS_method``		      str
+``restart``		      str
+``restart_read_only``	      str
+``restart_write_only``	      srt
+``preconditioner``	      list
+``mixer``		      str
+``empty_states``	      int	
+``ini_linear_mixing``	      int
+``mixer_threshold``	      list
+``occupation_type``	      list
+============================  ======
+
+Note:: 
+   
+ Any argument can be changed after the initial construction of the
+ calculator, simply by setting it with the method 
+
+   >>> calc.set( keyword=value )
+
+Volumetric Data Output
+======================
+
+The class
+
+.. class:: AimsCube(origin=(0,0,0),edges=[(0.1,0.0,0.0),(0.0,0.1,0.0),(0.0,0.0,0.1)],points=(50,50,50),plots=None)
+
+describes an object that takes care of the volumetric
+output requests within FHI-aims. An object of this type can 
+be attached to the main Aims() object as an option. 
+
+The possible arguments for AimsCube are:
+
+============================  ========
+keyword                       type 
+============================  ========
+``origin``		      list
+``edges``		      3x3-array
+``points``		      list
+``plots``		      list
+============================  ========
+
+The possible values for the entry of plots 
+are discussed in detail in the FHI-aims manual, 
+see below for an example.
+
+Example
+=======
+
+As an example, here is a possible setup to obtain 
+the geometry of a water molecule:
+
+.. literalinclude:: H2O_aims.py
diff --git a/doc/ase/calculators/H2O_aims.py b/doc/ase/calculators/H2O_aims.py
new file mode 100644
index 0000000..f70a072
--- /dev/null
+++ b/doc/ase/calculators/H2O_aims.py
@@ -0,0 +1,25 @@
+from ase import Atoms
+from ase.visualize import view
+from ase.calculators.aims import Aims, AimsCube
+from ase.optimize import QuasiNewton
+
+water = Atoms('HOH', [(1,0,0), (0,0,0), (0,1,0)])
+
+water_cube = AimsCube(points=(29,29,29),
+                      plots=('total_density','delta_density',
+                             'eigenstate 5','eigenstate 6'))
+
+calc=Aims(xc='pbe',
+          sc_accuracy_etot=1e-6,
+          sc_accuracy_eev=1e-3,
+          sc_accuracy_rho=1e-6,
+          sc_accuracy_forces=1e-4,
+          species_dir='/home/hanke/codes/fhi-aims/fhi-aims.workshop/species_defaults/light/',
+          run_command='aims.workshop.serial.x',
+          cubes=water_cube)
+
+water.set_calculator(calc)
+dynamics = QuasiNewton(water,trajectory='square_water.traj')
+dynamics.run(fmax=0.01)
+
+view(water)
diff --git a/doc/ase/calculators/INCAR_NaCl b/doc/ase/calculators/INCAR_NaCl
new file mode 100644
index 0000000..71efbfc
--- /dev/null
+++ b/doc/ase/calculators/INCAR_NaCl
@@ -0,0 +1,5 @@
+INCAR created by Atomic Simulation Environment
+ PREC = Accurate
+ LREAL = .FALSE.
+ ISPIN = 2
+ MAGMOM = 1*1.9280 1*0.7500 
diff --git a/doc/ase/calculators/NaCl.py b/doc/ase/calculators/NaCl.py
new file mode 100644
index 0000000..8ae6bbb
--- /dev/null
+++ b/doc/ase/calculators/NaCl.py
@@ -0,0 +1,15 @@
+from ase import Atoms, Atom
+from ase.calculators.vasp import Vasp
+
+a = [6.5, 6.5, 7.7]
+d = 2.3608
+NaCl = Atoms([Atom('Na', [0, 0, 0], magmom=1.928),
+              Atom('Cl', [0, 0, d], magmom=0.75)],
+             cell=a)
+
+calc = Vasp(prec = 'Accurate', 
+            xc = 'PBE', 
+            lreal = False)
+NaCl.set_calculator(calc)
+
+print NaCl.get_magnetic_moment()
diff --git a/doc/ase/calculators/Si_abinit.py b/doc/ase/calculators/Si_abinit.py
new file mode 100644
index 0000000..870ebf4
--- /dev/null
+++ b/doc/ase/calculators/Si_abinit.py
@@ -0,0 +1,21 @@
+from ase import *
+from ase.calculators.abinit import Abinit
+
+a0 = 5.43
+bulk = Atoms('Si2', [(0, 0, 0),
+                     (0.25, 0.25, 0.25)],
+             pbc=True)
+b = a0 / 2
+bulk.set_cell([(0, b, b),
+               (b, 0, b),
+               (b, b, 0)], scale_atoms=True)
+
+calc = Abinit(label='Si',
+              nbands=8,
+              xc='PBE',
+              ecut=50 * Ry,
+              mix=0.01,
+              kpts=[10, 10, 10])
+
+bulk.set_calculator(calc)
+e = bulk.get_potential_energy()
diff --git a/doc/ase/calculators/abinit.rst b/doc/ase/calculators/abinit.rst
new file mode 100644
index 0000000..a13bef8
--- /dev/null
+++ b/doc/ase/calculators/abinit.rst
@@ -0,0 +1,123 @@
+.. module:: abinit
+
+======
+ABINIT
+======
+
+Introduction
+============
+
+ABINIT_ is a density-functional theory code
+based on pseudopotentials and a planewave basis.
+
+
+.. _ABINIT: http://www.abinit.org
+
+
+
+Environment variables
+=====================
+
+**Note**: setting environment variables is necessary
+only if you configure your ABINIT/ASE environment from scratch.
+
+You need to write a script called :file:`abinit.py` containing
+something like this::
+
+  import os
+  abinit = '/usr/bin/abinis'
+  exitcode = os.system('%s < %s.files > %s.log' % (abinit, label, label))
+
+The environment variable :envvar:`ABINIT_SCRIPT` must point to that file.
+
+A directory containing the pseudopotential files :file:`.fhi` is also
+needed, and it is to be put in the environment variable
+:envvar:`ABINIT_PP_PATH`.
+
+Set both environment variables in your in your shell configuration file:
+
+.. highlight:: bash
+ 
+::
+
+  $ export ABINIT_SCRIPT=$HOME/bin/abinit.py
+  $ export ABINIT_PP_PATH=$HOME/mypps
+
+.. highlight:: python
+
+
+
+ABINIT Calculator
+================= 
+
+The default parameters are very close to those that the ABINIT Fortran
+code uses.  These are the exceptions:
+
+.. class:: Abinit(label='abinit', xc='LDA', pulay=5, mix=0.1)
+    
+Here is a detailed list of all the keywords for the calculator:
+
+============== ========= ================  =====================================
+keyword        type      default value     description
+============== ========= ================  =====================================
+``kpts``       ``list``  ``[1,1,1]``       Monkhorst-Pack k-point sampling
+``nbands``     ``int``   ``None``          Number of band. May be omitted.
+``nstep``      ``int``   ``None``          Number of self-consistent field STEPS.
+``ecut``       ``float`` ``None``          Planewave cutoff energy in eV (default: None)
+``xc``         ``str``   ``'LDA'``         Exchange-correlation functional.
+``pulay``      ``int``   ``5``             Number of old densities to use for
+                                           Pulay mixing
+``mix``        ``float`` ``0.1``           Pulay mixing weight 
+``width``      ``float`` ``0.04 Ha``       Fermi-distribution width in eV (default: 0.04 Ha)
+``charge``     ``float`` ``0``             Total charge of the system (default: 0)
+``label``      ``str``   ``'abinit'``      Name of the output file
+``toldfe``     ``float`` ``1.0e-6``        TOLerance on the DiFference of total Energy
+============== ========= ================  =====================================
+
+A value of ``None`` means that ABINIT's default value is used.
+
+**Warning**: abinit does not specify a default value for
+``Planewave cutoff energy in eV`` - you need to set them as in the example at thei bottom of the page, otherwise calculation will fail.
+Calculations wihout k-points are not parallelized by default
+and will fail! To enable band paralellization specify ``Number of BanDs in a BLOCK`` 
+(``nbdblock``) as `Extra parameters`_ -
+see `<http://www.abinit.org/Infos_v5.2/tutorial/lesson_parallelism.html>`_.
+
+Extra parameters
+================
+
+The ABINIT code reads the input parameters for any calculation from a 
+:file:`.in` file and :file:`.files` file.
+This means that you can set parameters by manually setting 
+entries in this input :file:`.in` file. This is done by the syntax:
+
+>>> calc.set_inp('name_of_the_entry', value)
+
+For example, the ``ndtset`` can be set using
+
+>>> calc.set_inp('ndtset', 2)
+
+The complete list of keywords can be found in the official `ABINIT
+manual`_.
+
+.. _ABINIT manual: http://www.abinit.org/Infos_v5.4/input_variables/keyhr.html
+
+
+
+Pseudopotentials
+================
+
+Pseudopotentials in the ABINIT format are available on the
+`pseudopotentials`_ website.
+A database of user contributed pseudopotentials is also available there.
+
+.. _pseudopotentials: http://www.abinit.org/Psps/?text=psps
+
+
+
+Example 1
+=========
+
+Here is an example of how to calculate the total energy for bulk Silicon:
+        
+.. literalinclude:: Si_abinit.py
diff --git a/doc/ase/calculators/ase_castep_demo.py b/doc/ase/calculators/ase_castep_demo.py
new file mode 100644
index 0000000..57db565
--- /dev/null
+++ b/doc/ase/calculators/ase_castep_demo.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+"""This simple demo calculates the total energy of CO molecules
+using once LDA and once PBE as xc-functional. Obviously
+some parts in this scripts are longer than necessary, but are shown
+to demonstrate some more features."""
+
+import ase
+import ase.calculators.castep, ase.io.castep
+
+calc = ase.calculators.castep.Castep()
+directory = 'CASTEP_ASE_DEMO'
+
+# include interface settings in .param file
+calc._export_settings = True
+
+# reuse the same directory
+calc._directory = directory
+calc._rename_existing_dir = False
+calc._label = 'CO_LDA'
+
+# necessary for tasks with changing positions
+# such as GeometryOptimization or MolecularDynamics
+calc._set_atoms = True
+
+# Param settings
+calc.param.xc_functional = 'LDA'
+calc.param.cut_off_energy = 400
+# Prevent CASTEP from writing *wvfn* files
+calc.param.num_dump_cycles = 0
+
+# Cell settings
+calc.cell.kpoint_mp_grid = '1 1 1'
+calc.cell.fix_com = False
+calc.cell.fix_all_cell = True
+
+# Set and clear and reset settings (just for shows)
+calc.param.task = 'SinglePoint'
+# Reset to CASTEP default
+calc.param.task.clear()
+
+# all of the following are identical
+calc.param.task = 'GeometryOptimization'
+calc.task = 'GeometryOptimization'
+calc.TASK = 'GeometryOptimization'
+calc.Task = 'GeometryOptimization'
+
+
+# Prepare atoms
+mol = ase.atoms.Atoms('CO', [[0, 0, 0], [0, 0, 1.2]], cell=[10, 10, 10])
+mol.set_calculator(calc)
+
+# Check for correct input
+if calc.dryrun_ok():
+    print('%s : %s ' % (mol.calc._label, mol.get_potential_energy()))
+else:
+    print("Found error in input")
+    print(calc._error)
+
+
+# Read all settings from previous calculation
+mol = ase.io.castep.read_seed('%s/CO_LDA' % directory)
+
+# Use the OTF pseudo-potential we have just generated
+mol.calc.set_pspot('OTF')
+
+# Change some settings
+mol.calc.param.xc_functional = 'PBE'
+# don't forget to set an appropriate label
+mol.calc._label = 'CO_PBE'
+# Recalculate the potential energy
+print('%s : %s ' % (mol.calc._label, mol.get_potential_energy()))
diff --git a/doc/ase/calculators/calculators.rst b/doc/ase/calculators/calculators.rst
new file mode 100644
index 0000000..65810e3
--- /dev/null
+++ b/doc/ase/calculators/calculators.rst
@@ -0,0 +1,160 @@
+.. module:: calculators
+   :synopsis: Energy, force and stress calculators.
+
+===========
+Calculators
+===========
+
+For ASE, a calculator is a black box that can take atomic numbers and
+atomic positions from an :class:`~atoms.Atoms` object and calculate the
+energy and forces and sometimes also stresses.
+
+In order to calculate forces and energies, you need to attach a
+calculator object to your atoms object:
+
+>>> a = read('molecule.xyz')
+>>> e = a.get_potential_energy()
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/jjmo/ase/ase/atoms.py", line 399, in get_potential_energy
+    raise RuntimeError('Atoms object has no calculator.')
+RuntimeError: Atoms object has no calculator.
+>>> from ase.calculators import Abinit
+>>> calc = Abinit(...)
+>>> a.set_calculator(calc)
+>>> e = a.get_potential_energy()
+>>> print e
+-42.0
+
+Here, we used the :meth:`~ase.atoms.Atoms.set_calculator` method to attach
+an instance of the :class:`~abinit.Abinit` class and then
+we asked for the energy.
+
+Alternatively, a calculator can be attached like this::
+
+  atoms = Atoms(..., calculator=Siesta())
+
+
+.. _supported calculators:
+
+Supported calculators
+=====================
+
+
+================  ===========================================  ============
+Code              Description                                  Type
+================  ===========================================  ============
+GPAW_             Grid-based real-space PAW code               :term:`DFT`,
+                                                               :term:`HF`
+Asap_             Highly efficient EMT code (written in C++)   :term:`EMT`
+:mod:`jacapo`     ASE interface to Dacapo_,                    :term:`DFT`
+                  a planewave ultra-soft pseudopotential code
+Dacapo_           Old interface to Dacapo_. Requires           :term:`DFT`
+                  Numeric python and ASE2.
+:mod:`emt`        Effective Medium Theory calculator           :term:`EMT`
+:mod:`abinit`     A planewave pseudopotential code             :term:`DFT`
+:mod:`siesta`     LCAO pseudopotential code                    :term:`DFT`
+:mod:`dftb`       DftbPlus_ DFT based tight binding            :term:`DFT`
+:mod:`turbomole`  Fast atom orbital code Turbomole_,           :term:`DFT`,
+                                                               :term:`HF`
+:mod:`castep`     Planewave pseodopotential code               :term:`DFT`,
+                                                               :term:`HF`
+:mod:`vasp`       Planewave PAW code                           :term:`DFT`
+:mod:`FHI-aims`   Numeric Atomic Orbital, full potential code  :term:`DFT`,
+                                                               :term:`HF` 
+:mod:`exciting`   Full Potential LAPW code                     :term:`DFT`,
+                                                               :term:`LAPW`
+:mod:`fleur`      Full Potential LAPW code                     :term:`DFT`,
+                                                               :term:`LAPW`
+:mod:`lammps`     Classical molecular dynamics code
+:mod:`mmtk`       XXX Library for molecular simulations 
+================  ===========================================  ============
+  
+
+.. _Asap: http://wiki.fysik.dtu.dk/asap
+.. _Dacapo: http://wiki.fysik.dtu.dk/dacapo
+.. _GPAW: http://wiki.fysik.dtu.dk/gpaw
+.. _DftbPlus: http://www.dftb-plus.info/
+.. _Turbomole: http://www.turbomole.com/
+
+
+The calculators can be divided in three groups:
+
+1) GPAW, Asap, Dacapo have their own native ASE interfaces.
+
+2) Jacapo, ABINIT, SIESTA, DftbPlus, TURBOMOLE, VASP, FLEUR, 
+   FHI-aims, LAMMPS and MMTK have Python wrappers in the ASE
+   package, but the actual codes are not part of ASE.
+
+3) EMT is a pure python implementation of the Effective Medium Theory
+   potential and it is included in the ASE package.
+
+
+Documentation for group 2 and 3 calculators
+===========================================
+
+.. toctree::
+
+   emt
+   jacapo
+   abinit
+   siesta
+   dftb
+   turbomole
+   vasp
+   FHI-aims
+   lammps
+   mmtk
+   exciting
+   fleur
+   castep
+
+
+
+Calculator interface
+====================
+
+All calculators must have the following interface:
+
+.. autoclass:: ase.calculators.interface.Calculator
+   :members:
+
+
+Electronic structure calculators
+================================
+
+These calculators have wave functions, electron densities, eigenvalues
+and many other quantities.  Therefore, it makes sense to have a set of
+standard methods for accessing those quantities:
+
+.. autoclass:: ase.calculators.interface.DFTCalculator
+   :members:
+
+
+
+Building new calculators
+========================
+
+Adding an ASE interface to your favorite force-calculator can be very
+simple.  Take a look at the Python wrapper we have in the ASE code for
+using the SIESTA_ code with ASE: :trac:`ase/calculators/siesta.py`.
+Here, a :class:`~siesta.Siesta` class is defined.  An instance of this class
+will simply write the fdf input-file, start the SIESTA Fortran
+program, and finally read the energy, forces and stresses from the
+text output-file.
+
+
+.. _SIESTA: http://www.uam.es/departamentos/ciencias/fismateriac/siesta/
+
+
+
+Building neighbor-lists
+=======================
+
+The :class:`EMT` potential and the GPAW_ DFT calculator both make
+use of ASE's built-in neighbor-list class:
+
+.. autoclass:: ase.calculators.neighborlist.NeighborList
+   :members:
+
+
diff --git a/doc/ase/calculators/castep.rst b/doc/ase/calculators/castep.rst
new file mode 100644
index 0000000..8e44053
--- /dev/null
+++ b/doc/ase/calculators/castep.rst
@@ -0,0 +1,18 @@
+.. module:: castep
+
+========
+CASTEP
+========
+
+
+
+.. include:: ../../../ase/calculators/castep.py
+   :encoding: utf-8
+   :start-after: CASTEP Interface Documentation
+   :end-before: End CASTEP Interface Documentation
+
+
+Example:
+========
+
+.. literalinclude:: ase_castep_demo.py
diff --git a/doc/ase/calculators/dftb.rst b/doc/ase/calculators/dftb.rst
new file mode 100755
index 0000000..1050433
--- /dev/null
+++ b/doc/ase/calculators/dftb.rst
@@ -0,0 +1,120 @@
+.. module:: dftb
+
+=========
+DftbPlus
+=========
+
+Introduction
+============
+
+DftbPlus_ is a density-functional based tight-binding code using 
+atom centered orbitals. This 
+interface makes it possible to use DftbPlus_ as a calculator in ASE.
+You need to register at DftbPlus_ site to download the code.
+Additionally you need Slater-coster files for the combination of 
+atom types of your system. These can be obtained at dftb.org_.
+
+.. _DftbPlus: http://www.dftb-plus.info/
+.. _dftb.org: http://www.dftb.org/
+
+
+
+Environment variables
+=====================
+
+Set environment variables in your configuration file (what is the directory
+for the Slater-Koster files and what is the name of the executable):
+
+- bash::
+
+  $ DFTB_PREFIX=/my_disk/my_name/lib/Dftb+sk/mio-0-1/  (an example)
+  $ DFTB_COMMAND=~/bin/DFTB+/dftb+_s081217.i686-linux  (an example)
+
+- csh/tcsh::
+
+  $ setenv DFTB_PREFIX /my_disk/my_name/lib/Dftb+sk/mio-0-1/  (an example)
+  $ setenv DFTB_COMMAND ~/bin/DFTB+/dftb+_s081217.i686-linux   (an example)
+
+
+DftbPlus Calculator
+==================== 
+This is a preliminary version of the DftbPlus calculator, so all the
+DftbPlus features are unfortunately not there.
+
+Use write_dftb=False to use your own 'dftb_in.hsd'-file with all the
+flavours you need. In that case ase only updates the coordinates of
+the system, otherwise file 'dftb_in.hsd' remains intact (see example 2
+below). However, in case of own 'dftb_in.hsd' file you need first read
+in atom position and atom type information of your system for ase (for
+instance a xyz-file), see example 2 below.
+
+The atom positions in file 'dftb_in.hsd' are updated during 
+ASE geometry optimization.
+
+For the spin polarised calculations this ASE-interface generates parameters 
+with GGA-PBE-spin parameters. If you need LDA use your own 'dftb_in.hsd'-file. 
+
+Information of periodicity is taken from ase (see example1 below).
+
+
+For example::
+
+    calc = Dftb(label='o2',
+                write_dftb=True,
+                do_spin_polarized=True,
+                unpaired_electrons=2.0,
+                fermi_temperature=100.0,
+                scc=True)	
+
+
+Parameters
+==========
+label: str
+    Prefix to use for filenames (label.txt, ...).
+    Default is 'dftb'.
+write_dftb: boolean
+    True: a minimal input file (name of which is always 'dftb_in.hsd')
+    is written based on values given here.
+    False: input file for dftb+ is not written. User must have
+    generated file 'dftb_in.hsd' in the working directory.
+    Use write_dftb=False to use your own 'dftb_in.hsd'-file.
+charge: float
+    Total charge of the system.
+include_dispersion: boolean
+    True: Default dispersion parameters are written in the 
+    file 'dftb_in.hsd' (requires that also write_dftb_input_file==True)
+    False: dispersion parameters are not written here.
+do_spin_polarized: boolean
+    True: Spin polarized calculation
+    False: Spin unpolarized calculation
+unpaired_electrons: float
+    Number of spin unpaired electrons in the system.
+    Relevant only if do_spin_polarized==True
+fermi_temperature: float
+    Fermi temperature for electrons.
+scc: boolean
+    True: Do charge self consistent dftb+
+    False: No SCC, charges on atoms are not iterated
+    
+Example1: Geometry Optimization
+===============================
+
+.. literalinclude:: dftb_ex1_relax.py
+
+Example2: Geometry Optimization using your own 'dftb_in.hsd' file
+==================================================================
+
+.. literalinclude:: dftb_ex2_relax.py
+
+Input file for example 2 (h2o_1.xyz)
+
+.. literalinclude:: h2o_1.xyz
+
+You also need to have generated the file 'dftb_in.hsd', here is and example:
+change the variable 'Prefix' below 
+
+.. literalinclude:: dftb_in.hsd
+
+
+
+
diff --git a/doc/ase/calculators/dftb_ex1_relax.py b/doc/ase/calculators/dftb_ex1_relax.py
new file mode 100755
index 0000000..e679668
--- /dev/null
+++ b/doc/ase/calculators/dftb_ex1_relax.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+import os
+
+from ase import Atoms
+from ase.calculators.dftb import Dftb
+from ase.optimize import QuasiNewton
+from ase.io import write
+
+d = 1.10
+test = Atoms('2O', positions=[(0., 0., 0.), (0., 0., d)])
+test.set_pbc([False, False, False])
+test.set_calculator(Dftb(label='o2',write_dftb=True,do_spin_polarized=True,unpaired_electrons=2.0,fermi_temperature=100.0,scc=True))
+
+dyn = QuasiNewton(test, trajectory='test.traj')
+dyn.run(fmax=0.01)
+
+write('test.final.xyz', test)
+
diff --git a/doc/ase/calculators/dftb_ex2_relax.py b/doc/ase/calculators/dftb_ex2_relax.py
new file mode 100644
index 0000000..4b2ed8f
--- /dev/null
+++ b/doc/ase/calculators/dftb_ex2_relax.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+from ase.io import read, write
+from ase.calculators.dftb import Dftb
+from ase.optimize import QuasiNewton
+
+system = read('h2o_1.xyz')
+system.set_calculator(Dftb(label='dftb',write_dftb=False))
+
+qn = QuasiNewton(system)
+qn.run(fmax=0.005)
+
+write('final.xyz', system)
+write('final.traj', system)
diff --git a/doc/ase/calculators/dftb_in.hsd b/doc/ase/calculators/dftb_in.hsd
new file mode 100755
index 0000000..a46d60d
--- /dev/null
+++ b/doc/ase/calculators/dftb_in.hsd
@@ -0,0 +1,24 @@
+Geometry = {
+TypeNames = { "O" }
+TypesAndCoordinates [Angstrom] = {
+     1     0.00000000000000     0.00000000000000     0.00000000000000
+     1     0.00000000000000     0.00000000000000     1.10000000000000
+ }
+Periodic = No
+}
+
+Driver = ConjugateGradient {
+MovedAtoms = Range { 1 -1 }
+  MaxForceComponent = 1.0e-4
+  MaxSteps = 0
+  OutputPrefix = o2
+}
+
+Hamiltonian = DFTB { # DFTB Hamiltonian
+  SCC = Yes # Use self consistent charges
+  SCCTolerance = 1.0e-5 # Tolerance for charge consistence
+  MaxSCCIterations = 1000 # Nr. of maximal SCC iterations
+  Mixer = Broyden { # Broyden mixer for charge mixing
+    MixingParameter = 0.2 # Mixing parameter
+  }
+  SlaterKosterFiles = Type2FileNames { # File names from two atom type names
diff --git a/doc/ase/calculators/emt.rst b/doc/ase/calculators/emt.rst
new file mode 100644
index 0000000..f49ee2f
--- /dev/null
+++ b/doc/ase/calculators/emt.rst
@@ -0,0 +1,25 @@
+.. module::  emt
+   :synopsis: Effective Medium Theory
+
+==========================
+Pure Python EMT calculator
+==========================
+
+The EMT potential is included in the ASE package in order to have a
+simple calculator that can be used for quick demonstrations and
+tests.
+
+.. warning::
+
+   If you want to do a real application using EMT, you should used the
+   *much* more efficient implementation in the ASAP_ calculator.
+
+.. class:: EMT()
+
+Right now, the only supported elements are: H, C, N, O, Al, Ni, Cu,
+Pd, Ag, Pt and Au.  The EMT parameters for the metals are quite
+realistic for many purposes, whereas the H, C, N and O parameters are
+just for fun!
+
+
+.. _ASAP: http://wiki.fysik.dtu.dk/asap
diff --git a/doc/ase/calculators/exciting.rst b/doc/ase/calculators/exciting.rst
new file mode 100644
index 0000000..fdb47a3
--- /dev/null
+++ b/doc/ase/calculators/exciting.rst
@@ -0,0 +1,90 @@
+.. module:: exciting
+
+========
+exciting
+========
+
+.. image:: ../../_static/exciting.png
+
+Introduction
+============
+
+``exciting`` is a full-potential *all-electron*
+density-functional-theory (:term:`DFT`) package based on the
+linearized augmented planewave (:term:`LAPW`) method. It can be
+applied to all kinds of materials, irrespective of the atomic species
+involved, and also allows for the investigation of the core
+region. The website is http://exciting-code.org/
+
+The module depends on lxml  http://codespeak.net/lxml/
+
+
+Exciting Calculator Class
+=========================
+
+.. autoclass:: ase.calculators.exciting.Exciting
+
+There are two ways to construct the exciting calculator.
+
+Constructor Keywords
+--------------------
+
+One is by giving parameters of the ground state in the
+constructor. The possible attributes can be found at
+http://exciting-code.org/input-reference#groundstate.
+
+.. class:: Exciting(bin='excitingser', kpts=(4, 4, 4), xctype='GGArevPBE')
+
+XSLT Template
+-------------
+The other way is to use an XSLT template
+
+.. class:: Exciting( template='template.xsl'  , bin='excitingser')
+
+The template may look like:
+
+.. highlight:: xml
+
+::
+
+    <?xml version="1.0" encoding="UTF-8" ?>
+    <xsl:stylesheet version="1.0"
+      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+      <xsl:output method="xml" />
+      <xsl:template match="/">
+        <xsl:comment>
+          created from template
+        </xsl:comment>
+    
+    
+    <!-- ############# -->
+        <input>
+          <title></title>
+          <structure speciespath="./">
+            <xsl:copy-of select="/input/structure/crystal" />
+            <xsl:copy-of select="/input/structure/species" />
+          </structure>
+          <groundstate ngridk="4  4  4"  vkloff="0.5  0.5  0.5" tforce="true" />
+        </input>
+    <!-- ############# -->
+    
+    
+    
+      </xsl:template>
+    </xsl:stylesheet>
+    
+.. highlight:: python    
+
+Current Limitations
+===================
+
+The calculator supports only total energy and forces no stress strain
+implemented in exciting yet.  However it is planned to be implemented
+soon. http://exciting-code.org/current-developments.
+
+The keywords on the constructor are converted into attributes 
+of the ``groundstate`` element. No check is done there and not all 
+options are accessible in that way. By using the template one can 
+exceed this limitation.
+
+
diff --git a/doc/ase/calculators/fcc_Ni_fleur.py b/doc/ase/calculators/fcc_Ni_fleur.py
new file mode 100644
index 0000000..1b840ab
--- /dev/null
+++ b/doc/ase/calculators/fcc_Ni_fleur.py
@@ -0,0 +1,17 @@
+from numpy import linspace
+
+from ase.calculators.fleur import FLEUR
+from ase.lattice import bulk
+from ase.io.trajectory import PickleTrajectory
+
+atoms = bulk('Ni', a=3.52)
+calc = FLEUR(xc='PBE', kmax=3.6, kpts=(10, 10, 10), workdir='lat_const')
+atoms.set_calculator(calc)
+traj = PickleTrajectory('Ni.traj','w', atoms)
+cell0 = atoms.get_cell()
+for s in linspace(0.95, 1.05, 7):
+    cell = cell0 * s
+    atoms.set_cell((cell))
+    ene = atoms.get_potential_energy()
+    traj.write()
+
diff --git a/doc/ase/calculators/fleur.rst b/doc/ase/calculators/fleur.rst
new file mode 100644
index 0000000..05fb9e9
--- /dev/null
+++ b/doc/ase/calculators/fleur.rst
@@ -0,0 +1,107 @@
+.. module:: fleur
+
+=====
+FLEUR
+=====
+
+Introduction
+============
+
+FLEUR_ is a density-functional theory code which uses the full
+potential linearized augmented plane-wave (FLAPW) method. FLEUR_ can
+be applied to any element in the periodic table, and the code is well
+suited especially to surfaces and magnetic materials.
+
+
+.. _FLEUR: http://www.flapw.de
+
+
+
+Environment variables
+=====================
+
+In order to use FLEUR through ASE, two environment variables have to
+be set. :envvar:`FLEUR_INPGEN` should point to the simple input
+generator of FLEUR, and :envvar:`FLEUR` to the actual executable. Note
+that FLEUR has different executables e.g. for cases with and without
+inversion symmetry, so the environment variable has to be set accordingly.
+As an example, the variables could be set like:
+
+.. highlight:: bash
+ 
+::
+
+  $ export FLEUR_INPGEN=$HOME/fleur/v25/inpgen.x
+  $ export FLEUR=$HOME/fleur/v25/fleur.x
+
+.. highlight:: python
+
+or:
+
+.. highlight:: bash
+ 
+::
+
+  $ export FLEUR="mpirun -np 32 $HOME/fleur/v25/fleur.x"
+
+.. highlight:: python
+
+for parallel calculations.
+
+FLEUR Calculator
+================
+
+Currently, limited number of FLEUR parameters can be set via the ASE interface 
+Below follows a list of supported parameters
+
+===============  =========  ==============  ============================
+keyword          type       default value   description
+===============  =========  ==============  ============================
+``xc``           ``str``    ``'LDA'``       XC-functional. Must be one 
+                                            of 'LDA', 'PBE', 'RPBE'
+``kpts``         *seq*      `\Gamma`-point  **k**-point sampling
+``convergence``  *dict*     ``{'energy':    Convergence criteria (meV)
+                            0.0001}``
+``width``        ``float``                  Width of Fermi smearing (eV)
+``kmax``         ``float``                  Plane-wave cut-off (a.u.)
+``mixer``        *dict*                     Mixing parameters 'imix',
+                                            'alpha', and 'spinf'
+``maxiter``      ``int``    40              Maximum number of SCF steps
+``maxrelax``     ``int``    20              Maximum number of relaxation 
+                                            steps
+``workdir``      ``str``    Current dir     Working directory for the
+                                            calculation
+===============  =========  ==============  ============================
+
+*seq*: A sequence of three ``int``'s.
+*dict*: A dictionary
+
+
+Spin-polarized calculation
+==========================
+
+If the atoms object has non-zero magnetic moments, a spin-polarized calculation
+will be performed by default.
+
+Utility functions
+=================
+
+As only a subset of FLEUR parameters can currently be specified
+through ASE interface, the interface defines some utility functions
+for cases where manual editing of the FLEUR input file ``inp`` is
+necessary.
+
+.. automethod:: ase.calculators.fleur.FLEUR.write_inp
+.. automethod:: ase.calculators.fleur.FLEUR.initialize_density
+.. automethod:: ase.calculators.fleur.FLEUR.calculate
+.. automethod:: ase.calculators.fleur.FLEUR.relax
+
+Examples
+========
+
+Lattice constant of fcc Ni
+
+.. literalinclude:: fcc_Ni_fleur.py
+
+See the :ref:`equation of states tutorial <eos>` for analysis of the results.
+
diff --git a/doc/ase/calculators/h2o_1.xyz b/doc/ase/calculators/h2o_1.xyz
new file mode 100755
index 0000000..69eb885
--- /dev/null
+++ b/doc/ase/calculators/h2o_1.xyz
@@ -0,0 +1,5 @@
+3
+H2O
+O          0.00000        0.00000        0.00000
+H          0.00000        0.00000        1.00000
+H          1.00000        0.00000        0.00000
diff --git a/doc/ase/calculators/jacapo.rst b/doc/ase/calculators/jacapo.rst
new file mode 100644
index 0000000..f058cce
--- /dev/null
+++ b/doc/ase/calculators/jacapo.rst
@@ -0,0 +1,92 @@
+.. module::  jacapo
+   :synopsis: ASE python interface for Dacapo
+
+==========================================================
+Jacapo - ASE python interface for Dacapo
+==========================================================
+
+Introduction
+============
+
+Jacapo_ is an ASE interface for Dacapo_ and fully compatible with ASE. It 
+replaces the old Dacapo interface using Numeric python and ASE2.
+The code was originally developed by John Kitchin and detailed documentation
+as well as many examples are available online:
+
+http://gilgamesh.cheme.cmu.edu/doc/software/jacapo/index.html
+
+Jacapo is included as an optional calculator in ASE and small differences to the
+above documentation may occur.
+
+.. _Jacapo: http://gilgamesh.cheme.cmu.edu/doc/software/jacapo/index.html
+.. _Dacapo: http://wiki.fysik.dtu.dk/dacapo
+
+Jacapo Calculator
+================= 
+
+The Jacapo calculator is automatically installed with ase and can be imported using::
+
+  from ase.calculators.jacapo import *
+
+.. class:: Jacapo()
+    
+Here is a list of available keywords to initialize the calculator:
+
+============== ============ =====================================
+keyword        type         description
+============== ============ =====================================
+``nc``         ``str``      Output NetCDF file, or input file if nc already exists.
+``outnc``      ``str``      Output file. By default equal to nc.
+``atoms``      ``object``   ase atoms object
+``pw``         ``float``    Planewave cutoff in eV
+``dw``         ``float``    Density cutoff in eV
+``xc``         ``str``      Exchange-correlation functional. One of ['PZ','VWN','PW91','PBE','RPBE','revPBE']
+``nbands``     ``int``      Number of bands
+``ft``         ``float``    Fermi temperature
+``kpts``       ``list``     K-point grid, e.g. kpts = (2,2,1)
+``spinpol``    ``boolean``  Turn on/off spin-polarization
+``fixmagmom``  ``str``      Magnetic moment of the unit cell
+``symmetry``   ``boolean``  Turn on/off symmetry reduction
+``stress``     ``boolean``  Turn on/off stress calculation
+``dipole``     ``boolean``  Turn on/off dipole correction
+``stay_alive`` ``boolean``  Turn on/off stay alive
+``debug``      ``int``      Set debug level (0=off, 10=extreme)
+============== ============ =====================================
+
+Example
+=======
+
+Here is an example of how to calculate the total energy of a CO molecule::
+        
+  #!/usr/bin/env python
+  from ase import *
+  from ase.calculators.jacapo import *
+
+  CO = data.molecules.molecule('CO')
+  CO.set_cell([6,6,6])
+  CO.center()
+
+  calc = Jacapo(nc='CO.nc',
+                atoms=CO,
+                pw=300,
+                nbands=8)
+  
+  print CO.get_potential_energy()
+  
+
+Restarting from an old Calculation
+==================================
+
+With Jacapo it is very easy to restart an old calculation. All necessary information
+(including the constraints) is stored in the out.nc file. To continue for example a
+geometry optimization do something like this::
+
+  calc = Jacapo('old.nc',stay_alive=True)
+  atoms = calc.get_atoms()
+  dyn = QuasiNewton(atoms,logfile='qn.log')
+  dyn.run(fmax=0.05)
+
+Note, that the stay_alive flag is not stored in the .nc file and must be set when the
+calculator instance is created.
+
+
diff --git a/doc/ase/calculators/lammps.rst b/doc/ase/calculators/lammps.rst
new file mode 100644
index 0000000..48bd3bf
--- /dev/null
+++ b/doc/ase/calculators/lammps.rst
@@ -0,0 +1,99 @@
+.. module:: lammps
+
+======
+LAMMPS
+======
+
+Introduction
+============
+
+LAMMPS_ (Large-scale Atomic/Molecular Massively Parallel Simulator) is a classical molecular dynamics code.
+
+    "LAMMPS has potentials for soft materials (biomolecules, polymers) and solid-state materials (metals, semiconductors) and coarse-grained or mesoscopic systems. It can be used to model atoms or, more generically, as a parallel particle simulator at the atomic, meso, or continuum scale."
+
+
+.. _LAMMPS: http://lammps.sandia.gov
+
+
+
+Environment variables
+=====================
+
+The environment variable :envvar:`LAMMPS_COMMAND` should contain
+the path to the lammps binary, or more generally, a command line 
+possibly also including an MPI-launcher command.
+For example (in a Bourne-shell compatible environment):
+
+.. highlight:: bash
+ 
+::
+
+  $ export LAMMPS_COMMAND=/path/to/lmp_binary
+
+.. highlight:: python
+
+or possibly something similar to
+
+.. highlight:: bash
+ 
+::
+
+  $ export LAMMPS_COMMAND="/path/to/mpirun --np 4 lmp_binary"
+
+.. highlight:: python
+
+
+
+LAMMPS Calculator
+================= 
+
+The LAMMPS calculator first appeared in ASE version 3.5.0. 
+At the time of the release of ASE 3.5.0, the LAMMPS calculator 
+is still in a fairly early stage of development
+(if you are missing some feature, consider checking out 
+the source code development tree or some more recent version of ASE).
+
+.. class:: LAMMPS(..., parameters={}, files=[], ...)
+
+Below follows a list with a selection of parameters
+
+==============  =========  ==============  =============================
+keyword         type       default value   description
+==============  =========  ==============  =============================
+``files``       ``list``   ``[]``          List of files needed by 
+                                           LAMMPS. Typically a list of
+                                           potential files.
+``parameters``  ``dict``   ``{}``          Dictionary with key-value
+                                           pairs corresponding to 
+                                           commands and arguments.
+                                           Command-argument pairs 
+					   provided here will
+                                           be used for overriding the
+                                           the calculator defaults.
+==============  =========  ==============  =============================
+
+
+Example
+=======
+
+A simple example.
+
+::
+
+  from ase import Atoms, Atom
+  from ase.calculators.lammps import LAMMPS
+  
+  a = [6.5, 6.5, 7.7]
+  d = 2.3608
+  NaCl = Atoms([Atom('Na', [0, 0, 0]),
+                Atom('Cl', [0, 0, d])],
+               cell=a, pbc=True)
+  
+  calc = LAMMPS()
+  NaCl.set_calculator(calc)
+  
+  print NaCl.get_stress()
+
+
+
+
diff --git a/doc/ase/calculators/mmtk.rst b/doc/ase/calculators/mmtk.rst
new file mode 100644
index 0000000..6c9b448
--- /dev/null
+++ b/doc/ase/calculators/mmtk.rst
@@ -0,0 +1,14 @@
+.. module:: mmtk
+
+===========================
+Molecular Modelling Toolkit
+===========================
+
+.. class:: MMTK()
+
+.. warning::
+
+   ASE2 has an interface to MMTK, but ASE3 has not.  There may be a
+   problem with MMTK and numpy?
+
+   Let us know if you need MMTK, and we will take a look at it.
diff --git a/doc/ase/calculators/siesta.rst b/doc/ase/calculators/siesta.rst
new file mode 100644
index 0000000..c43ad73
--- /dev/null
+++ b/doc/ase/calculators/siesta.rst
@@ -0,0 +1,224 @@
+.. module:: siesta
+
+======
+SIESTA
+======
+
+Introduction
+============
+
+SIESTA_ is a density-functional theory code for very large systems
+based on atomic orbital (LCAO) basis sets.
+
+
+.. _SIESTA: http://www.uam.es/siesta/
+
+
+
+Environment variables
+=====================
+
+You need to write a script called :file:`run_siesta.py` containing
+something like this::
+
+  import os
+  siesta = 'siesta_2.0'
+  exitcode = os.system('%s < %s.fdf > %s.txt' % (siesta, label, label))
+
+The environment variable :envvar:`SIESTA_SCRIPT` must point to that file.
+
+A directory containing the pseudopotential files :file:`.vps` is also
+needed, and it is to be put in the environment variable
+:envvar:`SIESTA_PP_PATH`.
+
+Set both environment variables in your shell configuration file:
+
+.. highlight:: bash
+ 
+::
+
+  $ export SIESTA_SCRIPT=$HOME/siesta/run_siesta.py
+  $ export SIESTA_PP_PATH=$HOME/mypps
+
+.. highlight:: python
+
+
+
+SIESTA Calculator
+================= 
+
+The default parameters are very close to those that the SIESTA Fortran
+code uses.  These are the exceptions:
+
+.. class:: Siesta(label='siesta', xc='LDA', pulay=5, mix=0.1)
+    
+Here is a detailed list of all the keywords for the calculator:
+
+============== ========= ============= =====================================
+keyword        type      default value description
+============== ========= ============= =====================================
+``kpts``       ``list``  ``[1,1,1]``   Monkhorst-Pack k-point sampling
+``nbands``     ``int``   ``None``      Number of bands 
+``meshcutoff`` ``float`` ``None``      Mesh cut-off energy in eV 
+``basis``      ``str``   ``None``      Type of basis set ('sz', 'dz', 'szp',
+                                       'dzp') 
+``xc``         ``str``   ``'LDA'``     Exchange-correlation functional.
+``pulay``      ``int``   ``5``         Number of old densities to use for
+                                       Pulay mixing
+``mix``        ``float`` ``0.1``       Pulay mixing weight 
+``width``      ``float`` ``None``      Fermi-distribution width in eV
+``charge``     ``float`` ``None``      Total charge of the system
+``label``      ``str``   ``'siesta'``  Name of the output file
+============== ========= ============= =====================================
+
+A value of ``None`` means that SIESTA's default value is used.
+
+
+
+Extra FDF parameters
+====================
+
+The SIESTA code reads the input parameters for any calculation from a 
+:file:`.fdf` file. This means that you can set parameters by manually setting 
+entries in this input :file:`.fdf` file. This is done by the syntax:
+
+>>> calc.set_fdf('name_of_the_entry', value)
+
+For example, the ``EnergyShift`` can be set using
+
+>>> calc.set_fdf('PAO.EnergyShift', 0.01 * Rydberg)
+
+The complete list of the FDF entries can be found in the official `SIESTA
+manual`_.
+
+.. _SIESTA manual: http://www.uam.es/departamentos/ciencias/fismateriac/siesta
+
+
+
+Customized basis-set
+====================
+
+Standard basis sets can be set by the keyword ``basis`` directly
+in the Siesta calculator object. If a customized basis is needed, it 
+can be set as an FDF entry, as explained in the previous section.
+
+As an example, we generate a triple-zeta triple-polarized (TZTP)
+basis for Au. Since the valence states are 6s and 5d, we will have
+3 zeta orbitals for l=0 and 3 for l=2 plus 3 polarization orbitals
+for l=1. The basis can be defined by
+
+>>> value = ["""Au   2   split  0.00  #label, num. of l-shells,type,charge
+>>>         0   3   P    3            #l,nzeta,'P'(opt):pol.functions,npolzeta
+>>>         0.00   0.00   0.00        #rc of basis functions for each zeta function
+>>>                                   #0.00  => rc determined by PAO.EnergyShift
+>>>         2   3                     #l,nzeta
+>>>         0.00   0.00   0.00"""]    #rc
+
+>>> calc.set_fdf('PAO.Basis',value=value)
+
+The default basis set generation fails for Pt for some versions of
+Siesta. If this happens, you should specify the basis set
+manually. This is exemplified below.
+
+For Pt, using ``calc.set_fdf('PAO.EnergyShift', 0.1 * eV)`` is usually
+resonable, and a single-zeta polarized basis set can be specified by
+writing::
+
+  # Define SZP basis set for Pt
+  calc.set_fdf('PAO.Basis',
+               ["""\
+  Pt   2         # Species label, number of l-shells
+  n=6  0  1 P    # n, l, Nzeta, Polarization, NzetaPol
+  0.             # 0.0 => default [6.982 \n 1.000]
+  n=5  2  1      # n, l, zeta
+  0."""])        # 0.0 => default [5.172 \n 1.000]
+
+A double-zeta polarized basis set for Pt may be specified by::
+
+  # Define DZP basis set for Pt
+  calc.set_fdf('PAO.Basis',
+               ["""\
+  Pt 2 split 0.00  # Species label, number of l-shells
+  n=6 0 2 P 1      # n, l, Nzeta, Polarization, NzetaPol
+  0.00 0.00        # 0.0 => default [6.982  5.935 \n 1.000  1.000]
+  n=5 2 2          # n, l, zeta
+  0.00 0.00"""])   # 0.0 => default [5.172  3.060 \n 1.000  1.000]
+
+You can also reuse the basis set of a previous calculation, by copying
+the .ion files to the new location, and set the ``User.Basis`` tag to
+``True``::
+
+  # Load basis from previous calc (*.ion files)
+  calc.set_fdf('User.Basis', True)
+
+Warning: Specifying a basis set manually in any way will, for some
+obscure reason, make Siesta crash if you have ghost atoms!
+
+
+
+Pseudopotentials
+================
+
+Pseudopotential files in the ``.psf`` or ``.vps`` formats are needed. 
+Pseudopotentials generated from the ABINIT code and converted to 
+the SIESTA format are available in the `SIESTA`_ website . A database of user 
+contributed pseudopotentials is also available there.
+
+You can also find an on-line pseudopotential generator_ from the
+OCTOPUS code.
+
+.. _generator: http://www.tddft.org/programs/octopus/wiki/index.php/Pseudopotentials
+
+
+
+Example
+=======
+
+Here is an example of how to calculate the total energy for bulk Silicon,
+using a double-zeta basis generated by specifying a given energy-shift::
+        
+  #!/usr/bin/env python
+  from ase import *
+  
+  a0 = 5.43
+  bulk = Atoms('Si2', [(0, 0, 0),
+                       (0.25, 0.25, 0.25)],
+               pbc=True)
+  b = a0 / 2
+  bulk.set_cell([(0, b, b),
+                 (b, 0, b),
+                 (b, b, 0)], scale_atoms=True)
+  
+  calc = Siesta(label='Si',
+                xc='PBE',
+                meshcutoff=200 * Ry,
+                basis='dz',
+                mix=0.01,
+                kpts=[10, 10, 10])
+   
+  calc.set_fdf('PAO.EnergyShift', 0.01 * Ry)
+  bulk.set_calculator(calc)
+  e = bulk.get_potential_energy()
+
+Here, the only input information on the basis set is, that it should
+be double-zeta (``basis='dz'``) and that the confinement potential
+should result in an energy shift of 0.01 Rydberg (the
+``PAO.EnergyShift`` fdf tag). Sometimes it can be necessary to specify
+more information on the basis set. For example, the default basis set
+generation fails for Pt for some versions of Siesta. To fix this, you
+*must* specify the basis set manually. Manual basis set specifications
+are described in `Customized basis-set`_.
+
+
+
+Restarting from an old Calculation
+==================================
+
+If you want to rerun an old SIESTA calculation, made using the ASE
+interface or not, you can set the fdf tag ``UseSaveData`` to
+``True``. This is equivalent to setting both ``DM.UseSaveDM`` and
+``MD.UseSaveXV`` to True, i.e. it will reuse the the density matrix,
+and the atomic coordinates (and unit cell) of the previous
+calculation.  Note that the Siesta jobname (the ``label`` keyword in
+the ASE interface) must be identical to the jobname of the old
+calculation.
diff --git a/doc/ase/calculators/turbomole.rst b/doc/ase/calculators/turbomole.rst
new file mode 100755
index 0000000..01fca52
--- /dev/null
+++ b/doc/ase/calculators/turbomole.rst
@@ -0,0 +1,79 @@
+.. module:: turbomole
+
+=========
+TURBOMOLE
+=========
+
+Introduction
+============
+
+TURBOMOLE_ is a density-functional or Hartree Fock code using 
+atom centered orbitals. This 
+interface makes it possible to use TURBOMOLE_ as a calculator in ASE.
+
+.. _Turbomole: http://www.turbomole.com/
+
+
+
+Environment variables
+=====================
+
+Set environment variables in your configuration file:
+
+- bash::
+
+  $ TURBODIR=/my_disk/my_name/TURBOMOLE
+  $ PATH=$PATH:$TURBODIR/scripts
+  $ PATH=$PATH:$TURBODIR/bin/‘sysname‘
+
+- csh/tcsh::
+
+  $ setenv TURBODIR /my_disk/my_name/TURBOMOLE
+  $ set path=($path ${TURBODIR}/scripts)
+  $ set path=($path ${TURBODIR}/bin/`sysname`)
+
+
+Turbomole Calculator
+==================== 
+
+All usual turbomole input files generated by Turbomole's define 
+[coord, control, basis, (auxbasis), 
+mos/alpha+beta] must be present in the current working directory. 
+
+Turbomole files are updated during the ASE-run. 
+
+Do not use internal coordinates, only cartesians are allowed.
+
+In the coord file one can fix atoms the turbomole way (writing a 'f' in the 
+end of the line in the coord file).
+
+Ase-Turbomole uses turbomole programs 'ridft' and 'rdgrad' 
+if keyword '$ricore' is present in the Turbomole's control file. 
+Otherwise programs 'dscf' and 'grad' are used.
+
+
+Example1: Geometry Optimization
+===============================
+
+
+.. literalinclude:: turbomole_ex1_relax.py
+
+
+Example2: Diffusion run using NEB 
+==================================
+
+
+.. literalinclude:: turbomole_ex2_diffuse_usingNEB.py
+
+
+Example3: Diffusion run using NEB, Restart old calculation
+==========================================================
+
+.. literalinclude:: turbomole_ex3_restart_diffuse_usingNEB.py
+ 
+
+For developers: python files affected by the turbomole interface
+================================================================
+
+.. literalinclude:: turbomole_changes.txt
+
diff --git a/doc/ase/calculators/turbomole_changes.txt b/doc/ase/calculators/turbomole_changes.txt
new file mode 100644
index 0000000..17a7ed5
--- /dev/null
+++ b/doc/ase/calculators/turbomole_changes.txt
@@ -0,0 +1,37 @@
+ase:
+__init__.py
+new lines 17-
+from ase.calculators import LennardJones, \
+    EMT, ASAP, Siesta, Dacapo, Vasp, Turbomole
+
+ase/io:
+__init__.py
+new line 44
+    TURBOMOLE coord file       (filename==='coord')
+new lines 145-
+    if format == 'turbomole':
+        from ase.io.turbomole import read_turbomole
+        return read_turbomole(filename)
+new line 184
+    TURBOMOLE coord file       turbomole
+new lines 267-
+    elif format == 'tmol':
+        from ase.io.turbomole import write_turbomole
+        write_turbomole(filename, images)
+        return
+new lines 349-
+    if lines[0].startswith('$coord'):
+        return 'turbomole'
+
+NEW FILE:
+ase/io/turbomole.py
+
+ase/calculators:
+__init__.py
+new line 10
+    from ase.calculators.turbomole import Turbomole
+
+NEW FILE:
+ase/calculators/turbomole.py
+
+
diff --git a/doc/ase/calculators/turbomole_ex1_relax.py b/doc/ase/calculators/turbomole_ex1_relax.py
new file mode 100755
index 0000000..4d48efa
--- /dev/null
+++ b/doc/ase/calculators/turbomole_ex1_relax.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+import os
+
+from ase.io import read,write
+from ase.optimize import QuasiNewton
+
+# Turbomole input coordinates must be in the file 'coord'.
+# The coordinates are updated to the file 'coord' during the minimization.
+
+
+test = read('coord')
+test.set_calculator(Turbomole())
+
+dyn = QuasiNewton(test, trajectory='test.traj')
+dyn.run(fmax=0.01)
+
+write('coord.final.tmol', test)
+
diff --git a/doc/ase/calculators/turbomole_ex2_diffuse_usingNEB.py b/doc/ase/calculators/turbomole_ex2_diffuse_usingNEB.py
new file mode 100755
index 0000000..0eb18bc
--- /dev/null
+++ b/doc/ase/calculators/turbomole_ex2_diffuse_usingNEB.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+import os
+
+from ase.io import read
+from ase.neb import NEB
+from ase.calculators.turbomole import Turbomole
+from ase.optimize import BFGS
+
+initial = read('initial.coord')
+final = read('final.coord')
+os.system('rm -f coord; cp initial.coord coord')
+
+# Make a band consisting of 5 configs:
+configs = [initial]
+configs += [initial.copy() for i in range(3)]
+configs += [final]
+
+band = NEB(configs, climb=True)
+# Interpolate linearly the positions of the not-endpoint-configs:
+band.interpolate()
+
+#Set calculators
+for config in configs:
+    config.set_calculator(Turbomole())
+
+# Optimize the Path:
+relax = BFGS(band, trajectory='neb.traj')
+relax.run(fmax=0.05)
+
+
diff --git a/doc/ase/calculators/turbomole_ex3_restart_diffuse_usingNEB.py b/doc/ase/calculators/turbomole_ex3_restart_diffuse_usingNEB.py
new file mode 100755
index 0000000..b36f99e
--- /dev/null
+++ b/doc/ase/calculators/turbomole_ex3_restart_diffuse_usingNEB.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+import os
+
+from ase.io import read
+from ase.neb import NEB
+from ase.calculators.turbomole import Turbomole
+from ase.optimize import BFGS
+
+initial = read('initial.coord')
+final = read('final.coord')
+os.system('rm -f coord; cp initial.coord coord')
+
+#restart
+configs = read('neb.traj at -5:')
+
+band = NEB(configs, climb=True)
+
+#Set calculators
+for config in configs:
+    config.set_calculator(Turbomole())
+
+# Optimize:
+relax = BFGS(band, trajectory='neb.traj')
+relax.run(fmax=0.05)
+
diff --git a/doc/ase/calculators/vasp.rst b/doc/ase/calculators/vasp.rst
new file mode 100644
index 0000000..b47a5aa
--- /dev/null
+++ b/doc/ase/calculators/vasp.rst
@@ -0,0 +1,161 @@
+.. module:: vasp
+
+====
+VASP
+====
+
+Introduction
+============
+
+VASP_ is a density-functional theory code using pseudopotentials or 
+the projector-augmented wave method and a plane wave basis set. This 
+interface makes it possible to use VASP_ as a calculator in ASE, and 
+also to use ASE as a post-processor for an already performed VASP_
+calculation.
+
+
+.. _VASP: http://cms.mpi.univie.ac.at/vasp/
+
+
+
+Environment variables
+=====================
+
+You need to write a script called :file:`run_vasp.py` containing
+something like this::
+
+  import os
+  exitcode = os.system('vasp')
+
+The environment variable :envvar:`VASP_SCRIPT` must point to that file.
+
+A directory containing the pseudopotential directories :file:`potpaw` 
+(LDA XC) :file:`potpaw_GGA` (PW91 XC) and :file:`potpaw_PBE` (PBE XC)
+is also needed, and it is to be put in the environment variable
+:envvar:`VASP_PP_PATH`.
+
+Set both environment variables in your shell configuration file:
+
+.. highlight:: bash
+ 
+::
+
+  $ export VASP_SCRIPT=$HOME/vasp/run_vasp.py
+  $ export VASP_PP_PATH=$HOME/vasp/mypps
+
+.. highlight:: python
+
+
+
+VASP Calculator
+=============== 
+
+The default setting used by the VASP interface is
+
+.. class:: Vasp(restart=None, xc='PW91', setups=None, kpts=(1,1,1), gamma=None)
+
+Below follows a list with a selection of parameters
+
+==============  =========  ==============  ============================
+keyword         type       default value   description
+==============  =========  ==============  ============================
+``restart``     ``bool``   None            Restart old calculation or
+                                           use ASE for post-processing
+``xc``          ``str``    ``'PW91'``      XC-functional
+``setups``      ``str``    None            Additional setup option
+``kpts``        *seq*      `\Gamma`-point  **k**-point sampling
+``gamma``       ``bool``   None            `\Gamma`-point centered 
+                                           **k**-point sampling
+``prec``        ``str``                    Accuracy of calculation
+``encut``       ``float``                  Kinetic energy cutoff
+``ediff``       ``float``                  Convergence break condition
+                                           for SC-loop.
+``nbands``      ``int``                    Number of bands
+``algo``        ``str``                    Electronic minimization 
+                                           algorithm
+``ismear``      ``int``                    Type of smearing
+``sigma``       ``float``                  Width of smearing
+``nelm``        ``int``                    Maximum number of
+                                           SC-iterations
+==============  =========  ==============  ============================
+
+*seq*: A sequence of three ``int``'s.
+
+For parameters in the list without default value given, VASP will set 
+the default value. Most of the parameters used in the VASP :file:`INCAR` file 
+are allowed keywords. See the official `VASP manual`_ for more details.
+
+.. _VASP manual: http://cms.mpi.univie.ac.at/vasp/vasp/vasp.html
+
+
+.. note:: 
+   
+   Parameters can be changed after the calculator has been constructed
+   by using the :meth:`~ase.calculators.vasp.Vasp.set` method:
+
+   >>> calc.set(prec='Accurate', ediff=1E-5)
+
+   This would set the precision to Accurate and the break condition for 
+   the electronic SC-loop to ``1E-5`` eV.
+
+
+Spin-polarized calculation
+==========================
+
+If the atoms object has non-zero magnetic moments, a spin-polarized calculation
+will be performed by default.
+
+Here follows an example how to calculate the total magnetic moment of a sodium
+chloride molecule.
+  
+.. literalinclude:: NaCl.py
+
+In this example the initial magnetic moments are assigned to the atoms
+when defining the Atoms object. The calculator will detect that at least
+one of the atoms has a non-zero magnetic moment and a spin-polarized
+calculation will automatically be performed. The ASE generated :file:`INCAR`
+file will look like:
+
+.. literalinclude:: INCAR_NaCl
+
+
+.. note:: 
+   
+   It is also possible to manually tell the calculator to perform a 
+   spin-polarized calculation:
+
+   >>> calc.set(ispin=2)
+
+   This can be useful for continuation jobs, where the initial magnetic 
+   moment is read from the WAVECAR file.
+
+
+Restart old calculation
+=======================
+
+To continue an old calculation which has been performed without the interface
+use the ``restart`` parameter when constructing the calculator
+
+>>> calc = Vasp(restart=True)
+
+Then the calculator will read atomic positions from the :file:`CONTCAR` file,
+physical quantities from the :file:`OUTCAR` file, **k**-points from the
+:file:`KPOINTS` file and parameters from the :file:`INCAR` file. 
+
+.. note::
+
+   Only Monkhorst-Pack and \Gamma-centered **k**-point sampling are supported 
+   for restart at the moment. Some :file:`INCAR` parameters may not be
+   implemented for restart yet. Please report any problems to the ASE mailing
+   list.
+
+The ``restart`` parameter can be used , as the name suggest to continue a job from where a
+previous calculation finished. Furthermore, it can be used to extract data from
+an already performed calculation. For example, to get the total potential energy
+of the sodium chloride molecule in the previous section, without performing any additional
+calculations, in the directory of the previous calculation do:
+
+>>> calc = Vasp(restart=True)
+>>> atoms = calc.get_atoms()
+>>> atoms.get_potential_energy()
+-4.7386889999999999
diff --git a/doc/ase/cmdline.rst b/doc/ase/cmdline.rst
new file mode 100644
index 0000000..3d6d8b3
--- /dev/null
+++ b/doc/ase/cmdline.rst
@@ -0,0 +1,263 @@
+.. highlight:: bash
+
+.. index:: ase
+
+.. _command line tools:
+
+==================
+Command line tools
+==================
+
+The :program:`ase` program can be used to do calculations with
+:ref:`ASE supported calculators<supported calculators>` on the command
+line without having to write a Python script.  The syntax is::
+
+    $ ase [calculator] [task] [options] system(s)
+
+The name of the calculator must be lower case and will default to
+:mod:`EMT <emt>`.  The task must be ``molecule`` or ``bulk``.  There are
+several ways to specify the system or systems to perform the
+calculations on:
+
+* Chemical names: ``H2O`` or ``Fe``.
+  Default :ref:`molecule<molecules-section>` definitions are used.
+* Range of chemical symbols: ``Sc-Zn`` (3d-metals)
+* Special names: ``G2``, ``G2-1`` or ``S22``
+* File names: ``benzene.xyz`` or ``slab.traj``
+
+The exact meaning of these names will depend on the task.
+
+Simple examples::
+
+    $ ase emt H2 --relax=0.01
+    $ ase abinit bulk Si -a 5.5 -p ecut=150 -k 4,4,4
+
+
+Command line options
+====================
+
+General options:
+
+-h, --help          Show help message and exit.
+-t TAG, --tag=TAG   String tag added to filenames.
+-M <M1,M2,...>, --magnetic-moment=<M1,M2,...>
+                    Magnetic moment(s).  Use "-M 1" or "-M 2.3,-2.3".
+-G, --gui           Pop up ASE's GUI.
+-s, --write-summary
+                    Write summary.
+--slice=<start:stop:step>
+                    Select subset of calculations using Python slice
+                    syntax.  Use "::2" to do every second calculation and
+                    ":-5" to do the last five.
+-w FILENAME, --write-to-file=FILENAME
+                    Write configuration to file.
+-i, --interactive-python-session
+                    Run calculation inside interactive Python session.  A
+                    possible $PYTHONSTARTUP script will be imported and
+                    the "atoms" variable refers to the Atoms object.
+-l, --use-lock-files
+                    Skip calculations where the json lock-file or result file
+                    already exists.
+-R FMAX, --relax=FMAX
+                    Relax internal coordinates using L-BFGS algorithm.
+-F <N,x>, --fit=<N,x>
+                    Find optimal bondlength and vibration frequency for
+                    dimer molecules or optimal volume and bulk modulus for
+                    bulk systems using N points and a variation from -x %
+		    to +x % for the bondlength or lattice constants.
+--constrain-tags=<T1,T2,...>
+                    Constrain atoms with tags T1, T2, ...
+-k <K1,K2,K3>, --monkhorst-pack=<K1,K2,K3>
+                    Monkhorst-Pack sampling of BZ.  Example: "4,4,4":
+                    4x4x4 k-points, "4,4,4g": same set of k-points shifted
+                    to include the Gamma point.
+--k-point-density=K_POINT_DENSITY
+                    Density of k-points in Å.
+-p <key=value,...>, --parameters=<key=value,...>
+                    Comma-separated key=value pairs of calculator specific
+                    parameters.
+
+Options specific to the molecule task:
+
+-v VACUUM, --vacuum=VACUUM
+                    Amount of vacuum to add around isolated systems (in
+                    Angstrom).
+--unit-cell=CELL
+                    Unit cell.  Examples: "10.0" or "9,10,11" (in
+                    Angstrom).
+--bond-length=BOND_LENGTH
+                    Bond length of dimer in Angstrom.
+--atomize           Calculate Atomization energies.
+
+Options specific to the bulk task:
+
+-x CRYSTAL_STRUCTURE, --crystal-structure=CRYSTAL_STRUCTURE
+                    Crystal structure.
+-a LATTICE_CONSTANT, --lattice-constant=LATTICE_CONSTANT
+                    Lattice constant in Å.
+--c-over-a=C_OVER_A
+                    c/a ratio.
+-O, --orthorhombic  Use orthorhombic unit cell.
+-C, --cubic         Use cubic unit cell.
+-r REPEAT, --repeat=REPEAT
+                    Repeat unit cell.  Use "-r 2" or "-r 2,3,1".
+
+
+Molecules
+=========
+
+Example::
+
+    $ ase abinit H2 -p ecut=200,xc=LDA -F 5,1 --atomize
+
+This will calculate the energy of a :mol:`H_2` molecule using
+:mod:`Abinit <abinit>` with a planewave cutoff of 200 eV and the LDA
+XC-functional.  A fit using 5 points and a variation of the bond
+length from -1 % to +1 % is made and in addition the energy of a
+single hydrogen atom is also calculated.
+
+Results are written to json files and can be analysed with::
+
+    $ ase abinit H H2 -s
+        name          E       E-E0         d0        hnu         Ea        Ea0
+                     eV         eV        Ang        meV         eV         eV
+          H2    -29.703      0.022      0.770    556.096      4.852      4.873
+           H    -12.426  
+
+
+.. note::
+
+    The json files are simple text files that can be more or less'ed
+    or pretty printed with ``python -m json.tool
+    H2-molecule-abinit.jon``.
+
+
+Bulk systems
+============
+
+Example::
+
+    $ ase bulk Ni Cu Pd Ag Pt Au -F 5,1
+
+Here we used the default EMT potential and the result is::
+
+    $ ase bulk Ni Cu Pd Ag Pt Au -s
+        name          E       E-E0         V0          B
+                     eV         eV      Ang^3        GPa
+          Ni     -0.009      0.005     10.600    175.978
+          Ag      0.002      0.002     16.775    100.151
+          Pt     -0.000      0.000     15.080    278.087
+          Au      0.003      0.003     16.684    173.868
+          Pd      0.000      0.001     14.588    179.105
+          Cu     -0.006      0.001     11.565    134.439
+
+
+More examples
+-------------
+
+Anti-ferromagnetic bcc iron::
+
+    $ ase vasp bulk -x bcc Fe -C -M 2.3,-2.3 -p xc=PBE -k 8,8,8
+
+Bulk silicon (128 atoms, `\Gamma` point only)::
+
+    $ ase abinit bulk Si -r 4,4,4 -k 1,1,1 -a 5.46
+
+Bulk aluminum in orthorhombic cell with LDA and fixed rmt::
+
+    $ ase elk bulk --orthorhombic Al -k 4,4,4 -a 4.05 -p "swidth=0.1,rmt={'Al': 1.9}"
+
+Batch jobs
+==========
+
+Suppose you want to run a large number of similar calculations like
+relaxing the structure of all the molecules in the :ref:`G2-1 database
+<molecular data>`.  You could do that by submitting this job to your
+compute cluster::
+
+    $ ase gpaw G2-1 -v 6.0 -p xc=vdW-DF,h=0.18 -R 0.02
+
+The molecule task will expand ``G2-1`` to a lot of molecules, so it
+makes sense to use :option:`-l` option (:option:`--use-lock-files`)
+and submit the same job many times. A lock file will be created
+for each started calculation and calculations with existing lock file skipped. 
+Moreover the calculations can be run in parallel
+(if parallel version of GPAW is installed)::
+
+    $ mpiexec gpaw-python `which ase` gpaw G2-1 -v 6.0 -p xc=vdW-DF,h=0.18 -R 0.02 -l
+
+
+Making your own tasks
+=====================
+
+FCC clusters with 13 atoms
+--------------------------
+
+Put this in :file:`m13.py`:
+
+.. literalinclude:: m13.py
+    :language: python
+
+Then do this::
+
+    $ ase m13.py Pt -R 0.01
+
+The relaxed EMT bondlength of 2.62 Å can be extracted from the
+created trajectory like this::
+
+    $ ag -tg "e,fmax,d(0,9)" Pt-m13-emt.traj
+    10.9824302706 2.27575724833 2.72
+    10.0805658256 1.45353946744 2.68
+    9.61654400875 0.447140179352 2.64
+    9.57510700742 0.0656434401881 2.6222281202
+    9.57424531861 0.00239771758341 2.62450316817
+
+or like this:
+
+.. highlight:: python
+
+>>> from ase.io import read
+>>> pt = read('Pt-m13-emt.traj')
+>>> pt.get_distance(0, 9)
+2.6245031681662452
+
+
+Convergence test
+----------------
+
+See `convergence.py`_.
+
+.. _convergence.py: https://trac.fysik.dtu.dk/projects/gpaw/browser/trunk/gpaw/
+                    tasks/convergence.py
+
+
+To be done
+==========
+
+* Optimize c/a ratio.
+* Implement different way of cell sampling for eos:
+  [a0 + s for s in [x * np.array(range(- N/2 + 1, N/2 + 1))]]
+  where x is sampling step length, N number of steps.
+  Current way of sampling gives different length of sampling interval
+  depending on the lattice constant guess a0.
+* Write results to file (pickel, csv, cmr (db), traj, ...) per system,
+  together with json file!
+* Split off EnergyTask from Task.
+* Set correct magnetic moments for atoms. DONE
+* Allow setting charges in ase.tasks.task
+* Check occupation numbers and requested total magnetic moments
+  for molecules/atoms. DONE
+* Add --exclude option.
+* Relax cell.
+* Optimize first then fit.
+* Behavior of -w option?
+* Reaction task?
+* Rethink analysis and summary stuff:
+   * it would be nice to calculate for example cohesive energies on 
+     the command line, i.e. using species belonging to different tasks
+   * analysis should be more modular: one may want for example to calculate
+     zpe energies for adsorption systems including molecules and surfaces
+     and print the zpe correction for a given reaction.
+* ase.tasks.main - print complete architecture string in case of error
+  (like in ase/test/__init__.py)
+
diff --git a/doc/ase/constraints.rst b/doc/ase/constraints.rst
new file mode 100644
index 0000000..966823e
--- /dev/null
+++ b/doc/ase/constraints.rst
@@ -0,0 +1,279 @@
+.. module:: constraints
+   :synopsis: Constraining some degrees of freedom
+
+===========
+Constraints
+===========
+
+
+When performing minimizations or dynamics one may wish to keep some
+degrees of freedom in the system fixed. One way of doing this is by
+attaching constraint object(s) directly to the atoms object.
+
+
+The FixAtoms class
+==================
+
+This class is used for fixing some of the atoms.
+
+.. class:: FixAtoms(indices=None, mask=None)
+
+
+
+**XXX positive or negative mask???**
+
+
+
+You must supply either the indices of the atoms that should be fixed
+or a mask. The mask is a list of booleans, one for each atom, being true
+if the atoms should be kept fixed.
+
+Example of use:
+
+>>> c = FixAtoms(mask=[a.symbol == 'Cu' for a in atoms])
+>>> atoms.set_constraint(c)
+
+This will fix the positions of all the Cu atoms in a
+simulation.
+
+
+The FixBondLength class
+=======================
+
+This class is used to fix the distance between two atoms specified by
+their indices (*a1* and *a2*)
+
+.. class:: FixBondLength(a1, a2)
+
+Example of use::
+
+  >>> c = FixBondLength(0, 1)
+  >>> atoms.set_constraint(c)
+
+In this example the distance between the atoms
+with indices 0 and 1 will be fixed in all following dynamics and/or
+minimizations performed on the *atoms* object.
+
+This constraint is useful for finding minimum energy barriers for
+reactions where the path can be described well by a single bond
+length (see the :ref:`mep2` tutorial).
+
+
+
+The FixBondLengths class
+========================
+
+More than one bond length can be fixed by using this class. Especially
+for cases in which more than one bond length constraint is applied on 
+the same atom. It is done by specifying the indices of the two atoms 
+forming the bond in pairs.
+
+.. class:: FixBondLengths(pairs)
+
+Example of use::
+
+  >>> c = FixBondLengths([[0, 1], [0, 2]])
+  >>> atoms.set_constraint(c)
+
+Here the distances between atoms with indices 0 and 1 and atoms with 
+indices 0 and 2 will be fixed. The constraint is for the same purpose 
+as the FixBondLength class. 
+
+The FixedPlane class
+====================
+
+.. autoclass:: ase.constraints.FixedPlane
+
+Example of use: :ref:`constraints_diffusion_tutorial`.
+
+The FixedMode class
+===================
+
+.. autoclass:: ase.constraints.FixedMode
+
+A mode is a list of vectors specifying a direction for each atom. It often comes from :meth:`ase.vibrations.Vibrations.get_mode`.
+
+The BondSpring class
+====================
+
+This constraint applies a Hookean restorative force between two atoms if the distance between them exceeds a threshhold. This is useful to maintain the identity of molecules in quenched molecular dynamics, without changing the degrees of freedom or violating conservation of energy. When the distance between the two atoms is less than the threshhold length, this constraint is completely inactive.
+
+The below example tethers together atoms at index 3 and 4 together::
+
+  >>> c = BondSpring(a1=3, a2=4, threshhold_length=1.79,
+                     springconstant=5.)
+  >>> atoms.set_constraint(c)
+
+Alternatively, this constraint can tether a single atom to a point in space, for example to prevent the top layer of a slab from subliming during a high-temperature MD simulation. An example of tethering atom at index 3 to its original position::
+
+  >>> c = BondSpring(a1=3, a2=atoms[3].position, threshhold_length=0.94,
+                     springconstant=2.)
+  >>> atoms.set_constraint(c)
+
+Reasonable values of the threshhold and spring constant for some common bonds are below.
+
+.. list-table::
+
+  * - Bond
+    - threshhold_length
+    - springconstant
+  * - O-H
+    - 1.40
+    - 5
+  * - C-O
+    - 1.79
+    - 5
+  * - C-H
+    - 1.59
+    - 7
+  * - C=O
+    - 1.58
+    - 10
+  * - Pt sublimation
+    - 0.94
+    - 2
+  * - Cu sublimation
+    - 0.97
+    - 2
+
+The FixInternals class
+======================
+
+This class allows to fix an arbitrary number of bond lengths, angles 
+and dihedral angles. The defined constraints are satisfied self 
+consistently. To define the constraints one needs to specify the 
+atoms object on which the constraint works (needed for atomic 
+masses), a list of bond, angle and dihedral constraints. 
+Those constraint definitions are always list objects containing 
+the value to be set and a list of atomic indices. The epsilon value 
+specifies the accuracy to which the constraints are fullfilled.
+
+.. class:: FixInternals(atoms, bonds=[bond1, bond2], \
+    angles=[angle1], dihedrals=[dihedral1, dihedral2], epsilon=1.e-7)
+
+Example of use::
+
+  >>> bond1 = [1.20, [1, 2]]
+  >>> angle_indices1 = [2, 3, 4]
+  >>> dihedral_indices1 = [2, 3, 4, 5]
+  >>> angle1 = [atoms.get_angle(angle_indices1), angle_indices1]
+  >>> dihedral1 = [atoms.get_dihedral(dihedral_indices1), \
+    dihedral_indices1]
+  >>> c = FixInternals(atoms, bonds=[bonds1], angles=[angles1], \
+    dihedrals=[dihedral1])
+  >>> atoms.set_onstraint(c)
+
+This example defines a bond an angle and a dihedral angle constraint 
+to be fixed at the same time.
+
+
+
+Combining constraints
+=====================
+
+It is possible to supply several constraints on an atoms object. For
+example one may wish to keep the distance between two nitrogen atoms
+fixed while relaxing it on a fixed ruthenium surface::
+
+  >>> pos = [[0.00000, 0.00000,  9.17625],
+  ...        [0.00000, 0.00000, 10.27625],
+  ...        [1.37715, 0.79510,  5.00000],
+  ...        [0.00000, 3.18039,  5.00000],
+  ...        [0.00000, 0.00000,  7.17625],
+  ...        [1.37715, 2.38529,  7.17625]]
+  >>> unitcell = [5.5086, 4.7706, 15.27625]
+
+  >>> atoms = Atoms(positions=pos,
+  ...               symbols='N2Ru4',
+  ...               cell=unitcell,
+  ...               pbc=[True,True,False])
+
+  >>> fa = FixAtoms(mask=[a.symbol == 'Ru' for a in atoms])
+  >>> fb = FixBondLength(0, 1)
+  >>> atoms.set_constraint([fa, fb])
+
+When applying more than one constraint they are passed as a list in
+the :meth:`set_constraint` method, and they will be applied one after
+the other.
+
+
+
+Making your own constraint class
+================================
+
+A constraint class must have these two methods:
+
+.. method:: adjust_positions(oldpositions, newpositions)
+
+   Adjust the *newpositions* array inplace.
+
+.. method:: adjust_forces(positions, forces)
+
+   Adjust the *forces* array inplace.
+
+
+A simple example::
+
+  import numpy as np
+  class MyConstraint:
+      """Constrain an atom to move along a given direction only."""
+      def __init__(self, a, direction):
+          self.a = a
+          self.dir = direction / sqrt(np.dot(direction, direction))
+  
+      def adjust_positions(self, oldpositions, newpositions):
+          step = newpositions[self.a] - oldpositions[self.a]
+          step = np.dot(step, self.dir)
+          newpositions[self.a] = oldpositions[self.a] + step * self.dir
+  
+      def adjust_forces(self, positions, forces):
+          forces[self.a] = self.dir * np.dot(forces[self.a], self.dir)
+
+
+
+
+The Filter class
+================
+
+Constraints can also be applied via filters, which acts as a wrapper
+around an atoms object. A typical use case will look like this::
+
+   -------       --------       ----------
+  |       |     |        |     |          |
+  | Atoms |<----| Filter |<----| Dynamics |
+  |       |     |        |     |          |
+   -------       --------       ----------
+
+and in Python this would be::
+
+  >>> atoms = Atoms(...)
+  >>> filter = Filter(atoms, ...)
+  >>> dyn = Dynamics(filter, ...)
+
+
+This class hides some of the atoms in an Atoms object.
+
+.. class:: Filter(atoms, indices=None, mask=None)
+
+You must supply either the indices of the atoms that should be kept
+visible or a mask. The mask is a list of booleans, one for each atom,
+being true if the atom should be kept visible.
+
+Example of use::
+
+  >>> from ase import Atoms, Filter
+  >>> atoms=Atoms(positions=[[ 0    , 0    , 0],
+  ...                        [ 0.773, 0.600, 0],
+  ...                        [-0.773, 0.600, 0]],
+  ...             symbols='OH2')
+  >>> f1 = Filter(atoms, indices=[1, 2])
+  >>> f2 = Filter(atoms, mask=[0, 1, 1])
+  >>> f3 = Filter(atoms, mask=[a.Z == 1 for a in atoms])
+  >>> f1.get_positions()
+  [[ 0.773  0.6    0.   ]
+   [-0.773  0.6    0.   ]]
+
+In all three filters only the hydrogen atoms are made
+visible.  When asking for the positions only the positions of the
+hydrogen atoms are returned.
+
diff --git a/doc/ase/data.rst b/doc/ase/data.rst
new file mode 100644
index 0000000..5001f9b
--- /dev/null
+++ b/doc/ase/data.rst
@@ -0,0 +1,174 @@
+.. module:: data
+
+===============
+The data module
+===============
+
+
+Atomic data
+===========
+
+This module defines the following variables:
+
+.. data:: atomic_masses
+.. data:: atomic_names
+.. data:: chemical_symbols
+.. data:: covalent_radii
+.. data:: cpk_colors
+.. data:: reference_states
+.. data:: vdw_radii
+
+All of these are lists that should be indexed with an atomic number:
+
+>>> from ase.data import *
+>>> atomic_names[92]
+'Uranium'
+>>> atomic_masses[2]
+4.0026000000000002
+
+
+.. data:: atomic_numbers
+
+If you don't know the atomic number of some element, then you can look
+it up in the :data:`atomic_numbers` dictionary:
+
+>>> atomic_numbers['Cu']
+29
+>>> covalent_radii[29]
+1.1699999999999999
+
+The covalent radii are taken from [Cordeo08]_. 
+The source of the van der Waals radii is given in vdw.py_.
+
+.. [Cordeo08] *Covalent radii revisited*,
+    Beatriz Cordero, Verónica Gómez, Ana E. Platero-Prats, Marc Revés,
+    Jorge Echeverría, Eduard Cremades, Flavia Barragán and Santiago Alvarez,
+    Dalton Trans., 2008, 2832-2838 DOI:10.1039/B801115J 
+
+.. _vdw.py: https://trac.fysik.dtu.dk/projects/ase/browser/trunk/ase/data/vdw.py
+
+.. _molecular data:
+
+Molecular data
+==============
+
+The G1, G2, and G3-databases are available in the :mod:`molecules` module.
+
+Example:
+
+>>> from ase.data.molecules import molecule
+>>> atoms = molecule('H2O')
+
+All molecular members of each database is conveniently contained in a list
+of strings (g1, g2, g3), and one can look up the
+experimental atomization energy for each molecule.
+This is extrapolated from experimental heats of formation at room temperature,
+using calculated zero-point energies and thermal corrections.
+
+Example:
+
+>>> from ase.data.molecules import molecule, g2, get_atomization_energy
+>>> g2[11]
+'H2O'
+>>> atoms = molecule(g2[11]) 
+>>> get_atomization_energy(g2[11])
+232.57990000000001
+>>> from ase.units import kcal,mol
+>>> get_atomization_energy(g2[11])*kcal/mol
+10.08562144637833
+
+where the last line converts the experimental atomization energy of H2O
+from units of kcal/mol to eV.
+
+
+S22, s26, and s22x5 data
+========================
+
+The s22, s26, and s22x5 databases are available in the :mod:`s22` module.
+
+Each weakly bonded complex is identified as an entry in a list of strings
+(s22, s26, s22x5), and is fully created by a 'create'-function:
+
+>>> from ase.data.s22 import s22, create_s22_system
+>>> sys = s22[0]
+>>> sys
+'Ammonia_dimer'
+>>> atoms = create_s22_system(sys)
+>>> atoms.get_chemical_symbols()
+['N', 'H', 'H', 'H', 'N', 'H', 'H', 'H']
+
+The coupled-cluster interaction energies for the s22 and s26 systems
+are retrieved like this:
+
+>>> from ase.data.s22 import s22, get_interaction_energy_s22
+>>> get_interaction_energy_s22(s22[0])
+-0.1375
+
+in units of eV. For s22 these are not the original energies,
+but from more recent work where the same (large) basis set
+was used for all complexes, yielding more accurate
+coupled-cluster interaction energies.
+
+The s22x5 database expands on the original s22 data by introducing
+non-equilibrium geometries for each complex
+(0.9, 1.0, 1.2, 1.5, and 2.0 times original intermolecular distance).
+However, these calculations were done in accordance with the methods
+used in the original s22 work, and so is expected to inherit the
+same problems with mixed basis set sizes.
+Assuming the interaction energy error due to this is the same in all
+5 geometries for each complex, the default s22x5 interaction energies
+are therefore corrected with the energy difference between
+original and newer energies at the original separation.
+
+Example:
+
+>>> from ase.data.s22 import *
+>>> sys1 = s22[0]
+>>> sys1
+'Ammonia_dimer'
+>>> atoms1 = create_s22_system(sys1)
+>>> sys2 = s22x5[0]
+>>> sys2
+'Ammonia_dimer_0.9'
+>>> atoms2 = create_s22_system(sys2)
+>>> sys3 = s22x5[1]
+>>> sys3
+'Ammonia_dimer_1.0'
+>>> atoms3 = create_s22_system(sys3)
+>>> get_interaction_energy_s22(sys1)
+-0.1375
+>>> get_interaction_energy_s22(sys2)
+-0.1375
+>>> get_interaction_energy_s22(sys3)
+-0.1375
+>>> get_interaction_energy_s22x5(sys2)
+-0.10549743024963291
+>>> get_interaction_energy_s22x5(sys3)
+-0.1375
+>>> get_interaction_energy_s22x5(sys3,correct_offset=False)
+-0.1362
+>>> get_interaction_energy_s22x5(sys1,dist=1.0)
+-0.1375
+>>> get_interaction_energy_s22x5(sys1,dist=0.9)
+-0.10549743024963291
+>>> get_interaction_energy_s22x5(sys1,dist=0.9,correct_offset=False)
+-0.1045
+>>> get_number_of_dimer_atoms(sys1)
+[4, 4]
+>>> get_s22x5_distance(sys2)
+-0.25040236345454536
+>>> get_s22x5_distance(sys3)
+0.0
+
+where sys1 is an s22 complex in the original geometry,
+while sys2 and sys3 are two different s22x5 geometries
+of the exact same complex. It is seen that the interaction
+energies for an s22 system and its s22x5 equivalent
+(indexed '_1.0') does not neccesarily match
+when the energy offset-correction is turned off.
+The last two functions are convenience functions,
+giving the number of atoms in the two molecules
+constituting a dimer and the relative intermolecular
+distance in a dimer
+(relative to the '1.0' separation, and in Angstrom),
+respectively.
diff --git a/doc/ase/dft/bader.rst b/doc/ase/dft/bader.rst
new file mode 100644
index 0000000..b74e7b7
--- /dev/null
+++ b/doc/ase/dft/bader.rst
@@ -0,0 +1,116 @@
+==============
+Bader Analysis
+==============
+
+Henkelman *et. al* have implemented a fast and robust algorithm for
+calculating the electronic charges on individual atoms in molecules or
+crystals, based on the Bader partitioning scheme [Bader]_. In that
+method, the analysis is based purely on the electron density. The
+partitioning of the density is determined according to its zero-flux
+surfaces. Details of their implementation can be found in [Tang]_. The
+program is freely available at
+http://theory.cm.utexas.edu/henkelman/research/bader/ where you will
+also find a description of the method.
+
+The algorithm is very well suited for large solid state physical
+systems, as well as large biomolecular systems. The computational time
+depends only on the size of the 3D grid used to represent the electron
+density, and scales linearly with the total number of grid points. As
+the accuracy of the method depends on the grid spacing, it is
+recommended to check for convergnce in this parameter (which should
+usually by smaller than the default value).
+
+The program takes cube input files. It does *not* support units, and
+assumes atomic units for the density (Bohr^-3).
+
+All ase dft calculators have a ``get_pseudo_density`` method, which
+can be used to get the density. A simple python script for making a
+cube file, ready for the Bader program, could be::
+
+  >>> from ase import *
+  >>> density = calc.get_pseudo_density() * Bohr**3
+  >>> write('filename.cube', atoms, data=density)
+
+Some calculators (e.g. gpaw) also have a method called
+``get_all_electron_density``, in which case this is preferable to
+``get_pseudo_density``.
+
+Note that it is strongly recommended to use version 0.26b or higher of
+the program, and the examples below refer to this version.
+
+.. [Bader] R. F. W. Bader.  Atoms in Molecules: A Quantum Theory.
+           Oxford University Press, New York, 1990.
+
+.. [Tang]  W. Tang, E. Sanville, G. Henkelman.
+           A grid-based Bader analysis algorithm without lattice bias.
+           J. Phys.: Compute Mater. 21, 084204 (2009).
+
+
+Example: The water molecule
+---------------------------
+
+The following example shows how to do Bader analysis for a water molecule.
+
+First do a ground state calculation, and save the density as a cube file::
+
+  from ase import *
+  from gpaw import *
+
+  atoms = molecule('H2O', cell=[7.5, 9, 9], calculator=GPAW(h=.17, xc='PBE'))
+  atoms.center()
+  atoms.get_potential_energy()
+
+  rho = atoms.calc.get_all_electron_density(gridrefinement=4) * Bohr**3
+  write('water_density.cube', atoms, data=rho)
+
+Then analyse the density cube file by running (use `bader -h` for a
+description of the possible options)::
+
+  $ bader -p all_atom -p atom_index water_density.cube
+
+This will produce a number of files. The `ACF.dat` file, contains a
+summary of the Bader analysis::
+
+  |     #         X           Y           Z        CHARGE     MIN DIST
+  |  -----------------------------------------------------------------
+  |     1      7.0865      8.5038      9.0672      9.1121      1.3250 
+  |     2      7.0865      9.9461      7.9403      0.4440      0.2834 
+  |     3      7.0865      7.0615      7.9403      0.4440      0.2834 
+  |  -----------------------------------------------------------------
+  |    NUMBER OF ELECTRONS:        9.99999
+
+Revealing that 0.56 electrons have been transfered from each
+Hydrogen atom to the Oxygen atom.
+
+The `BvAtxxxx.dat` files, are cube files for each Bader volume,
+describing the density within that volume. (I.e. it is just the
+original cube file, truncated to zero outside the domain of the
+specific Bader volume).
+
+`AtIndex.dat` is a cube file with an integer value at each grid point,
+describing which Bader volume it belongs to.
+
+The plot below shows the dividing surfaces of the Hydrogen Bader
+volumes. This was achieved by plotting a contour surface of
+`AtIndex.dat` at an isovalue of 1.5.
+
+.. image:: water_divide_surf.png
+   :height: 220 pt
+
+You can attach the output charges from the bader program to the atoms
+for further processing::
+
+  from ase import *
+  from ase.io.bader import attach_charges
+
+  # define the molecule as above
+  atoms = molecule('H2O')
+  atoms.set_cell([7.5, 9, 9])
+  atoms.center()
+
+  # the next two lines are equivalent (only one needed)
+  attach_charges(atoms)
+  attach_charges(atoms, 'ACF.dat')
+
+  for atom in atoms:
+      print 'Atom', atom.symbol, 'Bader charge', atom.charge
diff --git a/doc/ase/dft/dft.rst b/doc/ase/dft/dft.rst
new file mode 100644
index 0000000..e7b98c4
--- /dev/null
+++ b/doc/ase/dft/dft.rst
@@ -0,0 +1,15 @@
+.. module:: dft
+   :synopsis: Tools specific to DFT calculators
+
+
+Density Functional Theory
+=========================
+
+.. toctree::
+   :maxdepth: 2
+
+   kpoints
+   wannier
+   dos
+   stm
+   bader
diff --git a/doc/ase/dft/dos.py b/doc/ase/dft/dos.py
new file mode 100644
index 0000000..d647db8
--- /dev/null
+++ b/doc/ase/dft/dos.py
@@ -0,0 +1,31 @@
+# creates: dos.png
+
+import numpy as np
+
+from ase.dft import DOS
+
+class MyCalc:
+    def get_eigenvalues(self, kpt=0, spin=0):
+        return np.random.uniform(-5.0, 2.0, 90)
+    def get_k_point_weights(self):
+        return [1.0]
+    def get_number_of_spins(self):
+        return 1
+    def get_fermi_level(self):
+        return 0.0
+
+calc = MyCalc()
+dos = DOS(calc, width=0.2)
+d = dos.get_dos()
+e = dos.get_energies()
+
+import matplotlib
+matplotlib.use('Agg')
+import pylab as plt
+plt.figure(figsize=(5, 4))
+plt.plot(e, d)
+plt.xlabel('energy [eV]')
+plt.ylabel('DOS')
+plt.savefig('dos.png')
+
+
diff --git a/doc/ase/dft/dos.rst b/doc/ase/dft/dos.rst
new file mode 100644
index 0000000..422e28c
--- /dev/null
+++ b/doc/ase/dft/dos.rst
@@ -0,0 +1,41 @@
+.. module:: dft.dos
+   :synopsis: Density of states
+
+=================
+Density of states
+=================
+
+Example::
+
+  calc = ...
+  dos = DOS(calc, width=0.2)
+  d = dos.get_dos()
+  e = dos.get_energies()
+
+You can plot the result like this::
+
+  import pylab as plt
+  plt.plot(e, d)
+  plt.xlabel('energy [eV]')
+  plt.ylabel('DOS')
+  plt.show()
+
+.. image:: dos.png
+
+Calculations involving moments of a DOS distribution may be
+facilitated by the use of :func:`~ase.dft.get_distribution_moment`
+method, as in the following example::
+
+  from ase.dft import get_distribution_moment
+  volume = get_distribution_moment(e,d)
+  center, width = get_distribution_moment(e,d,(1,2))
+
+
+More details
+------------
+
+.. autoclass:: ase.dft.dos.DOS
+   :members: get_energies, get_dos
+
+.. automodule:: ase.dft
+   :members: get_distribution_moment
diff --git a/doc/ase/dft/kpoints.py b/doc/ase/dft/kpoints.py
new file mode 100644
index 0000000..07a375b
--- /dev/null
+++ b/doc/ase/dft/kpoints.py
@@ -0,0 +1,13 @@
+# creates: cc.png
+import matplotlib
+matplotlib.use('Agg')
+import numpy as np
+import pylab as plt
+from ase.dft.kpoints import cc162_1x1
+B = [(1, 0, 0), (-0.5, 3**0.5 / 2, 0), (0, 0, 1)]
+k = np.dot(cc162_1x1, B)
+plt.figure(figsize=(5, 4))
+plt.plot(k[:, 0], k[:, 1], 'o')
+plt.savefig('cc.png')
+
+
diff --git a/doc/ase/dft/kpoints.rst b/doc/ase/dft/kpoints.rst
new file mode 100644
index 0000000..7e55af2
--- /dev/null
+++ b/doc/ase/dft/kpoints.rst
@@ -0,0 +1,76 @@
+.. module:: dft.kpoints
+   :synopsis: Brillouin zone sampling
+.. default-role:: math
+
+=======================
+Brillouin zone sampling
+=======================
+
+The **k**-points are always given relative to the basis vectors of the
+reciprocal unit cell.
+
+
+Monkhorst-Pack
+--------------
+
+.. autofunction:: ase.dft.kpoints.monkhorst_pack
+
+The k-points are given as [MonkhorstPack]_:
+
+.. math::
+
+    \sum_{i=1,2,3} \frac{2n_i -N_i - 1}{2N_i} \mathbf{b}_i,
+
+where `n_i=1,2,...,N_i`, ``size`` = `(N_1, N_2, N_3)` and the
+`\mathbf{b}_i`'s are reciprocal lattice vectors.
+
+.. autofunction:: ase.dft.kpoints.get_monkhorst_pack_size_and_offset
+
+Example:
+
+>>> from ase.dft.kpoints import *
+>>> monkhorst_pack((4, 1, 1))
+array([[-0.375,  0.   ,  0.   ],
+       [-0.125,  0.   ,  0.   ],
+       [ 0.125,  0.   ,  0.   ],
+       [ 0.375,  0.   ,  0.   ]])
+>>> get_monkhorst_pack_size_and_offset([[0, 0, 0]])
+(array([1, 1, 1]), array([ 0.,  0.,  0.]))
+
+
+.. [MonkhorstPack]
+    Hendrik J. Monkhorst and James D. Pack:
+    *Special points for Brillouin-zone integrations*,
+    Phys. Rev. B 13, 5188–5192 (1976) 
+
+
+Chadi-Cohen
+-----------
+
+Predefined sets of **k**-points:
+
+.. data:: dft.kpoints.cc6_1x1
+.. data:: dft.kpoints.cc12_2x3
+.. data:: dft.kpoints.cc18_sq3xsq3
+.. data:: dft.kpoints.cc18_1x1
+.. data:: dft.kpoints.cc54_sq3xsq3
+.. data:: dft.kpoints.cc54_1x1
+.. data:: dft.kpoints.cc162_sq3xsq3
+.. data:: dft.kpoints.cc162_1x1
+
+
+Naming convention: ``cc18_sq3xsq3`` is 18 **k**-points for a
+sq(3)xsq(3) cell.
+
+Try this:
+
+>>> import numpy as np
+>>> import pylab as plt
+>>> from ase.dft.kpoints import cc162_1x1
+>>> B = [(1, 0, 0), (-0.5, 3**0.5 / 2, 0), (0, 0, 1)]
+>>> k = np.dot(cc162_1x1, B)
+>>> plt.plot(k[:, 0], k[:, 1], 'o')
+[<matplotlib.lines.Line2D object at 0x9b61dcc>]
+>>> p.show()
+
+.. image:: cc.png
diff --git a/doc/ase/dft/stm.rst b/doc/ase/dft/stm.rst
new file mode 100644
index 0000000..8a69456
--- /dev/null
+++ b/doc/ase/dft/stm.rst
@@ -0,0 +1,89 @@
+.. module:: dft.stm
+
+**XXX This page needs more work!**
+
+STMTool
+-------
+
+The ``STMTool`` class generates Tersoff-Hamann STM topographs.
+
+Here is how you could make a STM picture from the
+`GPAW`_ code, assuming you have allready generated
+the the Al100.gpw GPAW restart file,
+(see the :ref:`STM tutorial<stm-tutorial>` for a full description):
+
+
+>>> from ASE.Utilities.ElectronicStates import ElectronicStates
+>>> from ASE.Utilities.STMTool import STMTool
+>>> from ASE.Visualization.VTK import VTKPlotSTM
+
+>>> loe = ElectronicStates(filename = 'Al100.gpw')
+
+>>> stm = STMTool(loe,contourvalue=1.0e-4, channel="s",
+...               normalaxis=2, smoothfactor=0.1)    # available channels: s,px,py,pz
+
+>>> stmplot = VTKPlotSTM(stm)
+>>> stmplot.set_color_range((3.325,3.55))      # from surface span color range
+>>> stmplot.render()                         # updates picture
+
+
+
+From the planewave code `dacapo`_, the dacapo codes own
+version of the ElectronicStates class must be used:
+
+>>> from Dacapo.ElectronicStates import ElectronicStates
+>>> from ASE.Utilities.STMTool import STMTool
+>>> from ASE.Visualization.VTK import VTKPlotSTM
+
+>>> loe = ElectronicStates(filename='Al100.nc')
+
+From this point the code is identical to the example above.
+
+
+The list of keywords for the STMTool object is:
+
+``listofeigenstates``
+  The ``ElectronicStates`` object holding information about each eigenstate of the slab.
+
+``contourvalue``
+  The density of states at the Fermi level for which the topograph is generated.
+  This argument is mandatory, as no universal (simple) default is natural. Often
+  used values are [1.0e-6 - 1.0e-3], but is very system dependent.
+
+``channel``
+  Selects whether the topograph is generated by the value or gradient isosurface
+  of the Fermi level DOS. This adjusts  for the orbital character of the DOS of
+  the actual STM tip in the probing energy window, i.e. the STM tip states that
+  couples to the substrate. Available options are "s", "px", "py" and "pz".
+  Default is "s".
+
+``normalaxis``
+  The surface normal direction, (0,1,2) for (x,y,z) respectively.
+  normalaxis = 2 is default.
+
+``smoothfactor``
+  The thermal convolution applied to the slab states. It is necessary to apply
+  this due to finite k-point sampling.
+
+
+
+
+STM linescans
+-------------
+
+The class `STMLineScan` can be used to simulate an STM linescan. It must be
+initialized with an instance of the STMTool class.
+
+Note that the linescans are limited to be along one of the axis of
+the unit cell. The origin of the linescan can only assume discrete
+values corresponding to the FFT grid.
+
+The class is used like::
+
+   linescan = STMLineScan(stm,fftnumber=12,axis=1)
+
+See more details in the `linescan section` of the `STM tutorial`.
+
+
+.. _dacapo: http://www.fysik.dtu.dk/campos/Dacapo/
+.. _GPAW: http://wiki.fysik.dtu.dk/gpaw
diff --git a/doc/ase/dft/wannier.rst b/doc/ase/dft/wannier.rst
new file mode 100644
index 0000000..1d3a9c1
--- /dev/null
+++ b/doc/ase/dft/wannier.rst
@@ -0,0 +1,122 @@
+.. module:: dft.wannier
+   :synopsis: Maximally localized Wannier functions
+
+=====================================
+Maximally localized Wannier functions
+=====================================
+
+.. default-role:: math
+
+
+This page describes how to construct the Wannier orbitals using the
+class :class:`Wannier`. The page is organized as follows:
+
+* `Introduction`_: A short summary of the basic theory.
+* `The Wannier class`_ : A description of how the Wannier class is
+  used, and the methods defined within.
+
+Introduction
+============
+
+The point of Wannier functions is the transform the extended Bloch
+eigenstates of a DFT calculation, into a smaller set of states
+designed to facilitate the analysis of e.g. chemical bonding. This is
+achived by designing the Wannier functions to be localized in real
+space instead of energy (which would be the eigen states).
+
+The standard Wannier transformation is a unitary rotation of the Bloch
+states. This implies that the Wannier functions (WF) span the same
+Hilbert space as the Bloch states, i.e. they have the same eigenvalue
+spectrum, and the original Bloch states can all be exactly reproduced
+from a linear combination of the WF. For maximally localized Wannier
+functions (MLWF), the unitary transformation is chosen such that the
+spread of the resulting WF is minimized.
+
+The standard choice is to make a unitary transformation of the
+occupied bands only, thus resulting in as many WF as there are
+occupied bands. If you make a rotation using more bands, the
+localization will be improved, but the number of wannier functions
+increase, thus making orbital based analysis harder.
+
+The class defined here allows for construction of *partly* occupied
+MLWF. In this scheme the transformation is still a unitary rotation
+for the lowest states (the *fixed space*), but it uses a dynamically
+optimized linear combination of the remaining orbitals (the *active
+space*) to improve localization. This implies that e.g. the
+eigenvalues of the Bloch states contained in the fixed space can be
+exactly reproduced by the resulting WF, whereas the largest
+eigenvalues of the WF will not necessarily correspond to any "real"
+eigenvalues (this is irrelevant, as the fixed space is usually chosen
+large enough, i.e. high enough above the fermilevel, that the
+remaining DFT eigenvalues are meaningless anyway).
+
+For the theory behind this method see the paper "Partly Occupied
+Wannier Functions" Thygesen, Hansen and Jacobsen, *Phys. Rev. Lett*,
+Vol. **94**, 26405 (2005).
+
+
+The Wannier class
+=================
+
+Usual invocation::
+
+  from ase.dft import Wannier
+  wan = Wannier(nwannier=18, calc=GPAW('save.gpw'), fixedstates=15)
+  wan.localize() # Optimize rotation to give maximal localization
+  wan.save('file.pickle') # Save localization and rotation matrix
+  
+  # Re-load using saved wannier data
+  wan = Wannier(nwannier=18, calc=calc, fixedstates=15, file='file.pickle')
+
+  # Write a cube file
+  wan.write_cube(index=5, fname='wannierfunction5.cube')
+  
+For examples of how to use the **Wannier** class, see the `Wannier tutorial`_.
+
+.. _Wannier tutorial: https://wiki.fysik.dtu.dk/ase/tutorials/wannier.html
+
+.. autoclass:: ase.dft.wannier.Wannier
+   :members:
+
+In Dacapo, the inialwannier keyword can be a list as described below:
+
+    Setup an initial set of Wannier orbitals.
+    *initialwannier* can set up a starting guess for the Wannier
+    functions.  This is important to speed up convergence in
+    particular for large systems For transition elements with **d**
+    electrons you will always find 5 highly localized **d**-orbitals
+    centered at the atom.  Placing 5 **d**-like orbitals with a radius
+    of 0.4 Angstroms and center at atom no. 7, and 3 **p**-like
+    orbitals with a radius of 0.4 Angstroms and center at atom no. 27
+    looks like this::
+
+       initialwannier = [[[7],2,0.4],[[27],1,0.4]]
+
+    Placing only the l=2, m=-2 and m=-1 orbitals at atom no. 7 looks
+    like this::
+
+       initialwannier = [[[7],2,-2,0.4],[[7],2,-1,0.4]]
+
+    I.e. if you do not specify the m quantum number all allowed values
+    are used.  Instead of placing an orbital at an atom, you can place
+    it at a specified position. For example the following::
+
+       initialwannier = [[[0.5,0.5,0.5],0,0.5]]
+
+    places an **s** orbital with radius 0.5 Angstroms at the position
+    (0.5, 0.5, 0.5) in scaled coordinates of the unit cell.
+
+.. note:: For calculations using **k**-points, make sure that the
+   `\Gamma`-point is included in the **k**-point grid.
+   The Wannier module does not support **k**-point reduction by symmetry, so
+   you must use the ``usesymm=False`` keyword in the calc, and
+   shift all **k**-points by a small amount (but not less than 2e-5
+   in) in e.g. the x direction, before performing the calculation.
+   If this is not done the symmetry program will still use time-reversal
+   symmetry to reduce the number of **k**-points by a factor 2.
+   The shift can be performed like this::
+
+     from ase import *
+     kpts = monkhorst_pack((15, 9, 9)) + [2e-5, 0, 0]
+
+.. default-role::
diff --git a/doc/ase/general_surface.py b/doc/ase/general_surface.py
new file mode 100644
index 0000000..20daa58
--- /dev/null
+++ b/doc/ase/general_surface.py
@@ -0,0 +1,41 @@
+# creates: s1.png s2.png s3.png s4.png general_surface.pdf
+from ase.lattice.surface import surface
+s1 = surface('Au', (2, 1, 1), 9)
+s1.center(vacuum=10, axis=2)
+
+from ase.lattice import bulk
+Mobulk = bulk('Mo', 'bcc', a=3.16, cubic=True)
+s2 = surface(Mobulk, (3, 2, 1), 9)
+s2.center(vacuum=10, axis=2)
+
+a = 4.0
+from ase import Atoms
+Pt3Rh = Atoms('Pt3Rh',
+              scaled_positions=[(0, 0, 0),
+                                (0.5, 0.5, 0),
+                                (0.5, 0, 0.5),
+                                (0, 0.5, 0.5)],
+              cell=[a, a, a],
+              pbc=True)
+s3 = surface(Pt3Rh, (2, 1, 1), 9)
+s3.center(vacuum=10, axis=2)
+
+Pt3Rh.set_chemical_symbols('PtRhPt2')
+s4 = surface(Pt3Rh , (2, 1, 1), 9)
+s4.center(vacuum=10, axis=2)
+
+from ase.io import write
+for atoms, name in [(s1, 's1'), (s2, 's2'), (s3, 's3'), (s4, 's4')]:
+    write(name + '.pov', atoms,
+          rotation='-90x',
+          show_unit_cell=2,
+          transparent=False,
+          display=False,
+          run_povray=True)
+    
+import os
+
+for i in range(2):
+    error = os.system('pdflatex -interaction=nonstopmode general_surface ' +
+                      '> /dev/null')
+    assert error == 0, 'pdflatex failed'
diff --git a/doc/ase/general_surface.tex b/doc/ase/general_surface.tex
new file mode 100644
index 0000000..a107877
--- /dev/null
+++ b/doc/ase/general_surface.tex
@@ -0,0 +1,295 @@
+\documentclass[11pt]{article} % use larger type; default would be 10pt
+\usepackage[utf8]{inputenc} % set input encoding (not needed with XeLaTeX)
+\usepackage{graphicx} % support the \includegraphics command and options
+\usepackage{color}
+\usepackage{array} % for better arrays (eg matrices) in maths
+\usepackage{verbatim} % adds environment for commenting out blocks of text & fo
+\title{Theory and implementation behind: \\Universal surface creation - smallest unitcell}
+\author{Bjarke Brink Buus, Jakob Howalt \& Thomas Bligaard}
+\begin{document}
+\maketitle
+\section{Construction of surface slabs}
+The aim for this part of the project is to create all possible surfaces with a given Miller Indice and the conventional bulk structure. In this section, the theory behind the construction of surface slabs will be outlined and from this fundament an implementation has been developed in Python. This implementation, will be able to create any surface for any type of structure including following common bulk structures - the simple cubic unit cell, the body centered cubic unit cell, the face centered cubic unit cell and the hexagonal close packed unit cell.
+%TODO skal dette med: The theory and implementation, however, should work for any unit cell.
+%In this implementation, we will consider all three cases of bulk structures - the body centered cubic unit cell (bcc), the face centered cubic unit cell (ffc) and the hexagonal close packed unit cell (hcp). 
+\subsection{Theory}
+By introducing both the real and the reciprocal lattice spaces most pieces of the puzzle of creating any surface is derived. In addition some integer mathmatics will be used.
+\subsubsection{Real lattice space}
+First, we will start by defining the system in real space. We have three basis vectors that span the crystal lattice in the conventional unit cell ($\vec{a}_1,\vec{a}_2,\vec{a}_3$). These three vectors do not have to be orthogonal and the procedure will therefore also work for hcp structures. Additionally, the lengths of the vectors will not in all cases be the same, so the theoretical approach to this problem, will involve three independent lengths. For most bulk structures there will only be one or two different lattice constants determining the unit cell due to symmetry, for instance the L10 and L12 alloys as mentioned in section XXX method XXX. The unit cell can be seen in drawing 1 with the lengths and directions.
+
+\setlength{\unitlength}{2.5cm}
+\begin{picture}(1,1)(-0.65,0.05)
+\thicklines
+\put(0.1,0.1){\line(0,1){0.7}}
+\put(0.1,0.1){\line(1,0){0.7}}
+\put(0.1,0.8){\line(1,0){0.7}}
+\put(0.8,0.1){\line(0,1){0.7}}
+\put(0.8,0.1){\line(4,1){0.4}}
+\put(0.1,0.8){\line(4,1){0.4}}
+\put(0.8,0.8){\line(4,1){0.4}}
+\put(0.5,0.9){\line(1,0){0.7}}
+\put(1.2,0.2){\line(0,1){0.7}}
+\put(2.45,0.4){\vector(0,1){0.3}}
+\put(2.45,0.4){\vector(1,0){0.3}}
+\put(2.45,0.4){\vector(-4,-1){0.2}}
+\put(2.4,0.75){$\vec{a}_3$}
+\put(2.1,0.32){$\vec{a}_1$}
+\put(2.8,0.35){$\vec{a}_2$}
+\thinlines
+\put(1.235,0.5){$a_3$}
+\put(0.45,0.0){$a_2$}
+\put(1.02,0.06){$a_1$}
+\put(0.5,0.2){\line(1,0){0.7}}
+\put(0.5,0.2){\line(0,1){0.7}}
+\put(0.1,0.1){\line(4,1){0.4}}
+\end{picture}\\
+Drawing 1: \textit{This drawing shows the basis vectors and sizes for the system.}\\
+
+
+A surface is defined by its Miller Indice (h,k,l), where $h$, $k$ and $l$ all are integers, which in real space can be described by the crystal planes that are parallel to the plane that intersects the basis vectors ($\vec{a}_1,\vec{a}_2,\vec{a}_3$) at 
+\begin{eqnarray}
+\frac{1}{h}\vec{a}_1, \ \frac{1}{k}\vec{a}_2, \ \frac{1}{l}\vec{a}_3. \nonumber
+\end{eqnarray}
+The Miller Indices are used for all four types of structures sc, bcc, fcc and hcp. 
+In case of one or more of the Miller Indice (h,k,l) is zero, the plane does not intersect with the corresponding axis. For instance, if k is equal to zero, the normal vector to the plane, defined by (h,0,l), will be orthogonal to $\vec{a}_2$. A (0,0,0) Miller Indice is unphysical and hence will not be included in the theory section, however a part of the implementation code will notify the user that the chosen surface is not possible to create. \\ 
+%Normally, a plane can be defined as $b_1 x + b_2 y + b_3 z = d$. Here the normal vector will be ($b_1,b_2,b_3$) and $d$ will be a constant. If a point ($x_0,y_0,z_0$) in the plane is known, $d$ will equal to
+%\begin{eqnarray}
+%d = b_1 x_0 + b_2 y_0 + b_3 z_0 \nonumber
+%\end{eqnarray} 
+
+\subsubsection{Reciprocal lattice space}
+For the full understanding of the lattice construction, it is very usefull to introduce the reciprocal vector space. The basis vectors in the reciprocal lattice space are given by,
+\begin{eqnarray}
+\vec{b}_1 = \frac{\vec{a}_2 \times \vec{a}_3}{\vec{a}_1 \cdot (\vec{a}_2 \times \vec{a}_3)}, \ \vec{b}_2 = \frac{\vec{a}_3 \times \vec{a}_1}{\vec{a}_2 \cdot (\vec{a}_3 \times \vec{a}_1)}, \ \vec{b}_3 = \frac{\vec{a}_1 \times \vec{a}_2}{\vec{a}_3 \cdot (\vec{a}_1 \times \vec{a}_2)} 
+\end{eqnarray}
+% The geometry of the three reciprocal lattice vectors is as follows, $\vec{b}_1$ is orthogonal with both $\vec{a}_2$ and $\vec{a}_3$, because of the cross product in the construction of $\vec{b}_1$. The same follows for the other reciprocal lattice vectors, $\vec{b}_2 \ \perp \ (\vec{a}_1, \ \vec{a}_3)$ and $\vec{b}_3 \ \perp \ (\vec{a}_1, \ \vec{a}_2)$. In a more compact form, this can be written as
+\begin{eqnarray}
+\vec{a}_i \cdot \vec{b}_j = \delta_{ij}  \label{orthogonality}
+\end{eqnarray}
+When introducing the reciprocal lattice vectors, the normal vector for a given surface plane with the Miller Indices (hkl) is given by 
+\begin{eqnarray}
+\vec{n} = h \vec{b}_1 + k \vec{b}_2 + l \vec{b}_3 \label{hklnormalvector}
+\end{eqnarray}
+
+
+\setlength{\unitlength}{4.4cm}
+\begin{picture}(2,1.2)
+\thicklines
+\put(0.04,0.04){\line(0,1){0.64}} \put(0.04,0.04){\line(1,0){0.765}}
+\put(0.805,0.04){\line(0,1){0.64}} \put(0.04,0.68){\line(1,0){0.765}}
+
+\put(0.04,0.36){\line(1,0){0.765}}
+
+\put(0.4225,0.04){\line(0,1){0.64}} \put(1.002,0.2){\line(0,1){0.64}}
+
+\put(0.24,0.84){\line(1,0){0.765}}
+
+\put(0.805,0.04){\line(5,4){0.195}} \put(0.805,0.68){\line(5,4){0.195}}
+\put(0.805,0.36){\line(5,4){0.195}} \put(0.04,0.68){\line(5,4){0.195}}
+\put(0.4225,0.68){\line(5,4){0.195}}
+
+\thinlines
+\put(0.24,0.52){\line(1,0){0.765}} \put(0.04,0.36){\line(5,4){0.2}}
+\put(0.4225,0.36){\line(5,4){0.2}} \put(0.4225,0.04){\line(5,4){0.2}}
+\put(0.24,0.2){\line(1,0){0.765}} \put(0.24,0.2){\line(0,1){0.64}}
+\put(0.04,0.04){\line(5,4){0.2}} \put(0.6175,0.20){\line(0,1){0.64}}
+\thicklines
+\color{red}
+\put(0.04,0.04){\line(1,4){0.2}} \put(0.24,0.84){\line(6,-5){0.762}}
+\put(0.04,0.04){\line(6,1){0.962}}
+\color{black}
+\put(0.085,0.535){$\vec{t}_3$}
+\put(0.70,0.07){$\vec{t}_1$}
+\put(0.55,0.6){$\vec{t}_2$}
+
+\thinlines
+\put(1.2,0.68){\line(1,0){0.765}} \put(1.002,0.52){\line(5,4){0.2}}
+\put(1.3845,0.52){\line(5,4){0.2}} \put(1.3845,0.2){\line(5,4){0.2}}
+\put(1.202,0.36){\line(1,0){0.765}} \put(1.2,0.36){\line(0,1){0.64}}
+\put(1.002,0.2){\line(5,4){0.2}} \put(1.5852,0.360){\line(0,1){0.64}}
+\thicklines
+\color{red}
+\thinlines
+\put(0.24,0.84){\line(6,1){0.962}}
+\put(1.002,0.2){\line(1,4){0.2}} 
+\put(1.202,1){\line(6,-5){0.762}}
+\put(1.002,0.2){\line(6,1){0.962}}
+\color{black}
+\thicklines
+
+\thinlines
+\put(0.44,0.68){\line(1,0){0.765}} \put(0.24,0.52){\line(5,4){0.195}}
+\put(0.6225,0.52){\line(5,4){0.195}} \put(0.6225,0.2){\line(5,4){0.195}}
+\put(0.44,0.36){\line(1,0){0.765}} \put(0.44,0.36){\line(0,1){0.64}}
+\put(0.24,0.2){\line(5,4){0.195}} \put(0.8175,0.36){\line(0,1){0.64}}
+\thicklines
+\put(0.04,0.04){\line(0,1){0.64}} \put(0.04,0.04){\line(1,0){0.765}}
+\put(0.805,0.04){\line(0,1){0.64}} \put(0.04,0.68){\line(1,0){0.765}}
+
+\put(1.002,0.2){\line(1,0){0.765}}
+\put(1.765,0.2){\line(0,1){0.64}} \put(1.002,0.84){\line(1,0){0.765}}
+\put(1.002,0.52){\line(1,0){0.765}}
+\put(1.3845,0.2){\line(0,1){0.64}} \put(1.964,0.36){\line(0,1){0.64}}
+\put(1.2,1.0){\line(1,0){0.765}}
+\put(1.765,0.2){\line(5,4){0.195}} \put(1.765,0.84){\line(5,4){0.195}}
+\put(1.765,0.52){\line(5,4){0.195}} \put(1.002,0.84){\line(5,4){0.195}}
+\put(1.3845,0.84){\line(5,4){0.195}}
+\put(0.04,0.36){\line(1,0){0.765}}
+
+\put(0.4225,0.04){\line(0,1){0.64}} \put(1.002,0.2){\line(0,1){0.64}}
+\put(0.44,1){\line(1,0){0.765}}
+\put(0.805,0.04){\line(5,4){0.195}} \put(0.805,0.68){\line(5,4){0.195}}
+\put(0.805,0.36){\line(5,4){0.195}} \put(0.24,0.84){\line(5,4){0.195}}
+\put(0.6225,0.84){\line(5,4){0.195}}
+
+\put(2.3,0.4){\vector(0,1){0.3}}
+\put(2.3,0.4){\vector(1,0){0.3}}
+\put(2.3,0.4){\line(-5,-4){0.15}}
+\put(2.155,0.2847){\vector(-1,-1){0.025}}
+\put(2.25,0.75){$\vec{a}_3$}
+\put(2.05,0.26){$\vec{a}_1$}
+\put(2.63,0.35){$\vec{a}_2$}
+\label{unik}
+\end{picture}
+\setlength{\unitlength}{4cm}
+\begin{picture}(2,1.2)(-0.65,0.05)
+\put(1.5,0.4){\vector(0,1){0.3}}
+\put(1.5,0.4){\vector(1,0){0.3}}
+\put(1.5,0.4){\line(-5,-4){0.15}}
+\put(1.355,0.2847){\vector(-1,-1){0.025}}
+\put(1.45,0.75){$\vec{a}_3$}
+\put(1.2,0.32){$\vec{a}_1$}
+\put(1.9,0.35){$\vec{a}_2$}
+\end{picture}
+Drawing 2: \textit{This drawing shows a surface with Miller Indices (2,1,1). The repetition of a lattice point is shown, and the vectors spanning this surface can be found. }\\
+
+Furthermore, a desired surface with a normal vector $\vec{n}$, drawing 2 shows that for a set of non-zero Miller indices, three vectors, will be noted $\vec{t}_{1,2,3}$, in the plane can easily be found. Two vectors, linearly independent ofcourse, created by a linear combination of the vectors $\vec{t}_{1,2,3}$, can span the desired surface. 
+These vectors are given by 
+\begin{eqnarray}
+\vec{t}_1 = -\frac{1}{h}\vec{a}_1+\frac{1}{k}\vec{a}_2 ,\vec{t}_2 = \frac{1}{k}\vec{a}_2 - \frac{1}{l}\vec{a}_3, \vec{t}_3 = \frac{1}{h}\vec{a}_1 - \frac{1}{l}\vec{a}_3 \nonumber
+\end{eqnarray}
+but it should be noted that the anti-parallel versions of $\vec{t}_{1,2,3}$ also can be used. Since $h$, $k$ and $l$ all are integers, we can multiply with the product $hkl$ and divide with the indice, which will be common for each of the lattice vectors, $\vec{a}_i$, with respect to both of the lattice vectors $\vec{a}_i$, so a new set of vectors end up being,
+\begin{eqnarray}
+\vec{t}_1 = k\vec{a}_1-h\vec{a}_2 ,\ \vec{t}_2 = l\vec{a}_1 - h\vec{a}_3, \ \vec{t}_3 = l\vec{a}_2 - k\vec{a}_3  
+\end{eqnarray}
+For the special cases of two of the Miller Indices being zero, it is a very straightforward, to see that the appropiate vectors to span the normal vector, will be the corresponding basis vectors in real space. If for instance, h and k both are zero, it will result in a choice of $\vec{v}_1$ $=$ $\vec{a}_1$ and $\vec{v}_2$ $=$ $\vec{a}_2$.
+
+
+\subsubsection{Determination of the two surface vectors}
+Having introduced the real space and the reciprocal space, most of the theory is available and hence the determination of the two vectors that span the surface with respect to a given Miller Indice is possible. 
+
+The simple lattice points $r_{i,j,m}$ are placed at $\vec{r}_{i,j,m} = i\vec{a}_1+j\vec{a}_2+k\vec{a}_3$ where $\ i,j,m$ all are integers. Because of the arrangement of the lattice points, not all surface planes will go through these points. The dot product between the normal vector $\vec{n}$ and the lattice points $\vec{r}_{i,j,m}$ gives, 
+\begin{eqnarray}
+hi+kj+lm = d \nonumber
+\end{eqnarray}
+and since all constants are integers, $d$ must also be an integer and therefore the values of $d$ has been quantized. This equation is a 'Linear Diophantine Equation' and the smallest $d$ for which there exist an non-zero solution ($i,j,m$) when ($h,k,l$) are non-zero is, accordingly to 'Bezouts Identity', when $d$ is the smallest common divisor of $h$, $k$, and $l$. If one or two of the Miller Indices is zero, the identity is true, but only when choosing the largest common divisor for the non-zero parts of ($h,k,l$). If a Miller indice has a common divisor $e >$ 1, the non-zero components of the Miller Indice can then be reduced with $\frac{1}{e}(h,k,l)$, and still define the same surface. \\
+A solution will therefore exist for 
+\begin{eqnarray}
+hi+kj+lm = d 
+\end{eqnarray}
+and because of the reduction of the normal vector, the value for $d$ will be $\pm$1. The two surface vectors, must obey the fact that they are orthogonal with respect to the normal vector $\vec{n}$,
+\begin{eqnarray}
+\vec{v}_{1,2} \cdot \vec{n} = 0. \label{skalarnul}
+\end{eqnarray}
+Because of this, the cross product between the two surface vectors $\vec{v}_1$ and $\vec{v}_2$ must give a constant times the normal vector $\vec{n}$. The constant must be as small as possible, still non zero, because the area spanned by $\vec{v}_1$ and $\vec{v}_2$ is equal to the length of the cross product. And since the new normal vector is the smallest possible, the constant must be $\pm 1$. \\
+Consider a choice of the two surface vectors, $\vec{t}_1$ and $\vec{t}_3$. They will both fullfil equation \ref{skalarnul}, however the crossproduct between $\vec{t}_1$ and $\vec{t}_3$ will be,
+\begin{eqnarray}
+\vec{t}_1 \times \vec{t}_3 = \left( \begin{array}{c} k \\ - h \\ 0 \end{array} \right) \times \left( \begin{array}{c} 0 \\ l \\ -k \end{array} \right) = \left( \begin{array}{c} kh \\ k^2 \\ kl \end{array} \right) = k \left( \begin{array}{c} h \\  k \\ l \end{array} \right).\nonumber
+\end{eqnarray}
+Unless the size of $k$ is $\pm 1$, the size of the surface area spanned by $\vec{t}_1$ and $\vec{t}_3$ will be too big. It is therefore crucial to introduce a completely new linear combination of the three vectors $\vec{t}_1$, $\vec{t}_2$ and $\vec{t}_3$. A linear combination of $\vec{t}_1$ and $\vec{t}_2$ fullfill the same requirements when $p$ and $q$ are integers in
+%new way of combining these vectors is presented and it is also shown that this combination fullfil the previous requirements.
+\begin{eqnarray}
+\left( p \left( \begin{array}{c} k \\ -h \\ 0 \end{array} \right) + q \left( \begin{array}{c} l \\ 0 \\ -h \end{array} \right) \right) \times \left( \begin{array}{c} 0 \\ l \\ -k \end{array} \right) \Rightarrow (pk + ql) \left( \begin{array}{c} h \\ k \\ l \end{array} \right) = \left( \begin{array}{c} h \\ k \\ l \end{array} \right) \label{hkldependency}
+\end{eqnarray}
+This leaves a more simple equation to solve, 
+\begin{eqnarray}
+(pk + ql) = 1 \label{EquationFromTheory}
+\end{eqnarray}
+The solution to this equation can be found using the Extended Euclidean Algorithm to determine the unknowns integers, $p$ and $q$. The two new vectors, which span the surface are described
+\begin{eqnarray}
+\vec{v}_1 = p \left( \begin{array}{c} k\vec{a}_1 \\ - h \vec{a}_2 \\ 0 \end{array} \right) + q \left( \begin{array}{c} l \vec{a}_1 \\ 0 \\ - h \vec{a}_3 \end{array} \right), \ \vec{v}_2 = \left( \begin{array}{c} 0 \\ l\vec{a}_2 \\ -k\vec{a}_3 \end{array} \right) \label{v2formalism}
+\end{eqnarray}
+However, there are infinite possible solutions for $p$ and $q$ but some of the solutions are better than others, in relation to visualizing the surface. Therefore another criteria is implemented. The closer to being orthogonal the surface vectors are, the easier it becomes to apply adsorbates onto the the surface. The procedure for this will be explained in section \ref{implementation}, but the theory will be explained here. The solution for $p$ and $q$ can be chosen to accomodate this with respect to an integer, $c$, by
+\begin{eqnarray}
+\vec{v}_1 = (p+cl)\left( \begin{array}{c} k\vec{a}_1 \\ - h \vec{a}_2 \\ 0 \end{array} \right) + (q-ck)\left( \begin{array}{c} l \vec{a}_1 \\ 0 \\ - h \vec{a}_3 \end{array} \right) \label{v1formalism}
+\end{eqnarray}
+This change of vector $\vec{v}_1$, does not change the crossproduct between $\vec{v}_1$ and $\vec{v}_2$, as shown in equation \ref{hkldependency}, because the cross product between the changes and $\vec{v}_2$ is zero. This is shown below
+\begin{eqnarray}
+\left(cl \left( \begin{array}{c} k\vec{a}_1 \\ - h \vec{a}_2 \\ 0 \end{array} \right) - ck\left( \begin{array}{c} l \vec{a}_1 \\ 0 \\ - h \vec{a}_3 \end{array} \right) \right) \times \left( \begin{array}{c} 0 \\ l\vec{a}_2 \\ -k\vec{a}_3 \end{array} \right) & = & \nonumber \\  
+ \left( ckl \left( \begin{array}{c} h \\ k \\ l \end{array} \right) - ckl\left( \begin{array}{c} h \\ k \\ l \end{array} \right) \right) &  = & 0
+\end{eqnarray}
+This change of $\vec{v}_1$ results in an algorithm, which will be presented later, to determine the most appropriate choice of vectors.
+
+\subsubsection{Finding the 3$^{rd}$ vector for the new unit cell}
+After determining the two vectors spanning the surface ($\vec{v}_1,\vec{v}_2$), the third basis vector($\vec{v}_3$) of the surface slab can be found. This vector does not need to be orthogonal to the two surface vectors. The vector will go from one lattice point to its repeated lattice point another place in the structure. This means that the same contraints apply to this vector as for $\vec{v}_1$ and $\vec{v}_2$, but some additional constraints will be added. The vector will have to be an integer linear combination of the three original lattice vectors ($\vec{a}_1, \vec{a}_2, \vec{a}_3$) and have the coordinates ($i_3\vec{a}_1,j_3\vec{a}_2,m_3\vec{a}_3$). In addition $\vec{v}_3$ cannot be orthogonal to the surface normal, so $\vec{v}_3\cdot\vec{n}\neq 0$. \\
+To find the integers $i_3, j_3$ and $m_3$ by calculating the dot product using normalvector of the surface from equation \ref{hklnormalvector}, and the definitions of the reciprocal vectors ($\vec{b}_1,\vec{b}_2,\vec{b}_3$):
+\begin{eqnarray}
+ \vec{n} \cdot \vec{v}_3 & = (h \vec{b}_1 + k \vec{b}_2 + l \vec{b}_3 ) \cdot (i_3\vec{a}_1 + j_3\vec{a}_2 + m_3\vec{a}_3) \nonumber \\
+ & = hi_3+kj_3+lm_3 = d \label{vdotn},
+\end{eqnarray}
+where d must be a non-zero integer because all of $h,k,l,i_3,j_3$ and $m_3$ are integers. It will now be shown that $d = 1$.\\
+Defining the volume of the conventional unit cell to be V spanned by the conventional basis $\vec{a}_1, \vec{a}_2$ and $\vec{a}_3$.
+\begin{eqnarray}
+ V = (\vec{a}_1 \times \vec{a}_2) \cdot \vec{a}_3
+\end{eqnarray}
+and rewriting of the three reciprocal vectors to the following form
+\begin{eqnarray}
+V\vec{b}_1 = \vec{a}_2 \times \vec{a}_3, \ V\vec{b}_2 = \vec{a}_3 \times \vec{a}_1, \ V\vec{b}_3 = \vec{a}_1 \times \vec{a}_2. 
+\end{eqnarray}
+will ease the calculations, and with all the pieces set, a determination of the value of $d$ is possible. The volume of the cell spanned by ($\vec{v}_1,\vec{v}_2,\vec{v}_3$) is presented below, where the constants ($i$,$j$,$m$)$_{1,2}$ refer to the constants defined by the formalism used for $\vec{v}_1$, equation \ref{v1formalism}, and for $\vec{v}_2$, equation \ref{v2formalism}.
+\begin{eqnarray}
+V = & \vec{v}_3 \cdot (\vec{v}_1 \times \vec{v}_2) = \vec{v}_3 \cdot \left\lbrace ( i_1\vec{a}_1+j_1\vec{a}_2+m_1\vec{a}_3) \times ( i_2\vec{a}_1+j_2\vec{a}_2+m_2\vec{a}_3) \right\rbrace \nonumber \\
+= & \vec{v}_3 \cdot \left\lbrace i_1 j_2 \vec{a}_1 \times \vec{a}_2 + i_1 m_2 \vec{a}_1 \times \vec{a}_3 + j_1 i_2 \vec{a}_2 \times \vec{a}_1 + j_1 m_2 \vec{a}_2 \times \vec{a}_3 \right\rbrace \nonumber \\ & + \vec{v}_3 \cdot \left\lbrace m_1 i_2 \vec{a}_3 \times \vec{a}_1 + m_1 j_2 \vec{a}_3 \times \vec{a}_2 \right\rbrace \nonumber  \\
+= & V\vec{v}_3 \cdot \left\lbrace i_1 j_2 \vec{b}_3 - i_1 m_2 \vec{b}_2 - j_1 i_2 \vec{b}_3 + j_1 m_2 \vec{b}_1 + m_1 i_2 \vec{b}_2 - m_1 j_2 \vec{b}_1 \right\rbrace \nonumber \\
+= & V \vec{v}_3 \cdot \left\lbrace \left(\begin{array}{c} i_1 \\ j_1 \\ m_1 \end{array}\right) \times \left(\begin{array}{c} i_2 \\ j_2 \\ m_2 \end{array}\right) \right\rbrace \cdot \left(\begin{array}{c} \vec{b}_1 \\ \vec{b}_2 \\ \vec{b}_3 \end{array} \right)
+\end{eqnarray}
+The cross product between ($i_1, j_1, m_1$) and ($i_2, j_2, m_2$) has been found previously in equation \ref{hkldependency} using the Extended Euclidian Algorithm as ($h,k,l$). Inserting this into the equation 
+\begin{eqnarray}
+V = V \vec{v}_3 \cdot (h\vec{b}_1+k\vec{b}_2+l\vec{b}_3) = Vd.
+\end{eqnarray}
+$d$ is therefore equal to 1. The knowledge of $d$ in equation \ref{vdotn} leads to a new equation to solve, to determine the third vector $\vec{v}_3$.
+\begin{eqnarray}
+\vec{n} \cdot \vec{v}_3 & = (h \vec{b}_1 + k \vec{b}_2 + l \vec{b}_3 ) \cdot (i_3\vec{a}_1,j_3\vec{a}_2,m_3\vec{a}_3) \nonumber \\
+ & = hi_3+kj_3+lm_3 = 1 \label{eq:ext_gcd}
+\end{eqnarray}
+This equation can be solved using the Extended Euclidean Algorithm for three variables and therefore the third vector $\vec{v}_3$ is determined. With these three vectors, ($\vec{v}_1, \vec{v}_2, \vec{v}_3$), a basis for the new unit cell is created. The implementation of this will be described in the following section, along with some ways to get around the numerical issues in Python.
+
+\subsection{Implementation in Python}\label{implementation}
+Based on the theory derived above an arbitrary surface can be created using the procedure found on the DTU niflheim cluster. To create a surface using the procedure described in this section a conventional bulk cell of the surface material is needed along with the Miller indices and the depth of the slab. \\
+The implementation in Python using ASE to setup the atoms consists of three parts. First, a new basis is derived from the Miller indices with two of the basis vectors lying in the surface plane. Secondly, the atoms in the conventional bulk cell are expressed in the terms of the new basis in a slab with the selected depth. Finally, the unit cell of the slab is modified so the third cell vector points perpendicular to the surface and all atoms are moved into the unit cell.
+
+\subsubsection{Surface basis}\begin{small}                             \end{small}
+For any surface type described by a Miller indice $(h,k,l)$ the surface basis $(\vec{v}_1,\vec{v}_2,\vec{v}_3)$ is found relative to the conventional bulk unit cell. $\vec{v}_1$ and $\vec{v}_2$ are chosen to be in the surface plane.\\
+In the special case where only one of the Miller indices is non-zero $\vec{v}_1$ and $\vec{v}_2$ are simply the unit vectors in the directions where the Miller indices are zero, respectively and $\vec{v}_3$ is the direction where the Miller indice is non-zero.\\\\
+For all other situations $\vec{v}_1$ and $\vec{v}_2$ are found by solving the linear equation \ref{EquationFromTheory} using the Extended Euclidean Algorithm - in the script defined as \verb#ext_gcd()#.
+This yields an infinite set of solutions all of which can be used. However, the optimal structure is found when the angle between the two base vectors are as close to $90^o$ as possible, as the structure will be as compact as possible and specific sites are easier to identify. This solution is found by minimizing the scalar product of the two base vectors by $c \in \textbf{Z}$.
+\begin{eqnarray}
+\left|\left(\left(p+cl\right)\left(\begin{array}{c} k\vec{a}_1 \\ -h\vec{a}_2\\0\end{array}\right)
++\left(q-ck\right)\left(\begin{array}{c} l\vec{a}_1 \\ 0\\-h\vec{a}_3\end{array}\right)\right)
+\cdot\left(\begin{array}{c} 0 \\ l\vec{a}_2\\-k\vec{a}_3\end{array}\right)\right|_{min(c)} \nonumber 
+\end{eqnarray}
+This can be expressed as $\left| k_1+ck_2 \right|_{min(c)}$ and the solution is found when $c$ is equal to the fraction $-\frac{k_1}{k_2}$ rounded to the nearest integer. Because of numerical errors a tolerance is used. In python this is expressed as follows.
+\begin{verbatim}
+ p,q = ext_gcd(k,l)
+ k1 = dot( p*(k*a1-h*a2)+q*(l*a1-h*a3) , l*a2-k*a3)
+ k2 = dot( l*(k*a1-h*a2)-k*(l*a1-h*a3) , l*a2-k*a3)
+ if abs(k2)>tol:
+  c = -int(round(k1/k2))
+  p,q = p+c*l, q-c*k
+ v1 = p*array((k,-h,0))+q*array((l,0,-h))
+ v2 = reduce(array((0,l,-k)))
+ a,b = ext_gcd(p*k+q*l,h)
+ v3 = array((b,a*p,a*q))
+\end{verbatim}
+The last four lines define the base vectors for the surface using the Extended Euclidean Algorithm for two variables to find $\vec{v}_1$ and $\vec{v}_2$ and three variables to find $\vec{v}_3$. 
+
+\subsubsection{Atom positions}
+When the basis have been found the atoms in the conventional cell are base-changed to the new basis using \begin{verbatim}
+ for i in range(len(bulk)):
+  newpos = linalg.solve(basis.T,bulk.get_scaled_positions()[i])
+  scaled += [newpos-floor(newpos+tol)]
+\end{verbatim} 
+and then moved so the scaled positions in the new basis are within the box spanned by the basis. The tolerance is needed so atoms positioned exactly on the boundary are treated consistently despite numerical errors. The cell in the new basis is then repeated in the $\vec{v}_3$ direction to create the required slap depth. \\
+For many applications it is useful to have the $z$-direction pointing perpendicular to the surface to enable electrostatic decoupling and to make vacuum height and adsorbate distance well defined. The next step in the procedure is therefore to align the $z$-direction with the cross product of $\vec{v}_1$ and $\vec{v}_2$ with a length so the cell volume is preserved. The final step before the slab is created is then to move the atoms so the scaled coordinates are between 0 and 1 in the $\vec{v}_1$ and $\vec{v}_2$ directions making it obvious how the atoms are located relative to each other when the structure is visualized.
+
+\end{document}
diff --git a/doc/ase/gui/ag.png b/doc/ase/gui/ag.png
new file mode 100644
index 0000000..d4b34b0
Binary files /dev/null and b/doc/ase/gui/ag.png differ
diff --git a/doc/ase/gui/basics.rst b/doc/ase/gui/basics.rst
new file mode 100644
index 0000000..8eb5fee
--- /dev/null
+++ b/doc/ase/gui/basics.rst
@@ -0,0 +1,152 @@
+.. module:: basics
+
+==================================
+ag basics and command line options
+==================================
+
+General use
+-----------
+
+Visualizing a system with ag is straight-forward using a regular
+mouse. The scroll function allows to change the magnification, the
+left mouse button selects atoms, the right mouse button allows to
+rotate, and the middle button allows to translate the system on the
+screen. 
+
+Depending on the number of selected atoms, ag automatically measures
+different quantities: 
+
+================================= ======================================
+Selection			  measurement
+================================= ======================================
+single atom                       xyz position and atomic symbol
+two atoms                         interatomic distance and symbols
+three atoms                       all three internal angles and
+      				  symbols 
+four atoms, selected sequentially Measures the dihedral angle,
+     	    	     		  e.g. the angle between bonds 12 and 34
+more than four atoms		  chemical composition of selection. 
+================================= ======================================
+
+ag can save the following file formats: 
+
+=========== =================================
+File format Comment
+=========== =================================
+xyz 	    XYZ file
+traj	    ASE trajectory
+pdb	    PDB file
+cube	    Gaussian cube file
+py 	    Python script
+vnl	    VNL file
+png	    Portable Network Graphics
+pov	    Persistance of Vision
+eps	    Encapsulated PostScript
+in	    FHI-aims geometry input
+POSCAR	    VASP geometry input
+bundle	    ASE bundle trajectory
+cif	    Crystallographic Information File
+=========== =================================
+
+Files
+-----
+
+The :program:`ag` program can read all the file formats the ASE's
+:func:`~ase.io.read` function can understand.
+
+::
+  
+  $ ag N2Fe110-path.traj
+
+
+Selecting part of a trajectory
+------------------------------
+  
+A Python-like syntax for selecting a subset of configurations can be
+used.  Instead of the Python syntax ``list[start:stop:step]``, you use
+:file:`filaname at start:stop:step`::
+
+  $ ag x.traj at 0:10:1  # first 10 images
+  $ ag x.traj at 0:10    # first 10 images
+  $ ag x.traj@:10     # first 10 images
+  $ ag x.traj at -10:    # last 10 images
+  $ ag x.traj at 0       # first image
+  $ ag x.traj at -1      # last image
+  $ ag x.traj@::2     # every second image
+
+If you want to select the same range from many files, the you can use
+the :option:`-n` or :option:`--image-number` option::
+
+  $ ag -n -1 *.traj   # last image from all files
+  $ ag -n 0 *.traj    # first image from all files
+
+.. tip::
+
+  Type :program:`ag -h` for a description of all command line options.
+
+
+Writing files
+-------------
+
+::
+
+  $ ag -n -1 a*.traj -o new.traj
+
+Possible formats are: ``traj``, ``xyz``, ``cube``, ``pdb``, ``eps``,
+``png``, and ``pov``.  For details, see the :mod:`~ase.io` module
+documentation.
+
+Interactive use
+---------------
+
+The :program:`ag` program can also be launched directly from a Python
+script or interactive session:
+
+>>> from ase import *
+>>> atoms = ...
+>>> view(atoms)
+
+or
+
+>>> view(atoms, repeat=(3, 3, 2))
+
+or, to keep changes to your atoms:
+
+>>> atoms.edit()
+
+
+NEB calculations
+----------------
+
+Use :menuselection:`Tools --> NEB` to plot energy barrier.
+
+::
+  
+  $ ag --interpolate 3 initial.xyz final.xyz -o interpolated_path.traj
+
+
+Plotting data from the command line
+-----------------------------------
+Plot the energy relative to the energy of the first image as a
+function of the distance between atom 0 and 5::
+
+  $ ag -g "d(0,5),e-E[0]" x.traj
+  $ ag -t -g "d(0,5),e-E[0]" x.traj > x.dat  # No GUI, write data to stdout
+
+The symbols are the same as used in the plotting data function. 
+
+
+Defaults for ag
+---------------
+
+Using a file ~/.ase/gui.py, certain defaults can be set. If it exists,
+this file is executed after initializing the variables and colours
+normally used in ag. One can change the default graphs that are
+plotted, and the default radii for displaying specific atoms. This
+example will display the energy evolution and the maximal force in a
+graph and also display Cu atoms (Z=29) with a radius of 1.6 Angstrom.
+
+::
+
+  gui_default_settings['gui_graphs_string'] = "i, e - min(E), fmax"
+  gui_default_settings['covalent_radii'] = [[29,1.6]]
diff --git a/doc/ase/gui/calculate.rst b/doc/ase/gui/calculate.rst
new file mode 100644
index 0000000..c570279
--- /dev/null
+++ b/doc/ase/gui/calculate.rst
@@ -0,0 +1,33 @@
+.. module:: calculate
+
+=========
+Calculate
+=========
+
+Set calculator
+--------------
+
+Allows ag to choose a calculator for internal computations (see
+below). Different density functional codes and force fields, as well
+as the EMT calculator are available. For the FHI-aims and VASP
+calculators, it is also possible to export an entire set of input
+files. 
+
+Energy and forces
+-----------------
+
+Invokes the currently set calculator and provides energies and
+optional forces for all atoms. 
+
+Energy minimization
+-------------------
+
+Runs an ASE relaxation using the currently selected calculator with a
+choice of relaxation algorithm and convergence criteria. Great for
+quickly (pre-)relaxing a molecule before placing it into a bigger
+system. 
+
+Scale system
+------------
+
+Stub
diff --git a/doc/ase/gui/edit.rst b/doc/ase/gui/edit.rst
new file mode 100644
index 0000000..c82dac1
--- /dev/null
+++ b/doc/ase/gui/edit.rst
@@ -0,0 +1,46 @@
+.. module:: edit
+
+====
+Edit
+====
+
+Add atoms
+---------
+
+Allows to add single atoms or a group of atoms to an existing atoms
+object. If the description is an atom or a known molecule from the g1,
+g2, or g3 set (e.g. CH4), then the structure from the data molecule is
+used. In addition, a molecule can also be imported from file via the
+``load molecule`` button. 
+
+The specified position can either be absolute, or determined
+automatically via 
+
+::
+   
+   auto+<dist>
+
+where auto is the centre of mass of the currently selected atoms, and
+``<dist>`` is the distance toward the viewing plane. 
+
+The molecule-to-be is rotated into the current viewing plane before
+addition into the system. Two options exist for chosing the origin
+within the new atoms, it can be either the centre of mass or the
+origin of the loaded geometry. 
+
+Modify
+------
+
+Menu to allow modification of the atomic symbol, an attached tag, or
+its magnetic moment.
+
+Copy/Paste
+----------
+
+Allows to copy parts of an existing system to a clipboard, and pastes
+via the same infrastructure as the ``Add atoms`` functionality. Note
+that the on-screen orientation of the pasted atoms will be the same as
+at the time of copying. Note also that, by default, the origin of the
+pasted system is taken to be the atom furthest away from the viewing
+point. 
+
diff --git a/doc/ase/gui/gui.rst b/doc/ase/gui/gui.rst
new file mode 100644
index 0000000..03874cb
--- /dev/null
+++ b/doc/ase/gui/gui.rst
@@ -0,0 +1,27 @@
+.. module:: gui
+   :synopsis: Simple graphical user-interface for ASE.
+
+
+.. index:: gui, ag, ase-gui
+
+=======
+ASE-GUI
+=======
+
+The graphical user-interface allows users to visualize, manipulate,
+and render molecular systems and atoms objects. It also allows to
+setup and run a number of calculations and can be used to transfer
+between different file formats. 
+
+.. image:: ag.png
+   :height: 200 pt
+
+.. toctree::
+
+   basics
+   edit
+   view
+   tools
+   setup
+   calculate
+
diff --git a/doc/ase/gui/setup.rst b/doc/ase/gui/setup.rst
new file mode 100644
index 0000000..c93b6da
--- /dev/null
+++ b/doc/ase/gui/setup.rst
@@ -0,0 +1,13 @@
+.. module:: setup
+
+=====
+Setup
+=====
+
+The setup menus allow for the intuitive creation of numerous standard
+surfaces, nanoparticles, graphene and graphene nanoribbons, as well as
+nanotubes. 
+
+Along with creating the geometry within ag, a button provides the
+necessary python code allowing one to recreate the exact same geometry
+in ASE scripts. 
diff --git a/doc/ase/gui/tools.rst b/doc/ase/gui/tools.rst
new file mode 100644
index 0000000..ba030c3
--- /dev/null
+++ b/doc/ase/gui/tools.rst
@@ -0,0 +1,203 @@
+.. module:: tools
+
+=====
+Tools
+=====
+
+Graphs
+------
+Allows to graph different quantities for a given trajectory. A 'save' button also gives the opportunity to save the data to file.
+
+This example plots the maximal force for each image i and could help in investigating the convergence properties for relaxations:
+
+::
+
+  i, e-min(E), fmax
+
+These are the symbols that can be used:
+
+================ ==================================================
+ Symbol	         Interpretation
+================ ==================================================
+e                total energy
+epot        	 potential energy
+ekin        	 kinetic energy
+fmax        	 maximum force
+fave        	 average force
+d(n1,n2)    	 distance between two atoms
+R[n,0-2]    	 position of atom number n
+i           	 current image number
+E[i]        	 energy of image number i
+F[n,0-2]    	 force on atom number n
+M[n]        	 magnetic moment of atom number n
+A[0-2,0-2]  	 unit-cell basis vectors 
+s           	 path length
+a(n1,n2,n3) 	 tangle between atoms n1, n2 and n3, centered on n2
+dih(n1,n2,n3,n4) dihedral angle between n1, n2, n3, and n4
+T	         temperature (requires velocity)
+================ ==================================================
+
+Movie
+-----
+
+Allows to play the current trajectory as a movie using a number of
+different settings. Default duration is 5 s. 
+
+Expert mode
+-----------
+Python interface to all ag functions, with numerous extra commands
+defined that help to modify and visualize a system. The commands come
+in two flavors, the first is interpreted on an atom-by-atom basis
+(e.g. operates on position, color, etc) and the second is based on the
+entire frame. The flavor of a given line is determined from the first
+command. Note that the frame-based commands can be used in atom-based
+operations, but not vice versa. See below for some examples. 
+
+Regular python syntax applies to the commands and ``numpy`` has been
+imported as ``np``.
+
+Two buttons allow to reduce the operation to a given frame:
+
+======================== =============================================
+Only selected atoms (sa) Restricts operation only to the selected
+     	      	    	 atoms. The text command ``sa`` activates or
+			 deactivates this button. 
+Only current frame (cf)  Restricts operation only to the current
+     	     	   	 frame, useful for long trajectories. The text
+			 command ``cf`` activates or deactivates this
+			 button.  
+======================== =============================================
+
+List of atom-based commands with a few examples:
+
+================ ===================================================== 
+Command	         Interpretation
+================ ===================================================== 
+x,y,z            Cartesian coordinates. Example:
+		 ``x += A[0][0]``
+r,g,b		 Color components, invoking the expert mode changes
+		 the color mode to manual and allows to address all
+		 colors individually. Example:
+		 ``r = (z-min(R[:,2]))/(max(R[:,2])-min(R[:,2]))``
+rad		 atomic display radius
+s		 Boolean to control the selection of an atom. Example:
+		 ``s = Z == 6 and x > 5`` or ``s = d == False``
+f		 force on an atom
+Z		 atomic number
+m		 magnetic moment
+d		 dynamic, e.g. d = False fixes an atom
+================ =====================================================
+
+List of frame-based and global commands and global objects with
+examples: 
+
+================ =====================================================
+Command	         Interpretation
+================ =====================================================
+e		 total energy
+fmax 		 maximal force
+A		 unit cell
+E		 total energy array of all frames. Example:
+		 ``e-min(E)`` 
+F		 all forces in one frame
+M		 all magnetic moments
+R		 all atomic positions
+S		 boolean array of the entire selection
+D		 boolean array of dynamic atoms (False = atom is fixed)
+del S		 deletes current selection
+sa,cf		 toggles the selected-atoms-only or the
+		 current-frame-only buttons 
+frame		 provides and edits the frame number in a trajectory
+center		 centers system in its unit cell
+cov		 array of original covalent radii
+self		 expert mode window
+gui		 ag GUI object, this controls the entire ag session
+img		 ag images object, all physical data is stored here
+================ =====================================================
+
+To save data between commands, one has to assign variables to parent
+objects in the gui, e.g. via ``self.temp_var =
+R-img.P[0,:]``. DISCLAIMER: Doing so might risk the functionality
+ofthe entire ag session if you accidentally overwrite basic
+functionality of the gui or the image objects stored within.  
+
+Finally, recurring selections of commands collected as scripts can be
+executed as 
+
+::
+	
+	exec <filename>
+
+If the file in question is saved in the directory ``~/.ase/`` then
+just the filename will also do. 
+
+Constraints
+-----------
+Allows to set (or remove) constraints based on the currently selected atoms. 
+
+Render scene
+------------
+Graphical interface to the ASE povray interface, ideally it requires
+that povray is installed on your computer to function, but it also can
+be used just to export the complete set of povray files. 
+
+The texture of each atom is adjustable: The default texture is applied
+to all atoms, but then additional textures can be defined based on
+selections (``Create new texture from current selection``). These can
+be obtained either from selecting atoms by hand or by defining a
+selection with a boolean expression, for example ``Z==6 and x>5 and
+y<0`` will select all carbons with coordinates x>5 and y<0. The
+available commands are listed in the ``Help on textures`` window. 
+
+A movie-making mode (``render all N frames``) is also available. After
+rendering, the frames can be stitched together using the ``convert``
+unix program e.g. 
+
+::
+
+	localhost:doc hanke$ convert -delay 4.17 temp.*.png temp.gif
+
+For this particular application it might be a good idea to use a white
+background instead of the default transparent option. 
+
+Move atoms
+----------
+Allows selected atoms to be moved using the arrow keys. The direction
+is always parallel to the plane of the screen. Two possible movements
+are available: Just pressing the arrow keys will move by 0.1
+Angstrom, ``shift`` + arrow keys will move by 0.01 Angstrom. 
+
+Rotate atoms
+------------
+Allows sets of atoms to be rotated using the arrow keys. Different
+rotation modes are available depending on the number of selected
+atoms. Again, two modes are available. Just the arrow keys will rotate
+by 2.5 degrees, and ``shift`` + arrow keys will rotate by 0.5 deg.  
+
+============================== =======================================
+number of atoms labeled	       rotation mode
+============================== =======================================
+0 atoms, 1, 3, 5 or more atoms uses the centre of mass of the atoms to
+  			       be rotated as the rotation centre. 
+2 atoms			       Defines the vector connecting the two
+  			       atoms as rotation axis. 
+4 atoms, selected sequentially Defines the vector connecting the two
+  	 	  	       atoms as rotation axis. This mode has
+			       the advantage that the dihedral angle
+			       is measured at the same time, thus
+			       allowing one to monitor the degree of
+			       rotation. 
+============================== =======================================
+
+Orient atoms
+------------
+stub
+
+
+NEB
+---
+stub
+
+Bulk Modulus
+------------
+stub
diff --git a/doc/ase/gui/view.rst b/doc/ase/gui/view.rst
new file mode 100644
index 0000000..e311813
--- /dev/null
+++ b/doc/ase/gui/view.rst
@@ -0,0 +1,30 @@
+.. module:: view
+
+====
+View
+====
+
+Repeat
+------
+Menu to allow repition of periodic unit cells. Use the 'Set unit cell'
+button to set the overall unit cell to the current one.
+
+Rotate
+------
+Menu to manually fine tune the viewing angle. Use 'Update' button to
+set the menu to the current angle.
+
+Colors
+------
+The colors menu allows numerous ways to change the color scheme and to
+encode additional information into the display colors. This includes
+automatic coloring by atomic numbers (default), user-specified atomic
+number scheme, coloring by tags, forces, or completely manually
+specified colors. In addition, several standard color scales are
+available. 
+
+Settings
+--------
+Basic viewing settings. Also allows to constrain/unconstrain atoms and
+to mark selected atoms as invisible. 
+
diff --git a/doc/ase/infrared.rst b/doc/ase/infrared.rst
new file mode 100644
index 0000000..3445afa
--- /dev/null
+++ b/doc/ase/infrared.rst
@@ -0,0 +1,14 @@
+.. module:: infrared
+
+
+====================
+Infrared intensities
+====================
+
+:class:`~ase.infrared.InfraRed` is an extension of
+:class:`~ase.vibrations.Vibrations`, in addition to the
+vibrational modes, also the infrared intensities of the modes
+are calculated for an :class:`~ase.atoms.Atoms` object.
+
+.. autoclass:: ase.infrared.InfraRed
+   :members:
diff --git a/doc/ase/io.rst b/doc/ase/io.rst
new file mode 100644
index 0000000..8e4964c
--- /dev/null
+++ b/doc/ase/io.rst
@@ -0,0 +1,91 @@
+.. module:: io
+   :synopsis: File input-output module
+
+
+File input and output
+=====================
+
+The :mod:`io` module has two basic functions: :func:`read` and :func:`write`.
+The two methods are described here:
+
+.. autofunction:: ase.io.read
+.. autofunction:: ase.io.write
+
+The :func:`read` function is only designed to retrive the atomic configuration
+from a file, but for the CUBE format you can import the function:
+
+.. function:: read_cube_data
+
+
+which will return a ``(data, atoms)`` tuple::
+   
+  from ase.io.cube import read_cube_data
+  data, atoms = read_cube_data('abc.cube')
+
+
+
+Examples
+========
+
+::
+
+    from ase.lattice.surface import *
+    adsorbate = Atoms('CO')
+    adsorbate[1].z = 1.1
+    a = 3.61
+    slab = fcc111('Cu', (2, 2, 3), a=a, vacuum=7.0)
+    add_adsorbate(slab, adsorbate, 1.8, 'ontop')
+ 
+Write PNG image::
+
+    write('slab.png', slab * (3, 3, 1), rotation='10z,-80x')
+
+.. image:: io1.png
+
+Write POVRAY file::
+
+    write('slab.pov', slab * (3, 3, 1), rotation='10z,-80x')
+
+This will write both a ``slab.pov`` and a ``slab.ini`` file.  Convert
+to PNG with the command ``povray slab.ini`` or use the
+``run_povray=True`` option:
+
+.. image:: io2.png
+
+Here is an example using ``bbox``::
+
+    d = a / 2**0.5
+    write('slab.pov', slab * (2, 2, 1),
+          bbox=(d, 0, 3 * d, d * 3**0.5))
+
+.. image:: io3.png
+
+Note that the XYZ-format does not contain information about the unic cell:
+
+>>> write('slab.xyz', slab)
+>>> a = read('slab.xyz')
+>>> a.get_cell()
+array([[ 1.,  0.,  0.],
+       [ 0.,  1.,  0.],
+       [ 0.,  0.,  1.]])
+>>> a.get_pbc()
+array([False, False, False], dtype=bool)
+
+Use ASE's native format for writing all information:
+
+>>> write('slab.traj', slab)
+>>> b = read('slab.traj')
+>>> b.get_cell()
+array([[  5.10531096e+00,  -4.11836034e-16,   1.99569088e-16],
+       [  2.55265548e+00,   4.42132899e+00,   7.11236625e-17],
+       [  8.11559027e+00,   4.68553823e+00,   1.32527034e+01]])
+>>> b.get_pbc()
+array([ True,  True,  True], dtype=bool)
+
+A script showing all of the povray parameters, and generating the image below, 
+can be found here: :trac:`doc/ase/save_pov.py`
+
+.. image:: NaCl_C6H6.png
+
+An other example showing how to change colors and textures in pov can
+be found here: :trac:`doc/tutorials/saving_graphics.py`
diff --git a/doc/ase/iopng.py b/doc/ase/iopng.py
new file mode 100644
index 0000000..1944eaf
--- /dev/null
+++ b/doc/ase/iopng.py
@@ -0,0 +1,29 @@
+# creates: io1.png io2.png io3.png
+
+from ase import Atoms
+from ase.lattice.surface import fcc111, add_adsorbate
+from ase.io import write, read
+
+adsorbate = Atoms('CO')
+adsorbate[1].z = 1.1
+a = 3.61
+slab = fcc111('Cu', (2, 2, 3), a=a, vacuum=7.0)
+add_adsorbate(slab, adsorbate, 1.8, 'ontop')
+
+#view(slab)
+write('io1.png', slab * (3, 3, 1), rotation='10z,-80x')
+write('io2.pov', slab * (3, 3, 1), rotation='10z,-80x',
+      transparent=False, display=False, run_povray=True)
+d = a / 2**0.5
+write('io3.pov', slab * (2, 2, 1),
+      bbox=(d, 0, 3 * d, d * 3**0.5),
+      transparent=False, display=False, run_povray=True)
+
+write('slab.xyz', slab)
+a = read('slab.xyz')
+a.get_cell()
+a.get_pbc()
+write('slab.traj', slab)
+b = read('slab.traj')
+b.get_cell()
+b.get_pbc()
diff --git a/doc/ase/lattice.rst b/doc/ase/lattice.rst
new file mode 100644
index 0000000..04801a3
--- /dev/null
+++ b/doc/ase/lattice.rst
@@ -0,0 +1,262 @@
+.. default-role:: math
+
+.. _general-crystal-section:
+
+General crystal structures and surfaces
+=======================================
+
+.. module:: lattice
+
+Modules for creating crystal structures are found in the module
+:mod:`lattice`.  Most Bravais lattices are implemented, as
+are a few important lattices with a basis.  The modules can create
+lattices with any orientation (see below).  These modules can be used
+to create surfaces with any crystal structure and any orientation by
+later adding a vacuum layer with :func:`lattice.surface.add_vacuum`.
+
+Example
+-------
+
+To set up a slab of FCC copper with the [1,-1,0] direction along the
+x-axis, [1,1,-2] along the y-axis and [1,1,1] along the z-axis, use::
+
+  from ase.lattice.cubic import FaceCenteredCubic
+  atoms = FaceCenteredCubic(directions=[[1,-1,0], [1,1,-2], [1,1,1]],
+                            size=(2,2,3), symbol='Cu', pbc=(1,1,0))
+
+The minimal unit cell is repeated 2*2*3 times.  The lattice constant
+is taken from the database of lattice constants in :mod:`data` module.
+There are periodic boundary conditions along the *x* and *y* axis, but
+free boundary conditions along the *z* axis. Since the three directions
+are perpendicular, a (111) surface is created.
+
+To set up a slab of BCC copper with [100] along the first axis, [010]
+along the second axis, and [111] along the third axis use::
+
+  from ase.lattice.cubic import BodyCenteredCubic
+  atoms = BodyCenteredCubic(directions=[[1,0,0], [0,1,0], [1,1,1]],
+                            size=(2,2,3), symbol='Cu', pbc=(1,1,0),
+			    latticeconstant=4.0)
+
+Since BCC is not the natural crystal structure for Cu, a lattice
+constant has to be specified.  Note that since the repeat directions
+of the unit cell are not orthogonal, the Miller indices of the
+surfaces will *not* be the same as the Miller indices of the axes.
+The indices of the surfaces in this example will be (1,0,-1), (0,1,-1)
+and (0,0,1).
+
+
+Available crystal lattices
+--------------------------
+
+The following modules are currently available (the * mark lattices
+with a basis):
+
+* ``lattice.cubic``
+
+  - ``SimpleCubic`` 
+  - ``FaceCenteredCubic``
+  - ``BodyCenteredCubic``
+  - ``Diamond`` (*)
+
+* ``lattice.tetragonal``
+
+  - ``SimpleTetragonal``
+  - ``CenteredTetragonal``
+
+* ``lattice.orthorhombic``
+
+  - ``SimpleOrthorhombic``
+  - ``BaseCenteredOrthorhombic``
+  - ``FaceCenteredOrthorhombic``
+  - ``BodyCenteredOrthorhombic``
+
+* ``lattice.monoclinic``
+
+  - ``SimpleMonoclinic``
+  - ``BaseCenteredMonoclinic``
+
+* ``lattice.triclinic``
+
+  - ``Triclinic``
+
+* ``lattice.hexagonal``
+
+  - ``Hexagonal``
+  - ``HexagonalClosedPacked`` (*)
+  - ``Graphite`` (*)
+
+* The rhombohedral (or trigonal) lattices are not implemented.  They
+  will be implemented when the need arises (and if somebody can tell
+  me_ the precise definition of the 4-number Miller indices - I only
+  know that they are "almost the same as in hexagonal lattices").
+
+* ``lattice.compounds``
+
+  Lattices with more than one element.  These are mainly intended as
+  examples allowing you to define new such lattices.  Currenly, the
+  following are defined
+
+  - ``B1`` = ``NaCl`` = ``Rocksalt``
+  - ``B2`` = ``CsCl``
+  - ``B3`` = ``ZnS`` = ``Zincblende``
+  - ``L1_2`` = ``AuCu3``
+  - ``L1_0`` = ``AuCu``
+
+.. _me: http://www.fysik.dtu.dk/~schiotz
+
+Usage
+-----
+
+The lattice objects are called with a number of arguments specifying
+e.g. the size and orientation of the lattice.  All arguments should be
+given as named arguments.  At a minimum the ``symbol`` argument must
+be specified.
+
+
+``symbol``
+  The element, specified by the atomic number (an integer) or by the
+  atomic symbol (i.e. 'Au').  For compounds, a tuple or list of
+  elements should be given.  This argument is mandatory.
+
+``directions`` and/or ``miller``: 
+  Specifies the orientation of the
+  lattice as the Miller indices of the three basis vectors of the
+  supercell (``directions=...``) and/or as the Miller indices of the
+  three surfaces (``miller=...``).  Normally, one will specify either
+  three directions or three surfaces, but any combination that is both
+  complete and consistent is allowed, e.g. two directions and two
+  surface miller indices (this example is slightly redundant, and
+  consistency will be checked).  If only some directions/miller
+  indices are specified, the remaining should be given as ``None``.
+  If you intend to generate a specific surface, and prefer to specify
+  the miller indices of the unit cell basis (``directions=...``), it
+  is a good idea to give the desired Miller index of the surface as
+  well to allow the module to test for consistency.  Example:
+
+  >>> atoms = BodyCenteredCubic(directions=[[1,-1,0],[1,1,-1],[0,0,1]],
+  ...                           miller=[None, None, [1,1,2]], ...)
+
+  If neither ``directions`` nor ``miller`` are specified, the default
+  is ``directions=[[1,0,0], [0,1,0], [0,0,1]]``.
+
+``size``:
+  A tuple of three numbers, defining how many times the fundamental
+  repeat unit is repeated. Default: (1,1,1).  Be aware that if
+  high-index directions are specified, the fundamental repeat unit may
+  be large.
+
+``latticeconstant``:
+  The lattice constant.  If no lattice constant is
+  specified, one is extracted from ASE.ChemicalElements provided that
+  the element actually has the crystal structure you are creating.
+  Depending on the crystal structure, there will be more than one
+  lattice constant, and they are specified by giving a dictionary or a
+  tuple (a scalar for cubic lattices).  Distances are given in
+  Angstrom, angles in degrees. 
+
+  =============  ===================  ========================================
+  Structure      Lattice constants    Dictionary-keys
+  =============  ===================  ========================================
+  Cubic          a                    'a'
+  Tetragonal     (a, c)               'a', 'c' or 'c/a'
+  Orthorhombic   (a, b, c)            'a', 'b' or 'b/a', 'c' or 'c/a'
+  Triclinic      (a, b, c, `\alpha`,  'a', 'b' or 'b/a', 'c' or
+                 `\beta`, `\gamma`)   'c/a', 'alpha', 'beta', 'gamma'
+  Monoclinic     (a, b, c, alpha)     'a', 'b' or 'b/a', 'c' or 'c/a', 'alpha'
+  Hexagonal      (a, c)               'a', 'c' or 'c/a'
+  =============  ===================  ========================================
+  
+  Example:
+
+  >>> atoms = Monoclinic( ... , latticeconstant={'a': 3.06, 
+  ...     'b/a': 0.95, 'c/a': 1.07, 'alpha': 74})
+
+
+``debug``:
+  Controls the amount of information printed.  0: no info is printed.
+  1 (the default): The indices of surfaces and unit cell vectors are
+  printed.  2: Debugging info is printed.
+
+
+Defining new lattices
+---------------------
+
+Often, there is a need for new lattices - either because an element
+crystallizes in a lattice that is not a simple Bravais lattice, or
+because you need to work with a compound or an ordered alloy.
+
+All the lattice generating objects are instances of a class, you
+generate new lattices by deriving a new class and instantiating it.
+This is best explained by an example.  The diamond lattice is two
+interlacing FCC lattices, so it can be seen as a face-centered cubic
+lattice with a two-atom basis.  The Diamond object could be defined like
+this::
+
+  from ase.lattice.cubic import FaceCenteredCubicFactory
+  class DiamondFactory(FaceCenteredCubicFactory):
+      """A factory for creating diamond lattices."""
+      xtal_name = 'diamond'
+      bravais_basis = [[0, 0, 0], [0.25, 0.25, 0.25]]
+    
+  Diamond = DiamondFactory()
+
+
+
+Lattices with more than one element
+```````````````````````````````````
+
+Lattices with more than one element is made in the same way.  A new
+attribute, ``element_basis``, is added, giving which atoms in the
+basis are which element.  If there are four atoms in the basis, and
+element_basis is (0,0,1,0), then the first, second and fourth atoms
+are one element, and the third is the other element.  As an example,
+the AuCu3 structure (also known as `\mathrm{L}1_2`) is defined as::
+
+  # The L1_2 structure is "based on FCC", but is really simple cubic
+  # with a basis.
+  class AuCu3Factory(SimpleCubicFactory):
+      "A factory for creating AuCu3 (L1_2) lattices."
+      bravais_basis = [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]
+      element_basis = (0, 1, 1, 1)
+
+  AuCu3 = L1_2 = AuCu3Factory()
+
+Sometimes, more than one crystal structure can be used to define the
+crystal structure, for example the Rocksalt structure is two
+interpenetrating FCC lattices, one with one kind of atoms and one with
+another.  It would be tempting to define it as
+
+::
+
+  class NaClFactory(FaceCenteredCubicFactory):
+      "A factory for creating NaCl (B1, Rocksalt) lattices."
+
+      bravais_basis = [[0, 0, 0], [0.5, 0.5, 0.5]]
+      element_basis = (0, 1)
+
+
+  B1 = NaCl = Rocksalt = NaClFactory()
+
+but if this is used to define a finite system, one surface would be
+covered with one type of atoms, and the opposite surface with the
+other.  To maintain the stochiometry of the surfaces, it is better to
+use the simple cubic lattice with a larger basis::
+
+  # To prevent a layer of element one on one side, and a layer of
+  # element two on the other side, NaCl is based on SimpleCubic instead
+  # of on FaceCenteredCubic
+  class NaClFactory(SimpleCubicFactory):
+      "A factory for creating NaCl (B1, Rocksalt) lattices."
+
+      bravais_basis = [[0, 0, 0], [0, 0, 0.5], [0, 0.5, 0], [0, 0.5, 0.5],
+		       [0.5, 0, 0], [0.5, 0, 0.5], [0.5, 0.5, 0],
+		       [0.5, 0.5, 0.5]]
+      element_basis = (0, 1, 1, 0, 1, 0, 0, 1)
+
+
+  B1 = NaCl = Rocksalt = NaClFactory()
+
+More examples can be found in the file :trac:`ase/lattice/compounds.py`.
+
+.. default-role::
diff --git a/doc/ase/m13.py b/doc/ase/m13.py
new file mode 100644
index 0000000..0f8ccb5
--- /dev/null
+++ b/doc/ase/m13.py
@@ -0,0 +1,18 @@
+from math import sqrt
+from ase.cluster.cubic import FaceCenteredCubic
+from ase.tasks.molecule import MoleculeTask
+from ase.data import covalent_radii, atomic_numbers
+
+class M13Task(MoleculeTask):
+    taskname = 'm13'
+    def build_system(self, name):
+        if self.bond_length is None:
+            b = 2 * covalent_radii[atomic_numbers[name]]
+        else:
+             b = self.bond_length
+
+        return FaceCenteredCubic(name, [(1, 0, 0)], [1],
+                                 latticeconstant=b * sqrt(2))
+
+task = M13Task()
+
diff --git a/doc/ase/md.rst b/doc/ase/md.rst
new file mode 100644
index 0000000..9e1c8d5
--- /dev/null
+++ b/doc/ase/md.rst
@@ -0,0 +1,413 @@
+==================
+Molecular dynamics
+==================
+
+.. module:: md
+   :synopsis: Molecular Dynamics
+
+Typical computer simulations involve moving the atoms around, either
+to optimize a structure (energy minimization) or to do molecular
+dynamics.  This chapter discusses molecular dynamics, energy
+minimization algorithms will be discussed in the :mod:`optimize`
+section.
+
+A molecular dynamics object will operate on the atoms by moving them
+according to their forces - it integrates Newton's second law
+numerically.  A typical molecular dynamics simulation will use the
+`Velocity Verlet dynamics`_.  You create the
+:class:`VelocityVerlet` object, giving it the atoms and a time step, and then
+you perform dynamics by calling its :meth:`run` method::
+
+  dyn = VelocityVerlet(atoms, 5.0 * units.fs)
+  dyn.run(1000)  # take 1000 steps
+
+A number of different algorithms can be used to perform molecular
+dynamics, with slightly different results.  
+
+Choosing the time step
+======================
+
+All the dynamics objects need a time step.  Choosing it too small will
+waste computer time, choosing it too large will make the dynamics
+unstable, typically the energy increases dramatically (the system
+"blows up").  If the time step is only a little to large, the lack of
+energy conservation is most obvious in `Velocity Verlet dynamics`_,
+where energy should otherwise be conserved.
+
+Experience has shown that 5 femtoseconds is a good choice for most metallic
+systems.  Systems with light atoms (e.g. hydrogen) and/or with strong
+bonds (carbon) will need a smaller time step.
+
+All the dynamics objects documented here are sufficiently related to
+have the same optimal time step.
+
+
+File output
+===========
+
+The time evolution of the system can be saved in a trajectory file,
+by creating a trajectory object, and attaching it to the dynamics
+object.  This is documented in the module :mod:`ase.io.trajectory`. 
+
+Unlike the geometry optimization classes, the molecular dynamics
+classes do not support giving a trajectory file name in the
+constructor.  Instead the trajectory must be attached explicitly to
+the dynamics, and it is *stongly recommended* to use the optional
+``interval`` argument, so every time step is not written to the file.
+
+
+Logging
+=======
+
+A logging mechanism is provided, printing time; total, potential and
+kinetic energy; and temperature (calculated from the kinetic energy).
+It is enabled by giving the ``logfile`` argument when the dynamics
+object is created, ``logfile`` may be an open file, a filename or the
+string '-' meaning standard output.  Per default, a line is printed
+for each timestep, specifying the ``loginterval`` argument will chance
+this to a more reasonable frequency.
+
+The logging can be customized by explicitly attaching a
+:class:`ase.md.MDLogger` object to the dynamics::
+
+  from ase.md import MDLogger
+  dyn = VelocityVerlet(atoms, dt=2*ase.units.fs)
+  dyn.attach(MDLogger(dyn, atoms, 'md.log', header=False, stress=False,
+             peratom=True, mode="a"), interval=1000)
+
+This example will skip the header line and write energies per atom
+instead of total energies.  The parameters are
+
+  ``header``: Print a header line defining the columns.
+
+  ``stress``: Print the six components of the stress tensor.
+
+  ``peratom``:  Print energy per atom instead of total energy.
+
+  ``mode``:  If 'a', append to existing file, if 'w' overwrite
+  existing file.
+
+Despite appearances, attaching a logger like this does *not* create a
+cyclic reference to the dynamics.
+
+.. note::
+
+   If building your own logging class, be sure not to attach the dynamics
+   object directly to the logging object. Instead, create a weak reference
+   using the ``proxy`` method of the ``weakref`` package. See the
+   `ase.md.MDLogger` source code for an example. (If this is not done, a
+   cyclic reference may be created which can cause certain calculators,
+   such as Jacapo, to not terminate correctly.)
+
+
+
+Constant NVE simulations (the microcanonical ensemble)
+======================================================
+
+Newton's second law preserves the total energy of the system, and a
+straightforward integration of Newton's second law therefore leads to
+simulations preserving the total energy of the system (E), the number
+of atoms (N) and the volume of the system (V).  The most appropriate
+algorithm for doing this is velocity Verlet dynamics, since it gives
+very good long-term stability of the total energy even with quite
+large time steps.  Fancier algorithms such as Runge-Kutta may give
+very good short-term energy preservation, but at the price of a slow
+drift in energy over longer timescales, causing trouble for long
+simulations.
+
+In a typical NVE simulation, the temperature will remain approximately
+constant, but if significant structural changes occurs they may result
+in temperature changes.  If external work is done on the system, the
+temperature is likely to rise significantly.
+
+Velocity Verlet dynamics
+------------------------
+
+.. module:: md.verlet
+
+.. class:: VelocityVerlet(atoms, timestep)
+
+
+``VelocityVerlet`` is the only dynamics implementing the NVE ensemble.
+It requires two arguments, the atoms and the time step.  Choosing
+a too large time step will immediately be obvious, as the energy will
+increase with time, often very rapidly.
+
+Example: See the tutorial :ref:`md_tutorial`.
+
+
+
+Constant NVT simulations (the canonical ensemble)
+=================================================
+
+Since Newton's second law conserves energy and not temperature,
+simulations at constant temperature will somehow involve coupling the
+system to a heat bath.  This cannot help being somewhat artificial.
+Two different approaches are possible within ASE.  In Langevin
+dynamics, each atom is coupled to a heat bath through a fluctuating
+force and a friction term.  In Nosé-Hoover dynamics, a term
+representing the heat bath through a single degree of freedom is
+introduced into the Hamiltonian.
+
+Langevin dynamics
+-----------------
+
+.. module:: md.langevin
+
+.. class:: Langevin(atoms, timestep, temperature, friction)
+
+
+The Langevin class implements Langevin dynamics, where a (small)
+friction term and a fluctuating force are added to Newton's second law
+which is then integrated numerically.  The temperature of the heat
+bath and magnitude of the friction is specified by the user, the
+amplitude of the fluctuating force is then calculated to give that
+temperature.  This procedure has some physical justification: in a
+real metal the atoms are (weakly) coupled to the electron gas, and the
+electron gas therefore acts like a heat bath for the atoms.  If heat
+is produced locally, the atoms locally get a temperature that is
+higher than the temperature of the electrons, heat is transferred to
+the electrons and then rapidly transported away by them.  A Langevin
+equation is probably a reasonable model for this process.
+
+A disadvantage of using Langevin dynamics is that if significant heat
+is produced in the simulation, then the temperature will stabilize at
+a value higher than the specified temperature of the heat bath, since
+a temperature difference between the system and the heat bath is
+necessary to get a finite heat flow.  Another disadvantage is that the
+fluctuating force is stochastic in nature, so repeating the simulation
+will not give exactly the same trajectory.
+
+When the ``Langevin`` object is created, you must specify a time step,
+a temperature (in energy units) and a friction.  Typical values for
+the friction are 0.01-0.02 atomic units.
+
+::
+
+  # Room temperature simulation
+  dyn = Langevin(atoms, 5 * units.fs, units.kB * 300, 0.002)
+
+Both the friction and the temperature can be replaced with arrays
+giving per-atom values.  This is mostly useful for the friction, where
+one can choose a rather high friction near the boundaries, and set it
+to zero in the part of the system where the phenomenon being studied
+is located.
+
+
+
+Nosé-Hoover dynamics
+--------------------
+
+In Nosé-Hoover dynamics, an extra term is added to the Hamiltonian
+representing the coupling to the heat bath.  From a pragmatic point of
+view one can regard Nosé-Hoover dynamics as adding a friction term to
+Newton's second law, but dynamically changing the friction coefficient
+to move the system towards the desired temperature.  Typically the
+"friction coefficient" will fluctuate around zero.
+
+Nosé-Hoover dynamics is not implemented as a separate class, but is a
+special case of NPT dynamics.
+
+
+Berendsen NVT dynamics
+-----------------------
+.. module:: md.nvtberendsen
+
+.. class:: NVTBerendsen(atoms, timestep, temperature, taut, fixcm)
+
+In Berendsen NVT simulations the velocities are scaled to achieve the desired 
+temperature. The speed of the scaling is determined by the parameter taut.
+
+This method does not result proper NVT sampling but it usually is 
+sufficiently good in practise (with large taut). For discussion see 
+the gromacs manual at www.gromacs.org.
+
+*atoms*:
+    The list of atoms.
+    
+*timestep*:
+    The time step.
+
+*temperature*:
+    The desired temperature, in Kelvin.
+
+*taut*:
+    Time constant for Berendsen temperature coupling.
+
+*fixcm*:
+    If True, the position and momentum of the center of mass is
+    kept unperturbed.  Default: True.
+
+::
+
+  # Room temperature simulation (300K, 0.1 fs time step)
+  dyn = NVTBerendsen(atoms, 0.1 * units.fs, 300, taut=0.5*1000*units.fs)
+
+
+
+Constant NPT simulations (the isothermal-isobaric ensemble)
+===========================================================
+
+.. module:: md.npt
+
+.. class:: NPT(atoms, timestep, temperature, externalstress, ttime, pfactor, mask=None) 
+
+Dynamics with constant pressure (or optionally, constant stress) and
+constant temperature (NPT or N,stress,T ensemble).  It uses the
+combination of Nosé-Hoover and Parrinello-Rahman dynamics proposed by
+Melchionna et al. [1] and later modified by Melchionna [2].  The
+differential equations are integrated using a centered difference
+method [3].  Details of the implementation are available in the
+document XXX NPTdynamics.tex, distributed with the module.
+
+The dynamics object is called with the following parameters:
+
+*atoms*:
+  The atoms object.
+
+*timestep*:
+  The timestep in units matching eV, Å, u.  Use the *units.fs* constant.
+
+*temperature*:
+  The desired temperature in eV.
+
+*externalstress*:
+  The external stress in eV/Å^3.  Either a symmetric
+  3x3 tensor, a 6-vector representing the same, or a scalar
+  representing the pressure.  Note that the stress is positive in
+  tension whereas the pressure is positive in compression: giving a
+  scalar p is equivalent to giving the tensor (-p. -p, -p, 0, 0, 0).
+
+*ttime*:
+  Characteristic timescale of the thermostat.  Set to None to
+  disable the thermostat.
+
+*pfactor*:
+  A constant in the barostat differential equation.  If a
+  characteristic barostat timescale of ptime is desired, set pfactor
+  to ptime^2 * B (where B is the Bulk Modulus).  Set to None to
+  disable the barostat.  Typical metallic bulk moduli are of the order
+  of 100 GPa or 0.6 eV/Å^3.
+
+*mask=None*:
+  Optional argument.  A tuple of three integers (0 or 1),
+  indicating if the system can change size along the three Cartesian
+  axes.  Set to (1,1,1) or None to allow a fully flexible
+  computational box.  Set to (1,1,0) to disallow elongations along the
+  z-axis etc.
+
+
+Useful parameter values:
+
+* The same *timestep* can be used as in Verlet dynamics, i.e. 5 fs is fine
+  for bulk copper.
+
+* The *ttime* and *pfactor* are quite critical[4], too small values may
+  cause instabilites and/or wrong fluctuations in T / p.  Too
+  large values cause an oscillation which is slow to die.  Good
+  values for the characteristic times seem to be 25 fs for *ttime*,
+  and 75 fs for *ptime* (used to calculate pfactor), at least for
+  bulk copper with 15000-200000 atoms.  But this is not well
+  tested, it is IMPORTANT to monitor the temperature and
+  stress/pressure fluctuations.
+
+It has the following methods:
+
+.. method:: NPT.run(n):
+
+  Perform n timesteps.
+
+.. method:: NPT.initialize():
+
+  Estimates the dynamic variables for time=-1 to start the
+  algorithm.  This is automatically called before the first timestep.
+
+.. method:: NPT.set_stress():
+
+  Set the external stress.  Use with care.  It is
+  preferable to set the right value when creating the object.
+
+.. method:: NPT.set_mask():
+
+  Change the mask.  Use with care, as you may "freeze" a
+  fluctuation in the strain rate.
+  
+.. method:: NPT.set_strainrate(eps):
+
+  Set the strain rate.  ``eps`` must be an upper-triangular matrix.
+  If you set a strain rate along a direction that is "masked out"
+  (see ``set_mask``), the strain rate along that direction will be
+  maintained constantly.
+
+.. method:: NPT.get_gibbs_free_energy():
+
+  Gibbs free energy is supposed to be
+  preserved by this dynamics.  This is mainly intended as a diagnostic
+  tool.
+
+References:
+
+[1] S. Melchionna, G. Ciccotti and B. L. Holian, Molecular Physics
+78, p. 533 (1993).
+
+[2] S. Melchionna, Physical Review E 61, p. 6165 (2000).
+
+[3] B. L. Holian, A. J. De Groot, W. G. Hoover, and C. G. Hoover,
+Physical Review A 41, p. 4552 (1990).
+
+[4] F. D. Di Tolla and M. Ronchetti, Physical Review E 48, p. 1726 (1993).
+
+.. seealso::
+    
+   The :term:`API` documentation: :epydoc:`ase.md`
+
+
+Berendsen NPT dynamics
+-----------------------
+.. module:: md.nptberendsen
+
+.. class:: NPTBerendsen(atoms, timestep, temperature, taut, fixcm, pressure, taup,compressibility)
+
+In Berendsen NPT simulations the velocities are scaled to achieve the desired 
+temperature. The speed of the scaling is determined by the parameter taut.
+
+The atom positions and the simulation cell are scaled in order to achieve 
+the desired pressure. 
+
+This method does not result proper NPT sampling but it usually is 
+sufficiently good in practise (with large taut and taup). For discussion see 
+the gromacs manual at www.gromacs.org. or amber at ambermd.org
+
+*atoms*:
+    The list of atoms.
+    
+*timestep*:
+    The time step.
+
+*temperature*:
+    The desired temperature, in Kelvin.
+
+*taut*:
+    Time constant for Berendsen temperature coupling.
+
+*fixcm*:
+    If True, the position and momentum of the center of mass is
+    kept unperturbed.  Default: True.
+
+*pressure*:
+    The desired pressure, in bar (1 bar = 1e5 Pa).
+
+*taup*:
+    Time constant for Berendsen pressure coupling.
+
+*compressibility*:
+    The compressibility of the material, water 4.57E-5 bar-1, in bar-1
+
+
+::
+
+  # Room temperature simulation (300K, 0.1 fs time step, atmospheric pressure)
+  dyn = NPTBerendsen(atoms, timestep=0.1*units.fs, temperature=300,
+                   taut=0.1*1000*units.fs, pressure = 1.01325,
+                   taup=1.0*1000*units.fs, compressibility=4.57e-5)
+
diff --git a/doc/ase/molecules.rst b/doc/ase/molecules.rst
new file mode 100644
index 0000000..3861f91
--- /dev/null
+++ b/doc/ase/molecules.rst
@@ -0,0 +1,14 @@
+.. _molecules-section:
+
+Molecules
+=========
+
+The G2-database of common molecules is available in the
+:mod:`data.molecules` module.
+
+.. autofunction:: ase.data.molecules.molecule
+
+Example::
+
+>>> from ase.data.molecules import molecule
+>>> atoms = molecule('H2O')
diff --git a/doc/ase/neb.rst b/doc/ase/neb.rst
new file mode 100644
index 0000000..bd03996
--- /dev/null
+++ b/doc/ase/neb.rst
@@ -0,0 +1,185 @@
+===================
+Nudged elastic band
+===================
+
+.. module:: neb
+   :synopsis: Nudged Elastic Band method.
+
+.. default-role:: math
+
+The Nudged Elastic Band method is a technique for finding transition paths
+(and corresponding energy barriers) between given initial and final states.
+The method involves constructing a "chain" of "replicas" or "images" of the
+system and relaxing them in a certain way.
+
+Relevant literature References:
+
+
+1. H. Jonsson, G. Mills, and K. W. Jacobsen, in 'Classical and Quantum
+   Dynamics in Condensed Phase Systems', edited by B. J. Berne,
+   G. Cicotti, and D. F. Coker, World Scientific, 1998 [standard
+   formulation]
+
+2. 'Improved Tangent Estimate in the NEB method for Finding Minimum
+   Energy Paths and Saddle Points', Graeme Henkelman and Hannes
+   Jonsson, J. Chem. Phys. 113, 9978 (2000) [improved tangent
+   estimates]
+
+3. 'A Climbing-Image NEB Method for Finding Saddle Points and Minimum
+   Energy Paths', Graeme Henkelman, Blas P. Uberuaga and Hannes
+   Jonsson, J. Chem. Phys. 113, 9901 (2000)
+
+
+The NEB class
+=============
+
+This module defines one class:
+
+.. class:: NEB(images, k=0.1, climb=False)
+
+Example of use::
+
+  # Read initial and final states:
+  initial = read('A.traj')
+  final = read('B.traj')
+  # Make a band consisting of 5 images:
+  images = [initial]
+  images += [initial.copy() for i in range(3)]
+  images += [final]
+  neb = NEB(images)
+  # Interpolate linearly the potisions of the three middle images:
+  neb.interpolate()
+  # Set calculators:
+  for image in images[1:4]:
+      image.set_calculator(MyCalculator(...))
+  # Optimize:
+  optimizer = MDMin(neb, trajectory='A2B.traj')
+  optimizer.run(fmax=0.04)
+
+Notice the use of the :meth:`~NEB.interpolate` method to get a good
+initial guess for the path from A to B.
+
+.. method:: NEB.interpolate()
+
+   Interpolate path linearly from initial to final state.
+
+Note also that only the internal images (not the endpoints) need have
+calculators attached.
+
+
+.. seealso::
+
+   :mod:`optimize`:
+        Information about energy minimization (optimization).
+
+   :mod:`calculators`:
+        How to use calculators.
+
+   :ref:`tutorials`:
+
+        * :ref:`diffusion_tutorial`
+        * :ref:`neb1`
+        * :ref:`neb2`
+
+
+.. note::
+
+  If there are `M` images and each image has `N` atoms, then the NEB
+  object behaves like one big Atoms object with `MN` atoms, so its
+  :meth:`get_positions` method will return a `MN \times 3` array.
+
+
+Trajectories
+============
+
+The line::
+
+  optimizer = QuasiNewton(neb, trajectory='A2B.traj')
+
+will write all images to one file.  The Trajector object knows about
+NEB calculations, so it will write `M` images with `N` atoms at every
+iteration and not one big configuration containing `MN` atoms.
+
+The result of the latest iteration can now be analysed with this
+command: :command:`ag A2B.traj at -5:`.
+
+For the example above, you can write the images to individual
+trajectory files like this::
+
+  for i in range(1, 4):
+      qn.attach(PickleTrajectory('A2B-%d.traj' % d, 'w', images[i])
+
+The result of the latest iteration can be analysed like this:
+
+.. highlight:: bash
+
+::
+
+  $ ag A.traj A2B-?.traj B.traj -n -1 
+
+.. highlight:: python
+
+
+Restarting
+==========
+
+Restart the calculation like this::
+
+  images = read('A2B.traj at -5:')
+
+
+
+Climbing image
+==============
+
+The "climbing image" variation involves designating a specific image to behave
+differently to the rest of the chain: it feels no spring forces, and the
+component of the potential force parallel to the chain is reversed, such that
+it moves towards the saddle point. This depends on the adjacent images
+providing a reasonably good approximation of the correct tangent at the
+location of the climbing image; thus in general the climbing image is not
+turned on until some iterations have been run without it (generally 20% to 50%
+of the total number of iterations).
+
+To use the climbing image NEB method, instantiate the NEB object like this::
+
+  neb = NEB(images, climb=True)
+
+.. note::
+
+  Quasi-Newton methods, such as BFGS, are not well suited for climbing image
+  NEB calculations. FIRE have been known to give good results, although
+  convergence is slow.
+
+
+Parallelization over images
+===========================
+
+Some calculators can parallelize over the images of a NEB calculation.
+The script will have to be run with an MPI-enabled Python interpreter
+like GPAW_'s gpaw-python_.  All images exist on all processors, but
+only some of them have a calculator attached::
+
+  from ase.parallel import rank, size
+  # Number of internal images:
+  n = len(images) - 2
+  j = rank * n // size
+  for i, image in enumerate(images[1:-1]):
+      if i == j:
+          image.set_calculator(EMT())
+
+Create the NEB object with ``NEB(images, parallel=True)`` and let the
+master processes write the images::
+
+  if rank % (size // n) == 0:
+      traj = PickleTrajectory('neb%d.traj' % j, 'w', images[1 + j],
+                              master=True)
+      optimizer.attach(traj)
+
+For a complete example using GPAW_, see the here_.
+
+.. _GPAW: http://wiki.fysik.dtu.dk/gpaw
+.. _gpaw-python: https://wiki.fysik.dtu.dk/gpaw/documentation/manual.html#parallel-calculations
+.. _here: https://wiki.fysik.dtu.dk/gpaw/tutorials/neb/neb.html
+
+.. default-role::
diff --git a/doc/ase/optimize.rst b/doc/ase/optimize.rst
new file mode 100644
index 0000000..0eb9af0
--- /dev/null
+++ b/doc/ase/optimize.rst
@@ -0,0 +1,296 @@
+.. _structure_optimizations:
+
+======================
+Structure optimization
+======================
+
+The optimization algorithms can be roughly devided into local
+optimization algorithms which find the next local minimum and
+global optimization algorithms that try to find the global
+minimum (a much harder task).
+
+
+.. seealso::
+
+    `Performance test
+    <https://wiki.fysik.dtu.dk/gpaw/devel/ase_optimize/ase_optimize.html>`_ for all
+    ASE optimizers.
+ 
+
+
+Local optimization
+==================
+.. module:: optimize
+   :synopsis: Structure Optimization
+
+There are currently 5 different optimization algorithms available:
+``BFGS``, ``LBFGS``, ``BFGSLineSearch``, ``LBFGSLineSearch``,
+``MDMin``, and ``FIRE``.
+
+``MDMin`` and ``FIRE`` both use Newtonian dynamics with added
+friction, to converge to an energy minimum, whereas the first 3 are of
+the quasi-Newton type, where the forces of consecutive steps are used
+to dynamically update a Hessian describing the curvature of the
+potential energy landscape.  You can use the ``QuasiNewton`` synonym
+for ``BFGSLineSearch`` because this algorithm is in many cases the optimal one
+of the three quasi-Newton algorithms.
+
+All optimizer classes have the following structure::
+
+  class Optimizer:
+      def __init__(self, atoms, restart=None, logfile=None):
+      def run(self, fmax=0.05, steps=100000000):
+      def get_number_of_steps():
+
+The convergence criterion is that the force on all individual atoms
+should be less than *fmax*:
+
+.. math:: \max_a |\vec{F_a}| < f_\text{max}
+
+
+BFGS
+----
+.. module:: optimize.qn
+   :synopsis: Quasi-Newton
+
+The ``BFGS`` object is one of the minimizers in the ASE
+package.  Let's try to use it to optimize the structure of a water
+molecule.  We start with the experimental geometry::
+
+  from ase import *
+  import numpy as np
+  d = 0.9575
+  t = pi / 180 * 104.51
+  water = Atoms('H2O',
+                positions=[(d, 0, 0),
+                           (d * np.cos(t), d * np.sin(t), 0),
+                           (0, 0, 0)],
+                calculator=EMT())
+  dyn = BFGS(water)
+  dyn.run(fmax=0.05)
+  BFGS:   0  16:14:26        6.445801      51.6847
+  BFGS:   1  16:14:26        2.418583      27.2946
+  BFGS:   2  16:14:26        0.620874      13.0140
+  BFGS:   3  16:14:26       -0.028619       4.4019
+  BFGS:   4  16:14:26       -0.129349       0.7307
+  BFGS:   5  16:14:26       -0.132320       0.0138
+
+When doing structure optimization, it is useful to write the
+trajectory to a file, so that the progress of the optimization run can
+be followed during or after the run::
+
+  dyn = BFGS(water, trajectory='H2O.traj')
+  dyn.run(fmax=0.05)
+  
+Use the command ``ag H2O.traj`` to see what is going on (more here:
+:mod:`gui`).  The trajectory file can also be accessed using the
+module :mod:`ase.io.trajectory`.
+
+The ``attach`` method takes an optional argument ``interval=n`` that can
+be used to tell the structure optimizer object to write the
+configuration to the trajectory file only every ``n`` steps.
+
+During a structure optimization, the :class:`BFGS` and
+:class:`LBFGS` optimizers use two quantities to decide where to move
+the atoms on each step:
+
+ * the forces on each atom, as returned by the associated :class:`Calculator`
+   object
+ * the Hessian matrix, i.e. the matrix of second derivatives
+   :math:`\frac{\partial^2 E}{\partial x_i \partial x_j}` of the
+   total energy with respect to nuclear coordinates.
+
+If the atoms are close to the minimum, such that the potential energy
+surface is locally quadratic, the Hessian and forces accurately
+determine the required step to reach the optimal structure.  The
+Hessian is very expensive to calculate *a priori*, so instead the
+algorithm estimates it by means of an initial guess which is adjusted
+along the way depending on the information obtained on each step of
+the structure optimization.
+
+It is frequently practical to restart or continue a structure
+optimization with a geometry obtained from a previous relaxation.
+Aside from the geometry, the Hessian of the previous run can and
+should be retained for the second run.  Use the ``restart`` keyword to
+specify a file in which to save the Hessian::
+
+  dyn = BFGS(system, trajectory='qn.traj', restart='qn.pckl')
+
+This will create an optimizer which saves the Hessian to
+:file:`qn.pckl` (using the Python :mod:`pickle` module) on each
+step.  If the file already exists, the Hessian will also be
+*initialized* from that file.
+
+The trajectory file can also be used to restart a structure
+optimization, since it contains the history of all forces and
+positions, and thus whichever information about the Hessian was
+assembled so far::
+
+  dyn = BFGS(system, trajectory='qn.traj')
+  dyn.replay_trajectory('history.traj')
+
+This will read through each iteration stored in :file:`history.traj`,
+performing adjustments to the Hessian as appropriate.  Note that these
+steps will not be written to :file:`qn.traj`.  If restarting with more than
+one previous trajectory file, use :command:`ag` to concatenate them
+into a single trajectory file first::
+
+  $ ag part1.traj part2.traj -o history.traj
+
+The file :file:`history.traj` will then contain all necessary
+information.
+
+When switching between different types of optimizers, e.g. between
+``BFGS`` and ``LBFGS``, the pickle-files specified by the
+``restart`` keyword are not compatible, but the Hessian can still be
+retained by replaying the trajectory as above.
+
+.. note::
+
+   In many of the examples, tests, exercises and tutorials,
+   ``QuasiNewton`` is used -- it is a synonym for ``BFGS``.
+
+
+LBFGS
+-----
+.. module:: optimize.lbfgs
+
+LBFGS is the limited memory version of the BFGS algorithm, where 
+the inverse of Hessian matrix is updated instead of the Hessian
+itself. Two ways exist for determining the atomic
+step: Standard ``LBFGS`` and ``LBFGSLineSearch``. For the 
+first one, both the directions and lengths of the atomic steps 
+are determined by the approximated Hessian matrix. While for the 
+latter one, the approximated Hessian matrix is only used to find 
+out the directions of the line searches and atomic steps, the 
+step lengths are determined by the forces. 
+
+To start a structure optimization with LBFGS algorithm is similar to
+BFGS. A typical optimization should look like::
+
+  dyn = LBFGS(system, trajectory='lbfgs.traj', restart='lbfgs.pckl')
+
+where the trajectory and the restart save the trajectory of the 
+optimization and the vectors needed to generate the Hessian Matrix.
+
+
+FIRE
+----
+.. module:: optimize.fire
+
+Read about this algorithm here:
+
+  | Erik Bitzek, Pekka Koskinen, Franz Gähler, Michael Moseler, and Peter Gumbsch
+  | `Structural Relaxation Made Simple`__
+  | Physical Review Letters, Vol. **97**, 170201 (2006)
+
+__ http://dx.doi.org/10.1103/PhysRevLett.97.170201
+
+
+MDMin
+-----
+.. module:: optimize.mdmin
+
+The MDmin algorithm is a modification of the usual velocity-Verlet
+molecular dynamics algorithm.  Newtons second law is solved
+numerically, but after each time step the dot product between the
+forces and the momenta is checked.  If it is zero, the system has just
+passed through a (local) minimum in the potential energy, the kinetic
+energy is large and about to decrease again.  At this point, the
+momentum is set to zero.  Unlike a "real" molecular dynamics, the
+masses of the atoms are not used, instead all masses are set to one.
+
+The MDmin algorithm exists in two flavors, one where each atom is
+tested and stopped individually, and one where all coordinates are
+treated as one long vector, and all momenta are set to zero if the
+dotproduct between the momentum vector and force vector (both of
+length 3N) is zero.  This module implements the latter version.
+
+Although the algorithm is primitive, it performs very well because it
+takes advantage of the physics of the problem.  Once the system is so
+near the minimum that the potential energy surface is approximately
+quadratic it becomes advantageous to switch to a minimization method
+with quadratic convergence, such as `Conjugate Gradient` or `Quasi
+Newton`.
+
+
+SciPy optimizers
+----------------
+.. module:: optimize.sciopt
+
+SciPy provides a number of optimizers. An interface module for a couple of
+these have been written for ASE. Most notable are the optimizers SciPyFminBFGS
+and SciPyFminCG. These are called with the regular syntax and can be imported
+as::
+
+  from ase.optimize.sciopt import SciPyFminBFGS, SciPyFminCG
+
+.. autoclass:: ase.optimize.sciopt.SciPyFminBFGS
+.. autoclass:: ase.optimize.sciopt.SciPyFminCG
+
+.. seealso::
+
+  :epydoc:`optimize.sciopt.SciPyFminBFGS`, 
+  :epydoc:`optimize.sciopt.SciPyFminCG`
+
+
+BFGSLineSearch
+--------------
+.. module:: optimize.bfgslinesearch
+
+BFGSLineSearch is the BFGS algorithm with an line search mechanism
+that enforces the step taken fulfills the Wolfe conditions, so that
+the energy and absolute value of the force decrease monotonically. Like
+the lbfgs algorithm the inverse of the Hessian Matrix is updated.
+
+The usage of BFGSLineSearch algorithm is similar to other BFGS type
+algorithms. A typical optimization should look like::
+
+  from ase.optimize.bfgslinesearch import BFGSLineSearch
+
+  dyn = BFGSLineSearch(system, trajectory='bfgs_ls.traj', restart='bfgs_ls.pckl')
+
+where the trajectory and the restart save the trajectory of the
+optimization and the information needed to generate the Hessian Matrix.
+
+
+Global optimization
+===================
+
+There is currently one global optimisation algorithm available.
+
+
+Basin hopping
+-------------
+.. module:: optimize.basin
+
+The global optimization algorithm can be used quite similar as a 
+local optimization algorithm::
+
+  from ase import *
+  from ase.optimize.basin import BasinHopping
+
+  bh = BasinHopping(system,               # the system to optimize 
+                    temperature=100 * kB, # 'temperature' to overcome barriers
+                    dr=0.5,               # maximal stepwidth
+	       	    optimizer=LBFGS,      # optimizer to find local minima
+		    fmax=0.1,             # maximal force for the optimizer
+                    )
+
+Read more about this algorithm here:
+
+  | David J. Wales and Jonathan P. K. Doye
+  | `Global Optimization by Basin-Hopping and the Lowest Energy Structures of Lennard-Jones Clusters Containing up to 110 Atoms`__
+  | J. Phys. Chem. A, Vol. **101**, 5111-5116 (1997)
+
+__ http://pubs.acs.org/doi/abs/10.1021/jp970984n
+
+and here:
+
+  | David J. Wales and Harold A. Scheraga
+  | `Global Optimization of Clusters, Crystals, and Biomolecules`__
+  | Science, Vol. **285**, 1368 (1999)
+
+__ http://www.sciencemag.org/cgi/content/abstract/sci;285/5432/1368
+
+
diff --git a/doc/ase/parallel.rst b/doc/ase/parallel.rst
new file mode 100644
index 0000000..8c1281e
--- /dev/null
+++ b/doc/ase/parallel.rst
@@ -0,0 +1,9 @@
+.. module:: parallel
+
+=====================
+Parallel calculations
+=====================
+
+.. autofunction:: ase.parallel.paropen
+
+.. autofunction:: ase.parallel.parprint
diff --git a/doc/ase/phonons.py b/doc/ase/phonons.py
new file mode 100644
index 0000000..b49371a
--- /dev/null
+++ b/doc/ase/phonons.py
@@ -0,0 +1,89 @@
+# creates: Al_phonon.png Al_mode.gif Al_mode.pdf
+
+from ase.lattice import bulk
+from ase.calculators.emt import EMT
+from ase.dft.kpoints import ibz_points, get_bandpath
+from ase.phonons import Phonons
+
+# Setup crystal and EMT calculator
+atoms = bulk('Al', a=4.05)
+calc = EMT()
+
+# Phonon calculator
+N = 6
+ph = Phonons(atoms, calc, supercell=(N, N, N))
+ph.run()
+
+# Read forces and assemble the dynamical matrix
+ph.read(acoustic=True)
+
+# High-symmetry points in the Brillouin zone
+points = ibz_points['fcc']
+G = points['Gamma']
+X = points['X']
+W = points['W']
+K = points['K']
+L = points['L']
+U = points['U']
+
+point_names = ['$\Gamma$', 'X', 'U', 'L', '$\Gamma$', 'K']
+path = [G, X, U, L, G, K]
+path_kc, q, Q = get_bandpath(path, atoms.cell, 100)
+omega_kn = 1000 * ph.band_structure(path_kc)
+
+# DOS
+omega_e, dos_e = ph.dos(kpts=(50, 50, 50), npts=5000, delta=1e-4)
+omega_e *= 1000
+
+# Plot phonon dispersion
+import matplotlib
+matplotlib.use('Agg')
+import pylab as plt
+
+plt.figure(1, (8, 6))
+plt.axes([.1, .07, .67, .85])
+for n in range(len(omega_kn[0])):
+    omega_n = omega_kn[:, n]
+    plt.plot(q, omega_n, 'k-', lw=2)
+
+plt.xticks(Q, point_names, fontsize=18)
+plt.yticks(fontsize=18)
+plt.xlim(q[0], q[-1])
+plt.ylim(0, 35)
+plt.ylabel("Frequency ($\mathrm{meV}$)", fontsize=22)
+plt.grid('on')
+
+plt.axes([.8, .07, .17, .85])
+plt.fill_between(dos_e, omega_e, y2=0, color='lightgrey', edgecolor='k', lw=2)
+plt.ylim(0, 35)
+plt.xticks([], [])
+plt.yticks([], [])
+plt.xlabel("DOS", fontsize=18)
+plt.savefig('Al_phonon.png')
+
+# Write modes for specific q-vector to trajectory files
+ph.write_modes([l/2 for l in L], branches=[2], repeat=(8, 8, 8), kT=3e-4,
+               center=True)
+
+# Generate png animation
+from subprocess import call
+from ase.io import PickleTrajectory, write
+
+trajfile = 'phonon.mode.2.traj'
+trajectory = PickleTrajectory(trajfile, 'r')
+
+for i, atoms in enumerate(trajectory):
+    write('picture%02i.png' %i, atoms, show_unit_cell=2,
+          rotation='-36x,26.5y,-25z')
+    # Flatten images for better quality
+    call(['convert', '-flatten', 'picture%02i.png' %i, 'picture%02i.png' %i])
+
+# Make static pdf image for pdflatex
+call(['convert', 'picture00.png', 'Al_mode.pdf'])
+
+# Concatenate to gif animation
+call(['convert', '-delay', '5', '-loop', '0', '-dispose', 'Previous', 'picture*.png',
+      'Al_mode.gif'])
+
+
+
diff --git a/doc/ase/phonons.rst b/doc/ase/phonons.rst
new file mode 100644
index 0000000..b5ec00b
--- /dev/null
+++ b/doc/ase/phonons.rst
@@ -0,0 +1,96 @@
+.. module:: phonons
+
+Phonon calculations
+-------------------
+
+Module for calculating vibrational normal modes for periodic systems using the
+so-called small displacement method (see e.g. [Alfe]_). So far, space-group
+symmetries are not exploited to reduce the number of atomic displacements that
+must be calculated and subsequent symmetrization of the force constants.
+
+For polar materials the dynamical matrix at the zone center acquires a
+non-analytical contribution that accounts for the LO-TO splitting. This
+contribution requires additional functionality to evaluate and is not included
+in the present implementation. Its implementation in conjunction with the small
+displacement method is described in [Wang]_.
+
+
+Example
+-------
+
+Simple example showing how to calculate the phonon dispersion for bulk aluminum
+using a 7x7x7 supercell within effective medium theory::
+
+  from ase.structure import bulk
+  from ase.calculators.emt import EMT
+  from ase.dft.kpoints import ibz_points, get_bandpath
+  from ase.phonons import Phonons
+  
+  # Setup crystal and EMT calculator
+  atoms = bulk('Al', 'fcc', a=4.05)
+  calc = EMT()
+  
+  # Phonon calculator
+  N = 7
+  ph = Phonons(atoms, calc, supercell=(N, N, N), delta=0.05)
+  ph.run()
+  
+  # Read forces and assemble the dynamical matrix
+  ph.read(acoustic=True)
+  
+  # High-symmetry points in the Brillouin zone
+  points = ibz_points['fcc']
+  G = points['Gamma']
+  X = points['X']
+  W = points['W']
+  K = points['K']
+  L = points['L']
+  U = points['U']
+
+  point_names = ['$\Gamma$', 'X', 'U', 'L', '$\Gamma$', 'K']
+  path = [G, X, U, L, G, K]
+
+  # Band structure in meV
+  path_kc, q, Q = get_bandpath(path, atoms.cell, 100)
+  omega_kn = 1000 * ph.band_structure(path_kc)
+
+  # Calculate phonon DOS
+  omega_e, dos_e = ph.dos(kpts=(50, 50, 50), npts=5000, delta=5e-4)
+  omega_e *= 1000
+
+  # Plot the band structure and DOS
+  import pylab as plt
+  plt.figure(1, (8, 6))   
+  plt.axes([.1, .07, .67, .85])
+  for n in range(len(omega_kn[0])):
+      omega_n = omega_kn[:, n]
+      plt.plot(q, omega_n, 'k-', lw=2)
+
+  plt.xticks(Q, point_names, fontsize=18)
+  plt.yticks(fontsize=18)
+  plt.xlim(q[0], q[-1])
+  plt.ylabel("Frequency ($\mathrm{meV}$)", fontsize=22)
+  plt.grid('on')
+
+  plt.axes([.8, .07, .17, .85])
+  plt.fill_between(dos_e, omega_e, y2=0, color='lightgrey', edgecolor='k', lw=1)
+  plt.ylim(0, 35)
+  plt.xticks([], [])
+  plt.yticks([], [])
+  plt.xlabel("DOS", fontsize=18)
+  plt.show()
+
+.. image:: Al_phonon.png
+
+Mode inspection using ag::
+  
+  # Write modes for specific q-vector to trajectory files  
+  ph.write_modes([l/2 for l in L], branches=[2], repeat=(8, 8, 8), kT=3e-4)
+
+.. image:: Al_mode.*
+
+.. [Alfe] D. Alfe, PHON: A program to calculate phonons using the small
+          displacement method, Comput. Phys. Commun. 180, 2622 (2009)
+.. [Wang] Y. Wang *et al.*, A mixed-space approach to first-principles
+          calculations of phonon frequencies for polar materials, J. Phys.:
+          Cond. Matter 22, 202201 (2010)
\ No newline at end of file
diff --git a/doc/ase/save_pov.py b/doc/ase/save_pov.py
new file mode 100644
index 0000000..6be5bd2
--- /dev/null
+++ b/doc/ase/save_pov.py
@@ -0,0 +1,58 @@
+# creates: NaCl_C6H6.png
+
+import numpy as np
+
+from ase import Atoms
+from ase.io import write
+from ase.data.molecules import molecule
+
+a = 5.64 # Lattice constant for NaCl
+cell = [a / np.sqrt(2), a / np.sqrt(2), a]
+atoms = Atoms(symbols='Na2Cl2', pbc=True, cell=cell,
+              scaled_positions=[(.0, .0, .0),
+                                (.5, .5, .5),
+                                (.5, .5, .0),
+                                (.0, .0, .5),]) * (3, 4, 2) + molecule('C6H6')
+
+# Move molecule to 3.5Ang from surface, and translate one unit cell in xy
+atoms.positions[-12:, 2] += atoms.positions[:-12, 2].max() + 3.5
+atoms.positions[-12:, :2] += cell[:2]
+
+# Mark a single unit cell
+atoms.cell = cell
+
+# View used to start ag, and find desired viewing angle
+#view(atoms)
+rot='35x,63y,36z' # found using ag: 'view -> rotate'
+
+# Common kwargs for eps, png, pov
+kwargs = {
+    'rotation'      : rot, # text string with rotation (default='' )
+    'radii'         : .85, # float, or a list with one float per atom
+    'colors'        : None,# List: one (r, g, b) tuple per atom
+    'show_unit_cell': 2,   # 0, 1, or 2 to not show, show, and show all of cell
+    }
+
+# Extra kwargs only avaliable for povray (All units in angstrom)
+kwargs.update({
+    'run_povray'   : True, # Run povray or just write .pov + .ini files
+    'display'      : False,# Display while rendering
+    'pause'        : True, # Pause when done rendering (only if display)
+    'transparent'  : False,# Transparent background
+    'canvas_width' : None, # Width of canvas in pixels
+    'canvas_height': None, # Height of canvas in pixels 
+    'camera_dist'  : 50.,  # Distance from camera to front atom
+    'image_plane'  : None, # Distance from front atom to image plane
+    'camera_type'  : 'perspective', # perspective, ultra_wide_angle
+    'point_lights' : [],             # [[loc1, color1], [loc2, color2],...]
+    'area_light'   : [(2., 3., 40.), # location
+                      'White',       # color
+                      .7, .7, 3, 3], # width, height, Nlamps_x, Nlamps_y
+    'background'   : 'White',        # color
+    'textures'     : None, # Length of atoms list of texture names
+    'celllinewidth': 0.1,  # Radius of the cylinders representing the cell
+    })
+   
+# Write the .pov (and .ini) file. If run_povray=False, you must run command
+# `povray filename.ini` to convert .pov file to .png
+write('NaCl_C6H6.pov', atoms, **kwargs)
diff --git a/doc/ase/setup-overview.rst b/doc/ase/setup-overview.rst
new file mode 100644
index 0000000..5a072df
--- /dev/null
+++ b/doc/ase/setup-overview.rst
@@ -0,0 +1,65 @@
+==========================
+Creating atomic structures
+==========================
+
+.. toctree::
+   :maxdepth: 2
+
+   structure
+   surface
+   lattice
+   molecules
+
+ASE contains a number of modules for setting up atomic structures,
+mainly molecules, bulk crystals and surfaces.  Some of these modules
+have overlapping functionality, but strike a different balance between
+flexibility and ease-of-use.
+
+
+**Common bulk crystals**
+
+The :func:`ase.structure.bulk` function can be used to create the most
+common bulk crystal structures.  The function creates a single unit cell
+oriented such that the number of atoms in the cell is minimal.
+
+Read more: :ref:`bulk-crystal-section`.
+
+
+**Common surfaces**
+
+The :mod:`lattice.surface` module contains a number of 
+functions for creating the most common surfaces in a minimal unit
+cell, and for adding adsorbates to these surfaces.
+
+Read more: :ref:`lattice-surface-section`.
+
+
+**Nanotubes and nanoribbons**
+
+The functions :func:`ase.structure.nanotube` and
+:func:`ase.structure.graphene_nanoribbon` can be used to create Carbon
+nanotubes and graphene sheets or nanoribbons.  Per default, they
+create Carbon nanotubes and sheets, but other elements can be used. 
+
+Read more:  :ref:`nanotubes-section` and :ref:`nanoribbons-section`.
+
+**Generally oriented bulk crystals and surfaces**
+
+The :mod:`lattice` module contains functions for creating most common
+crystal structures with arbitrary orientation.  The user can specify
+the desired Miller index along the three axes of the simulation, and
+the smallest periodic structure fulfilling this specification is
+created.  Thirteen of the 14 Bravais lattices are supported by the
+module, as are a few lattices with a basis, and lattices for some of
+the most common compounds/alloys.  The modules makes it possible to
+define further lattices based on the supported Bravais lattices.
+
+Both bulk crystals and surfaces can be created.
+
+Read more: :ref:`general-crystal-section`.
+
+**Molecules**
+
+Some common molecules are available in the :mod:`~data.molecules` module.
+
+Read more: :ref:`molecules-section`.
diff --git a/doc/ase/structure.py b/doc/ase/structure.py
new file mode 100644
index 0000000..a3ac117
--- /dev/null
+++ b/doc/ase/structure.py
@@ -0,0 +1,34 @@
+# creates: a1.png a2.png a3.png cnt1.png cnt2.png gnr1.png gnr2.png
+from ase.io import write
+from ase.structure import bulk, nanotube, graphene_nanoribbon
+import numpy as np
+
+for i, a in enumerate([
+    bulk('Cu', 'fcc', a=3.6),
+    bulk('Cu', 'fcc', a=3.6, orthorhombic=True),
+    bulk('Cu', 'fcc', a=3.6, cubic=True)]):
+    write('a%d.pov' % (i + 1), a,
+          show_unit_cell=2, display=False, run_povray=True)
+
+cnt1 = nanotube(6, 0, length=4)
+cnt1.rotate('x', 'z', rotate_cell=True)
+cnt2 = nanotube(3, 3, length=6, bond=1.4, symbol='Si')
+cnt2.rotate('x', 'z', rotate_cell=True)
+
+for i, a in enumerate([cnt1, cnt2]):
+    write('cnt%d.pov' % (i + 1), a,
+          show_unit_cell=2, display=False, run_povray=True)
+
+ind = [2, 0, 1]
+gnr1 = graphene_nanoribbon(3, 4, type='armchair')
+gnr1.set_cell(np.diag(gnr1.cell)[ind])
+gnr1.positions = gnr1.positions[:, ind]
+gnr2 = graphene_nanoribbon(2, 6, type='zigzag', saturated=True,
+                           C_H=1.1, C_C=1.4, vacuum=3.0, 
+                           magnetic=True, initial_mag=1.12)
+gnr2.set_cell(np.diag(gnr2.cell)[ind])
+gnr2.positions = gnr2.positions[:, ind]
+
+for i, a in enumerate([gnr1, gnr2]):
+    write('gnr%d.pov' % (i + 1), a,
+          show_unit_cell=2, display=False, run_povray=True)
diff --git a/doc/ase/structure.rst b/doc/ase/structure.rst
new file mode 100644
index 0000000..3f70e96
--- /dev/null
+++ b/doc/ase/structure.rst
@@ -0,0 +1,97 @@
+.. module:: structure
+
+.. seealso:: 
+
+   * The :mod:`lattice` module
+   * The :mod:`~lattice.spacegroup` module
+   * The :mod:`~lattice.surface` module
+
+
+.. _bulk-crystal-section:
+
+Common bulk crystals
+====================
+
+.. autofunction:: ase.structure.bulk
+
+examples:
+
+>>> from ase.structure import bulk
+>>> a1 = bulk('Cu', 'fcc', a=3.6)
+>>> a2 = bulk('Cu', 'fcc', a=3.6, orthorhombic=True)
+>>> a3 = bulk('Cu', 'fcc', a=3.6, cubic=True)
+>>> a1.cell
+array([[ 0. ,  1.8,  1.8],
+       [ 1.8,  0. ,  1.8],
+       [ 1.8,  1.8,  0. ]])
+>>> a2.cell
+array([[ 2.54558441,  0.        ,  0.        ],
+       [ 0.        ,  2.54558441,  0.        ],
+       [ 0.        ,  0.        ,  3.6       ]])
+>>> a3.cell
+array([[ 3.6,  0. ,  0. ],
+       [ 0. ,  3.6,  0. ],
+       [ 0. ,  0. ,  3.6]])
+
+|a1| |a2| |a3|
+
+.. |a1| image:: a1.png
+.. |a2| image:: a2.png
+.. |a3| image:: a3.png
+
+
+.. _nanotubes-section:
+
+Nanotubes
+=========
+
+.. autofunction:: ase.structure.nanotube
+
+examples:
+
+>>> from ase.structure import nanotube
+>>> cnt1 = nanotube(6, 0, length=4)
+>>> cnt2 = nanotube(3, 3, length=6, bond=1.4, symbol='Si')
+
+|cnt1| |cnt2|
+
+.. |cnt1| image:: cnt1.png
+.. |cnt2| image:: cnt2.png
+
+.. _nanoribbons-section:
+
+Graphene nanoribbons
+====================
+
+.. autofunction:: ase.structure.graphene_nanoribbon
+
+examples:
+
+>>> from ase.structure import graphene_nanoribbon
+>>> gnr1 = graphene_nanoribbon(3, 4, type='armchair')
+>>> gnr2 = graphene_nanoribbon(2, 6, type='zigzag', saturated=True,
+>>>                             C_H=1.1, C_C=1.4, vacc=6.0, 
+>>>                            magnetic=True,initial_mag=1.12)                     
+
+|gnr1| |gnr2|
+
+.. |gnr1| image:: gnr1.png
+.. |gnr2| image:: gnr2.png
+
+Special points in the Brillouin zone
+====================================
+
+You can find the psecial points in the Brillouin zone:
+
+>>> from ase.structure import bulk
+>>> from ase.dft.kpoints import ibz_points, get_bandpath
+>>> si = bulk('Si', 'diamond', a=5.459)
+>>> points = ibz_points['fcc']
+>>> G = points['Gamma']
+>>> X = points['X']
+>>> W = points['W']
+>>> K = points['K']
+>>> L = points['L']
+>>> kpts, x, X = get_bandpath([W, L, G, X, W, K], si.cell)
+>>> print len(kpts), len(x), len(X)
+50 50 6
diff --git a/doc/ase/surface.py b/doc/ase/surface.py
new file mode 100644
index 0000000..52b0ab7
--- /dev/null
+++ b/doc/ase/surface.py
@@ -0,0 +1,42 @@
+# creates: fcc100.png fcc110.png bcc100.png fcc111.png bcc110.png bcc111.png hcp0001.png fcc111o.png bcc110o.png bcc111o.png hcp0001o.png ontop-site.png hollow-site.png fcc-site.png hcp-site.png bridge-site.png diamond100.png diamond111.png hcp10m10.png
+
+import os
+from ase import Atoms
+from ase.io import write
+from ase.lattice.surface import (fcc100, fcc110, bcc100, fcc111, 
+                                 bcc110, bcc111, hcp0001, hcp10m10,
+                                 diamond100, diamond111, add_adsorbate)
+
+
+surfaces = ['fcc100', 'fcc110', 'bcc100', 'hcp10m10', 'diamond100',
+            'fcc111', 'bcc110', 'bcc111', 'hcp0001', 'diamond111']
+
+symbols = {'fcc': 'Cu', 'bcc': 'Fe', 'hcp': 'Ru', 'dia': 'C'}
+radii = {'fcc': 1.1, 'bcc': 1.06, 'hcp': 1.08, 'dia': 0.5}
+adsorbates= {'ontop': 'H', 'hollow': 'O', 'fcc': 'N', 'hcp': 'C',
+             'bridge': 'F'}
+
+def save(name, slab):
+    write(name + '.png', slab, show_unit_cell=2, radii=radii[name[:3]],
+          scale=10)
+
+for name in surfaces:
+    f = eval(name)
+    for kwargs in [{}, {'orthogonal': True}]:
+        print name, kwargs
+        try:
+            slab = f(symbols[name[:3]], (3, 4, 5), vacuum=4, **kwargs)
+        except (TypeError, NotImplementedError):
+            continue
+        for site in slab.adsorbate_info['sites']:
+            if site.endswith('bridge'):
+                h = 1.5
+            else:
+                h = 1.2
+            add_adsorbate(slab, adsorbates.get(site, 'F'), h, site)
+        if kwargs:
+            name += 'o'
+        save(name, slab)
+
+for site, symbol in adsorbates.items():
+    write('%s-site.png' % site, Atoms(symbol), radii=1.08, scale=10)
diff --git a/doc/ase/surface.rst b/doc/ase/surface.rst
new file mode 100644
index 0000000..baef175
--- /dev/null
+++ b/doc/ase/surface.rst
@@ -0,0 +1,235 @@
+.. default-role:: math
+
+.. _lattice-surface-section:
+
+========
+Surfaces
+========
+
+.. module:: ase.lattice.surface
+
+
+Common surfaces
+===============
+
+A number of utility functions are provided to set up
+the most common surfaces, to add vacuum layers, and to add adsorbates
+to a surface.  In general, all surfaces can be set up with
+the modules described in the section :ref:`general-crystal-section`, but these
+utility functions make common tasks easier.
+
+
+
+Example
+-------
+
+To setup an Al(111) surface with a hydrogen atom adsorbed in an on-top
+position::
+
+  from ase.lattice.surface import *
+  slab = fcc111('Al', size=(2,2,3), vacuum=10.0)
+
+This will produce a slab 2x2x3 times the minimal possible size, with a
+(111) surface in the z direction.  A 10 Å vacuum layer is added on
+each side.
+
+To set up the same surface with with a hydrogen atom adsorbed in an on-top
+position 1.5 Å above the top layer::
+
+  from ase.lattice.surface import *
+  slab = fcc111('Al', size=(2,2,3))
+  add_adsorbate(slab, 'H', 1.5, 'ontop')
+  slab.center(vacuum=10.0, axis=2)
+
+Note that in this case is is probably not meaningful to use the vacuum
+keyword to fcc111, as we want to leave 10 Å of vacuum *after* the
+adsorbate has been added. Instead, the :meth:`~ase.atoms.Atoms.center` method
+of the :class:`~ase.atoms.Atoms` is used
+to add the vacuum and center the system.
+
+The atoms in the slab will have tags set to the layer number: First layer
+atoms will have tag=1, second layer atoms will have tag=2, and so on.
+Addsorbates get tag=0:
+
+>>> print atoms.get_tags()
+[3 3 3 3 2 2 2 2 1 1 1 1 0]
+
+This can be useful for setting up :mod:`constraints` (see
+:ref:`diffusion_tutorial`).
+
+
+Utility functions for setting up surfaces
+-----------------------------------------
+
+All the functions setting up surfaces take the same arguments.
+
+*symbol*:
+  The chemical symbol of the element to use.
+
+*size*:
+  A tuple giving the system size in units of the minimal unit cell.
+
+*a*: 
+  (optional) The lattice constant.  If specified, it overrides the
+  expermental lattice constant of the element.  Must be specified if
+  setting up a crystal structure different from the one found in
+  nature.
+
+*c*: 
+  (optional) Extra HCP lattice constant.  If specified, it overrides the
+  expermental lattice constant of the element.  Can be specified if
+  setting up a crystal structure different from the one found in
+  nature and an ideal `c/a` ratio is not wanted (`c/a=(8/3)^{1/3}`).
+
+*vacuum*: 
+  The thickness of the vacuum layer.  The specified amount of
+  vacuum appears on both sides of the slab.  Default value is None,
+  meaning not to add any vacuum.  In that case a "vacuum" layer equal
+  to the interlayer spacing will be present on the upper surface of
+  the slab.  Specify ``vacuum=0.0`` to remove it.
+
+*orthogonal*:
+  (optional, not supported by all functions). If specified and true,
+  forces the creation of a unit cell with orthogonal basis vectors.
+  If the default is such a unit cell, this argument is not supported.
+
+Each function defines a number of standard adsorbtion sites that can
+later be used when adding an adsorbate with
+:func:`lattice.surface.add_adsorbate`.
+
+
+The following functions are provided
+````````````````````````````````````
+
+.. function:: fcc100(symbol, size, a=None, vacuum=0.0)
+.. function:: fcc110(symbol, size, a=None, vacuum=0.0)
+.. function:: bcc100(symbol, size, a=None, vacuum=0.0)
+.. function:: hcp10m10(symbol, size, a=None, c=None, vacuum=0.0)
+.. function:: diamond100(symbol, size, a=None, vacuum=0.0)
+
+These allways give orthorhombic cells:
+
+==========  ============
+fcc100      |fcc100|
+fcc110      |fcc110|
+bcc100      |bcc100|
+hcp10m10    |hcp10m10|
+diamond100  |diamond100|
+==========  ============
+
+
+.. function:: fcc111(symbol, size, a=None, vacuum=0.0, orthogonal=False)
+.. function:: bcc110(symbol, size, a=None, vacuum=0.0, orthogonal=False)
+.. function:: bcc111(symbol, size, a=None, vacuum=0.0, orthogonal=False)
+.. function:: hcp0001(symbol, size, a=None, c=None, vacuum=0.0, orthogonal=False)
+.. function:: diamond111(symbol, size, a=None, vacuum=0.0, orthogonal=False)
+
+These can give both non-orthorhombic and orthorhombic cells:
+
+==========  ============  ===============
+fcc111      |fcc111|      |fcc111o|
+bcc110      |bcc110|      |bcc110o|
+bcc111      |bcc111|      |bcc111o|
+hcp0001     |hcp0001|     |hcp0001o|
+diamond111  |diamond111|  not implemented
+==========  ============  ===============
+
+The adsorption sites are marked with:
+
+=======  ========  =====  =====  ========  ===========  ==========
+ontop    hollow    fcc    hcp    bridge    shortbridge  longbridge
+|ontop|  |hollow|  |fcc|  |hcp|  |bridge|  |bridge|     |bridge|
+=======  ========  =====  =====  ========  ===========  ==========
+
+.. |ontop|    image:: ontop-site.png
+.. |hollow|   image:: hollow-site.png
+.. |fcc|      image:: fcc-site.png
+.. |hcp|      image:: hcp-site.png
+.. |bridge|   image:: bridge-site.png
+.. |fcc100|   image:: fcc100.png
+.. |fcc110|   image:: fcc110.png
+.. |bcc100|   image:: bcc100.png
+.. |fcc111|   image:: fcc111.png
+.. |bcc110|   image:: bcc110.png
+.. |bcc111|   image:: bcc111.png
+.. |hcp0001|  image:: hcp0001.png
+.. |fcc111o|  image:: fcc111o.png
+.. |bcc110o|  image:: bcc110o.png
+.. |bcc111o|  image:: bcc111o.png
+.. |hcp0001o| image:: hcp0001o.png
+.. |hcp10m10| image:: hcp10m10.png
+.. |diamond100| image:: diamond100.png
+.. |diamond111| image:: diamond111.png
+
+
+
+Adding new utility functions
+````````````````````````````
+
+If you need other surfaces than the ones above, the easiest is to look
+in the source file surface.py, and adapt one of the existing
+functions.  *Please* contribute any such function that you make
+either by cheking it into SVN or by mailing it to the developers.
+
+
+Adding adsorbates
+-----------------
+
+After a slab has been created, a vacuum layer can be added.  It is
+also possible to add one or more adsorbates.
+
+.. autofunction:: ase.lattice.surface.add_adsorbate
+
+
+.. _general-surface-section:
+
+Create specific non-common surfaces
+===================================
+
+.. versionadded:: 3.5.2
+
+In addition to the most normal surfaces, a function has been
+constructed to create more uncommon surfaces that one could be
+interested in.  It is constructed upon the Miller Indices defining the
+surface and can be used for both fcc, bcc and hcp structures.  The
+theory behind the implementation can be found here:
+:download:`general_surface.pdf`.
+
+
+Example
+-------
+
+To setup a Au(211) surface with 9 layers and 10 Å of vacuum:
+
+.. literalinclude:: general_surface.py
+    :lines: 2-4
+
+This is the easy way, where you use the experimental lattice constant
+for gold bulk structure.  You can write::
+
+    from ase.visualize import view
+    view(s1)
+
+or simply ``s1.edit()`` if you want to see and rotate the structure.
+
+.. image:: s1.png
+
+Next example is a molybdenum bcc(321) surface where we decide what
+lattice constant to use:
+
+.. literalinclude:: general_surface.py
+    :lines: 6-9
+
+.. image:: s2.png
+
+As the last example, creation of alloy surfaces is also very easily
+carried out with this module.  In this example, two :mol:`Pt_3Rh`
+fcc(211) surfaces will be created:
+
+.. literalinclude:: general_surface.py
+    :lines: 11-25
+
+|s3| |s4|
+
+.. |s3| image:: s3.png
+.. |s4| image:: s4.png
diff --git a/doc/ase/thermochemistry.rst b/doc/ase/thermochemistry.rst
new file mode 100644
index 0000000..4bb368f
--- /dev/null
+++ b/doc/ase/thermochemistry.rst
@@ -0,0 +1,165 @@
+.. module:: thermochemistry
+   :synopsis: Thermochemistry module
+
+===============
+Thermochemistry
+===============
+
+ASE contains a :mod:`thermochemistry` module that lets the user derive commonly desired thermodynamic quantities from ASE output and some user-specified parameters. Two cases are currently handled by this module: the ideal-gas limit (in which translational and rotational degrees of freedom are taken into account) and the harmonic limit (generally used for adsorbates, in which all degrees of freedom are treated harmonically). Both cases rely on good vibrational energies being fed to the calculators, which can be calculated with the :mod:`vibrations` module.
+
+Ideal-gas limit
+===============
+
+The thermodynamic quantities of ideal gases are calculated by assuming that all spatial degrees of freedom are separable into translational, rotational, and vibrational degrees of freedom. The :class:`~ase.thermochemistry.IdealGasThermo` class supports calculation of enthalpy (:math:`H`), entropy (:math:`S`), and free energy (:math:`G`), and has the interface listed below.
+
+.. autoclass:: ase.thermochemistry.IdealGasThermo
+   :members:
+
+Example
+-------
+
+The :class:`~ase.thermochemistry.IdealGasThermo` class would generally be called after an energy optimization and a vibrational analysis. The user needs to supply certain parameters if the entropy or free energy are desired, such as the geometry and symmetry number. An example on the nitrogen molecule is::
+
+        from ase.data.molecules import molecule
+        from ase.calculators.emt import EMT
+        from ase.optimize import QuasiNewton
+        from ase.vibrations import Vibrations
+        from ase.thermochemistry import IdealGasThermo
+
+        atoms = molecule('N2')
+        atoms.set_calculator(EMT())
+        dyn = QuasiNewton(atoms)
+        dyn.run(fmax=0.01)
+        electronicenergy = atoms.get_potential_energy()
+
+        vib = Vibrations(atoms)
+        vib.run()
+        vib_energies = vib.get_energies()
+
+        thermo = IdealGasThermo(vib_energies=vib_energies,
+                                electronicenergy=electronicenergy, atoms=atoms, 
+                                geometry='linear', symmetrynumber=2, spin=0)
+        G = thermo.get_free_energy(temperature=298.15, pressure=101325.)
+
+This will give the thermodynamic summary output::
+
+        Enthalpy components at T = 298.15 K:
+        ===============================
+        E_elec                 0.000 eV
+        E_ZPE                  0.116 eV
+        Cv_trans (0->T)        0.039 eV
+        Cv_rot (0->T)          0.026 eV
+        Cv_vib (0->T)          0.000 eV
+        (C_v -> C_p)           0.026 eV
+        -------------------------------
+        H                      0.206 eV
+        ===============================
+
+        Entropy components at T = 298.15 K and P = 101325.0 Pa:
+        =================================================
+        S               T*S
+        S_trans (1 atm)    0.0015579 eV/K        0.464 eV
+        S_rot              0.0004314 eV/K        0.129 eV
+        S_elec             0.0000000 eV/K        0.000 eV
+        S_vib              0.0000001 eV/K        0.000 eV
+        S (1 atm -> P)    -0.0000000 eV/K       -0.000 eV
+        -------------------------------------------------
+        S                  0.0019893 eV/K        0.593 eV
+        =================================================
+
+        Free energy components at T = 298.15 K and P = 101325.0 Pa:
+        =======================
+           H          0.206 eV
+        -T*S         -0.593 eV
+        -----------------------
+           G         -0.387 eV
+        =======================
+
+
+
+
+Harmonic limit
+==============
+
+In the harmonic limit, all degrees of freedom are treated harmonically. The :class:`HarmonicThermo` class supports the calculation of internal energy, entropy, and free energy. This class uses all of the energies given to it in the vib_energies list; this is a list as can be generated with the .get_energies() method of :class:`ase.vibrations.Vibrations`, but the user should take care that all of these energies are real (non-imaginary). The class :class:`HarmonicThermo` has the interface described below.
+
+.. autoclass:: ase.thermochemistry.HarmonicThermo
+   :members:
+
+
+Background
+==========
+
+**Ideal gas.** The conversion of electronic structure calculations to thermodynamic properties in the ideal-gas limit is well documented; see, for example, Chapter 10 of Cramer, 2004. The key equations used in the :class:`~ase.thermochemistry.IdealGasThermo` class are summarized here.
+
+   C.J. Cramer. *Essentials of Computational Chemistry*, Second Edition. Wiley, 2004.
+
+The ideal-gas enthalpy is calculated from extrapolation of the energy at 0 K to the relevant temperature (for an ideal gas, the enthalpy is not a function of pressure):
+
+.. math ::
+   H(T) = E_\text{elec} + E_\text{ZPE} + \int_0^\text{T} C_P \, \text{d}T
+
+where the first two terms are the electronic energy and the zero-point energy, and the integral is over the constant-pressure heat capacity. The heat capacity is separable into translational, rotational, vibrational, and electronic parts (plus a term of :math:`k_\text{B}` to switch from constant-volume to constant-pressure):
+
+.. math ::
+   C_P = k_\text{B} + C_{V\text{,trans}} + C_{V\text{,rot}} + C_{V\text{,vib}} + C_{V\text{,elec}}
+
+The translational heat capacity is 3/2 :math:`k_\text{B}` for a 3-dimensional gas. The rotational heat capacity is 0 for a monatomic species, :math:`k_\text{B}` for a linear molecule, and 3/2 :math:`k_\text{B}` for a nonlinear molecule. In this module, the electronic component of the heat capacity is assumed to be 0. The vibrational heat capacity contains :math:`3N-6` degrees of freedom for nonlinear molecules and :math:`3N-5` degrees of freedom for linear molecules (where :math:`N` is the number of atoms). The integrated form of the vibrational heat capacity is:
+
+.. math ::
+   \int_0^T C_{V,\text{vib}} \text{d}T = \sum_i^\text{vib DOF} \frac{\epsilon_i}{e^{\epsilon_i / k_\text{B} T} - 1 }
+
+where :math:`\epsilon_i` are the energies associated with the vibrational frequencies, :math:`\epsilon_i = h \omega_i`.
+
+The ideal gas entropy can be calculated as a function of temperature and pressure as:
+
+.. math ::
+   S(T,P) &= S(T,P^\circ) - k_\text{B} \ln \frac{P}{P^\circ} \\
+          &= S_\text{trans} + S_\text{rot} + S_\text{elec} + S_\text{vib} - k_\text{B} \ln \frac{P}{P^\circ}
+
+where the translational, rotational, electronic, and vibrational components are calculated as below. (Note that the translational component also includes components from the Stirling approximation, and that the vibrational degrees of freedom are enumerated the same as in the above.)
+
+.. math ::
+   S_\text{trans} = k_\text{B} \left\{ \ln \left[ \left(
+   \frac{2 \pi M k_\text{B} T}{h^2} \right)^{3/2}
+   \frac{k_\text{B} T}{P^\circ} \right] + \frac{5}{2} \right\}
+
+.. math ::
+   S_\text{rot} = \left\{  \begin{array}{ll}
+   0 & \text{, if monatomic} \\
+   k_\text{B} \left[ \ln \left( \frac{8\pi^2 I k_\text{B}T}{\sigma h^2}\right) + 1 \right] & \text{, if linear} \\
+   k_\text{B} \left\{ \ln \left[ \frac{\sqrt{\pi I_\text{A} I_\text{B} I_\text{C}}}{\sigma} \left(\frac{8\pi^2 k_\text{B} T}{h^2}\right)^{3/2}\right] + \frac{3}{2} \right\} & \text{, if nonlinear} \\
+   \end{array}
+   \right.
+   
+.. math ::
+   S_\text{vib} = k_\text{B} \sum_i^\text{vib DOF}
+   \left[ \frac{\epsilon_i}{k_\text{B}T\left(e^{\epsilon_i/k_\text{B}T}-1\right)} - \ln \left( 1 - e^{-\epsilon_i/k_\text{B}T} \right)\right]
+
+.. math ::
+   S_\text{elec} = k_\text{B} \ln \left[
+   2 \times \left(\text{spin multiplicity}\right) + 1\right]
+
+:math:`I_\text{A}` through :math:`I_\text{C}` are the three principle moments of inertia for a non-linear molecule. :math:`I` is the degenerate moment of inertia for a linear molecule. :math:`\sigma` is the symmetry number of the molecule.
+
+The ideal-gas free energy is then just calculated from the combination of the enthalpy and entropy:
+
+.. math ::
+   G(T,P) = H(T) - T\, S(T,P)
+
+**Harmonic limit.** The conversion of electronic structure calculation information into thermodynamic properties is less established for adsorbates. However, the simplest approach often taken is to treat all :math:`3N` degrees of freedom of the adsorbate harmonically since the adsorbate often has no real translational or rotational degrees of freedom. This is the approach implemented in the :class:`~ase.thermochemistry.HarmonicThermo` class. Thus, the internal energy and entropy of the adsorbate are calculated as
+
+.. math ::
+   U(T) = E_\text{elec} + E_\text{ZPE} + \sum_i^\text{harm DOF} \frac{\epsilon_i}{e^{\epsilon_i / k_\text{B} T} - 1 }
+
+.. math ::
+   S = k_\text{B} \sum_i^\text{harm DOF}
+   \left[ \frac{\epsilon_i}{k_\text{B}T\left(e^{\epsilon_i/k_\text{B}T}-1\right)} - \ln \left( 1 - e^{-\epsilon_i/k_\text{B}T} \right)\right]
+
+and the free energy is calculated as
+
+.. math ::
+   G(T,P) = U(T) - T\, S(T,P)
+
+In this case, the number of harmonic energies (:math:`\epsilon_i`) used in the summation is generally :math:`3N`, where :math:`N` is the number of atoms in the adsorbate.
+
diff --git a/doc/ase/trajectory.rst b/doc/ase/trajectory.rst
new file mode 100644
index 0000000..d9e818a
--- /dev/null
+++ b/doc/ase/trajectory.rst
@@ -0,0 +1,78 @@
+.. module:: ase.io.trajectory
+   :synopsis: Trajectory input-output module
+
+================
+Trajectory files
+================
+
+The :mod:`io.trajectory` module defines Trajectory objects, that is
+objects storing the temporal evolution of a simulation.  A Trajectory
+file contains one or more :class:`~ase.atoms.Atoms` objects, usually to be interpreted as
+a time series, although that is not a requirement.
+
+The :mod:`io.trajectory` module currently defines two kinds of
+Trajectory files, the PickleTrajectory and the BundleTrajectory.
+PickleTrajectory is the recommended Trajectory format,
+BundleTrajectory is only intended for large molecular dynamics
+simulations (large meaning millions of atoms).
+
+In the future, other kinds of Trajectories may be defined, with
+similar Python interface but with different underlying file formats.
+
+PickleTrajectory
+================
+
+The PickleTrajectory has the interface
+
+.. autoclass:: ase.io.trajectory.PickleTrajectory
+   :members:
+
+Note that there is apparently no methods for reading the trajectory.
+Reading is instead done by indexing the trajectory, or by iterating
+over the trajectory: ``traj[0]`` and ``traj[-1]`` return the first and
+last :class:`~ase.atoms.Atoms` object in the trajectory.
+
+Examples
+--------
+
+Reading a configuration::
+
+    from ase.io.trajectory import PickleTrajectory
+    traj = PickleTrajectory("example.traj")
+    atoms = traj[-1]
+
+Reading all configurations::
+
+    traj = PickleTrajectory("example.traj")
+    for atoms in traj:
+        # Analyze atoms
+
+Writing every 100th time step in a molecular dynamics simulation::
+
+    # dyn is the dynamics (e.g. VelocityVerlet, Langevin or similar)
+    traj = PickleTrajectory("example.traj", "w", atoms)
+    dyn.attach(traj.write, interval=100)
+    dyn.run(10000)
+    traj.close()
+
+
+BundleTrajectory
+================
+
+The BundleTrajectory has the interface
+
+.. autoclass:: ase.io.bundletrajectory.BundleTrajectory
+   :members:
+
+
+See also
+========
+
+The function :func:`ase.io.write` can write a single
+:class:`~ase.atoms.Atoms` object to a Trajectory file.
+
+The function :func:`ase.io.read` can read an :class:`~ase.atoms.Atoms`
+object from a Trajectory file, per default it reads the last one.
+
+
+
diff --git a/doc/ase/transport/transport.rst b/doc/ase/transport/transport.rst
new file mode 100644
index 0000000..fb094e8
--- /dev/null
+++ b/doc/ase/transport/transport.rst
@@ -0,0 +1,90 @@
+.. module:: transport
+   :synopsis: Electron transport
+
+==================
+Electron transport
+==================
+
+.. default-role:: math
+
+The :mod:`transport` module of ASE assumes the generic setup of the system in
+question sketched below:
+
+. . . |setup| . . .
+
+.. |setup| image:: transport_setup.png
+   :align: middle
+
+There is a central region (blue atoms plus the molecule) connected to
+two semi-infinite leads constructed by infinitely repeated *principal
+layers* (red atoms). The entire structure may be periodic in the
+transverse direction, which can be effectively sampled using
+**k**-points (yellowish atoms).
+
+The system is described by a Hamiltonian matrix which must be
+represented in terms of a localized basis set such that each element
+of the Hamiltonian can be ascribed to either the left, central, or
+right region, *or* the coupling between these.
+
+The Hamiltonian can thus be decomposed as:
+
+.. math::
+
+    H = \begin{pmatrix}
+      \ddots      & V_L         &             &             &     \\
+      V_L^\dagger & H_L         & V_L         &             &     \\
+                  & V_L^\dagger & H_C         & V_R         &     \\
+                  &             & V_R^\dagger & H_R         & V_R \\
+                  &             &             & V_R^\dagger & \ddots
+    \end{pmatrix}
+
+where `H_{L/R}` describes the left/right principal layer, and `H_C`
+the central region. `V_{L/R}` is the coupling between principal
+layers, *and* from the principal layers into the central region.  The
+central region must contain at least one principal layer on each side,
+and more if the potential has not converged to its bulk value at this
+size. The central region is assumed to be big enough that there is no
+direct coupling between the two leads. The principal layer must be so
+big that there is only coupling between nearest neighbor layers.
+
+Having defined `H_{L/R}`, `V_{L/R}`, and `H_C`, the elastic
+transmission function can be determined using the Nonequilibrium
+Green Function (NEGF) method.  This is achieved by the class:
+:class:`~ase.transport.calculators.TransportCalculator` (in
+ase.transport.calculators) which makes no requirement on the origin of
+these five matrices.
+
+.. class:: ase.transport.calculators.TransportCalculator(energies, h, h1, h2, s=None, s1=None, s2=None, align_bf=False)
+
+  Determine transport properties of device sandwiched between
+  semi-infinite leads using nonequillibrium Green function methods.
+
+  energies is the energy grid on which the transport properties should
+  be determined.
+
+  h1 (h2) is a matrix representation of the Hamiltonian of two
+  principal layers of the left (right) lead, and the coupling between
+  such layers.
+
+  h is a matrix representation of the Hamiltonian of the scattering
+  region. This must include at least on lead principal layer on each
+  side. The coupling in (out) of the scattering region is assumed to
+  be identical to the coupling between left (right) principal layers.
+
+  s, s1, and s2 are the overlap matrices corresponding to h, h1, and
+  h2. Default is the identity operator.
+
+  If align_bf is True, the onsite elements of the Hamiltonians will be
+  shifted to a common fermi level.
+
+
+This module is stand-alone in the sense that it makes no requirement
+on the origin of these five matrices. They can be model Hamiltonians
+or derived from different kinds of electronic structure codes.
+
+For an example of how to use the :mod:`transport` module, see the GPAW
+exercise on `electron transport`_
+
+.. _electron transport: http://wiki.fysik.dtu.dk/gpaw/exercises/transport/transport.html
+
+.. default-role::
diff --git a/doc/ase/transport/transport_setup.py b/doc/ase/transport/transport_setup.py
new file mode 100644
index 0000000..25dde9a
--- /dev/null
+++ b/doc/ase/transport/transport_setup.py
@@ -0,0 +1,90 @@
+# creates: transport_setup.png
+
+import numpy as np
+from ase import Atoms
+from ase.data.molecules import molecule
+from ase.io import write
+
+a = 3.92 # Experimental lattice constant
+sqrt = np.sqrt
+cell = np.array([[a / sqrt(3),          0.,               0.],
+                 [         0., a / sqrt(2),               0.],
+                 [         0., a / sqrt(8), a * sqrt(3 / 8.)]])
+repeat = (1, 3, 3)
+
+A = Atoms('Pt', pbc=True, positions=[(0., 0., 0.)])
+B = Atoms('Pt', pbc=True, positions=[(0., 1/3., 1/3.)])
+C = Atoms('Pt', pbc=True, positions=[(0., 2/3., 2/3.)])
+
+A *= repeat
+B *= repeat
+C *= repeat
+
+pyramid_BC = Atoms('Pt4', pbc=True, tags=[1, 1, 1, 2],
+                   positions=[( 0., 1/3., 1/3.), # B
+                              ( 0., 4/3., 1/3.), # B
+                              ( 0., 1/3., 4/3.), # B
+                              ( 1., 2/3., 2/3.), # C
+                              ])
+
+inv_pyramid_BC = pyramid_BC.copy()
+inv_pyramid_BC.positions[:, 0] *= -1
+
+def pos(atoms, x):
+    atoms2 = atoms.copy()
+    atoms2.translate([x, 0, 0])
+    return atoms2
+
+princ = pos(A, 0) + pos(B, 1) + pos(C, 2)
+large = (pos(princ, -8) +
+         pos(princ, -4) +
+         pos(princ, 0) +
+         pos(A, 3) +
+         pos(pyramid_BC, 4) +
+         pos(inv_pyramid_BC, 3) +
+         pos(princ, 4) +
+         pos(princ, 8))
+
+large.set_cell(cell * repeat, scale_atoms=True)
+large.cell[0, 0] = 7 * large.cell[0, 0]
+
+dist=18.
+large.cell[0, 0] += dist - cell[0, 0]
+large.positions[-(9 * 6 + 4):, 0] += dist - cell[0, 0]
+
+tipL, tipR = large.positions[large.get_tags() == 2]
+tipdist = np.linalg.norm(tipL - tipR)
+
+mol = molecule('C6H6', pbc=True, tags=[3] * 6 + [4] * 6)
+mol.rotate('y', 'x')
+mol.rotate('z', 'y')
+
+large += mol
+large.positions[-len(mol):] += tipL
+large.positions[-len(mol):, 0] += tipdist / 2
+
+old = large.cell.copy()
+large *= (1, 1, 3)
+large.set_cell(old)
+
+#view(large)
+
+colors = np.zeros((len(large), 3))
+colors[:] = [1., 1., .75]
+
+pr = [.7, .1, .1]
+H = [1, 1, 1]
+C = [.3, .3, .3]
+Pt = [.7, .7, .9]
+
+colors[164:218] = pr # principal layer
+colors[289:316] = pr # principal layer
+colors[218:289] = Pt # Central region Pt
+colors[316:322] = C # Molecule C
+colors[322:328] = H # Molecule H
+
+
+#write('test.png', large, rotation='-90x,-13y', radii=.9, show_unit_cell=0, colors=colors)
+write('transport_setup.pov', large, rotation='-90x,-13y', radii=1.06,
+      show_unit_cell=0, colors=colors,
+      display=False, transparent=False, run_povray=True)
diff --git a/doc/ase/units.rst b/doc/ase/units.rst
new file mode 100644
index 0000000..8aac8f2
--- /dev/null
+++ b/doc/ase/units.rst
@@ -0,0 +1,54 @@
+.. module:: units
+
+=====
+Units
+=====
+
+Physical units are defined in the :trac:`ase/units.py` module.  Electron volts
+(``eV``) and angstroms (``Ang``) are defined as 1.0.
+Other units are
+``nm``, ``Bohr``, ``Hartree`` or ``Ha``, ``kJ``, ``kcal``, ``mol``,
+``Rydberg`` or ``Ry``, ``second``, ``fs`` and ``kB``.
+
+.. note::
+
+    All constants are taken from the 1986 CODATA_.
+
+.. _CODATA: http://physics.nist.gov/cuu/Constants/archive1986.html
+
+Examples:
+
+>>> from ase import *
+>>> 2 * Bohr
+1.0583545150138329
+>>> 25 * Rydberg
+340.14244569396635
+>>> 100 * kJ/mol
+1.0364272141304978
+>>> 300 * kB
+0.025852157076770025
+>>> 0.1 * fs
+0.009822693531550318
+>>> print '1 Hartree = '+str(Hartree*mol/kcal)+' kcal/mol'
+
+=======================
+The ``ase.data`` module
+=======================
+
+This module defines the following variables: ``atomic_masses``,
+``atomic_names``, ``chemical_symbols``, ``covalent_radii``,
+``cpk_colors`` and ``reference_states``.  All of these are lists that
+should be indexed with an atomic number:
+
+>>> atomic_names[92]
+'Uranium'
+>>> atomic_masses[2]
+4.0026000000000002
+
+If you don't know the atomic number of some element, then you can look
+it up in the ``atomic_numbers`` dictionary:
+
+>>> atomic_numbers['Cu']
+29
+>>> covalent_radii[29]
+1.1699999999999999
diff --git a/doc/ase/utils.rst b/doc/ase/utils.rst
new file mode 100644
index 0000000..597a4ea
--- /dev/null
+++ b/doc/ase/utils.rst
@@ -0,0 +1,34 @@
+.. module:: utils
+
+==============================
+Utillity functions and classes
+==============================
+
+
+.. index:: Bulk modulus
+
+Equation of state
+=================
+
+The :class:`~ase.utils.eos.EquationOfState` class can be used to find
+equilibrium volume, energy, and bulk modulus for solids:
+
+
+.. autoclass:: ase.utils.eos.EquationOfState
+  :members: fit, plot
+
+
+.. seealso::  The :ref:`eos` tutorial.
+
+
+
+Symmetry analysis
+=================
+
+http://spglib.sourceforge.net/pyspglibForASE/
+
+
+Phonons
+=======
+
+http://phonopy.sourceforge.net/
diff --git a/doc/ase/vibrations.rst b/doc/ase/vibrations.rst
new file mode 100644
index 0000000..46d6e92
--- /dev/null
+++ b/doc/ase/vibrations.rst
@@ -0,0 +1,46 @@
+.. module:: vibrations
+
+Vibration analysis
+------------------
+
+You can calculate the vibrational modes of a an
+:class:`~ase.atoms.Atoms` object in the harmonic approximation using
+the :class:`~ase.vibration.Vibrations`.
+
+.. autoclass:: ase.vibrations.Vibrations
+   :members:
+
+name is a string that is prefixed to the names of all the files
+created. atoms is an Atoms object that is either at a
+fully relaxed ground state or at a saddle point. freeatoms is a
+list of atom indices for which the vibrational modes will be calculated,
+the rest of the atoms are considered frozen. displacements is a
+list of displacements, one for each free atom that are used in the
+finite difference method to calculate the Hessian matrix. method is -1
+for backward differences, 0 for centered differences, and 1 for
+forward differences.
+
+.. warning::
+   Using the `dacapo` caculator you must make sure that the symmetry
+   program in dacapo finds the same number of symmetries for the
+   displaced configurations in the vibrational modules as found in
+   the ground state used as input.
+   This is because the wavefunction is reused from one displacement
+   to the next.
+   One way to ensure this is to tell dacapo not to use symmetries.
+
+   This will show op as a python error 'Frames are not aligned'.
+   This could be the case for other calculators as well.
+
+
+You can get a NetCDF trajectory corresponding to a specific mode by
+using:
+
+>>> mode=0
+>>> vib.create_mode_trajectory(mode=mode,scaling=5)
+
+This will create a NetCDF trajectory file `CO_vib_mode_0.traj`,
+corresponding to the highest frequency mode.
+`scaling` is an option argument, that will give the amplitude of
+the mode, default is 10.
+
diff --git a/doc/ase/visualize.rst b/doc/ase/visualize.rst
new file mode 100644
index 0000000..9f62e58
--- /dev/null
+++ b/doc/ase/visualize.rst
@@ -0,0 +1,129 @@
+.. module:: visualize
+
+Visualization
+=============
+
+.. function:: view(atoms, data=None, viewer=None, repeat=None)
+
+This provides an interface to various visualization tools, such as
+:mod:`ase.gui <gui>`, :mod:`ase.visualize.vtk <vtk>`, RasMol_, VMD_, VTK_, gOpenMol_, or
+Avogadro_. The default viewer is the ase.gui, described in the
+:mod:`gui` module. The simplest invocation is::
+
+  >>> from ase import view
+  >>> view(atoms)
+
+where ``atoms`` is any :class:`Atoms` object.  Alternative viewers can
+be used by specifying the optional keyword ``viewer=...`` - use one of
+'ase.gui', 'gopenmol', 'vmd', or 'rasmol'.  The VMD and Avogadro
+viewers can take an optional ``data`` argument to show 3D data, such
+as charge density::
+
+  >>> view(atoms, viewer='VMD', data=array)
+
+If you do not wish to open an interactive gui, but rather visualize
+your structure by dumping directly to a graphics file; you can use the
+``write`` command of the :mod:`io` module, which can write 'eps',
+'png', and 'pov' files directly, like this::
+
+  >>> write('image.png', atoms)
+
+.. _RasMol: http://openrasmol.org/
+.. _VMD: http://www.ks.uiuc.edu/Research/vmd/
+.. _VTK: http://www.vtk.org/VTK/project/about.html
+.. _gOpenMol: http://www.csc.fi/gopenmol/
+.. _Avogadro: http://avogadro.openmolecules.net/
+
+
+VTK
+------------
+
+.. module:: visualize.vtk
+
+The Visualization Toolkit (VTK_) is a powerful platform-independent graphics
+engine, which comes as an open source graphics toolkit licensed under the 
+`BSD license`_. It is available for a wide range of programming languages, 
+including easily scriptable interfaces in Python and Tcl_.
+
+.. _BSD license: http://en.wikipedia.org/wiki/BSD_licenses
+.. _Tcl: http://www.tcl.tk/about
+
+In the scientific community, VTK is used by thousands of researchers and
+developers for 3D computer graphics, image processing, and visualization.
+VTK includes a suite of 3D interaction widgets within the development
+framework for information visualization, integrating GUI toolkits such as
+Qt_ and Tk_ into a highly flexible design platform.
+
+For visualization purposes within ASE, two different VTK-approaches are
+supported, namely:
+
+:Scripted on-the-fly rendering:
+	ASE includes VTK-scripting for easy data visualization using the
+	:mod:`vtk` module. Development is in progress, so you might want to
+	check out the latest development release from SVN 
+	(see :ref:`latest_development_release`).
+
+:Interactive rendering:
+	MayaVi_ is an easy-to-use GUI for VTK. With Enthought's traits-based
+	VTK-wrapper (TVTK_), constructing VTK pipelines has been simplified greatly
+	by introducing three basic concepts: data sources, filters and visualization
+	modules. MayaVi also supports the VTK file formats, including the flexible
+	VTK XML, which in ASE can be used to export atomic positions, forces and 
+	volume data using the ``write`` command in the :mod:`io` module.
+
+.. XXX -	`VTK Designer`_ is a visual editor for creating and editing VTK pipelines.
+
+.. _Qt: http://www.qtsoftware.com/products
+.. _Tk: http://www.tcl.tk/about
+.. _VTK Designer: http://www.vcreatelogic.com/oss/vtkdesigner
+.. _MayaVi: http://code.enthought.com/projects/mayavi
+.. _TVTK: https://svn.enthought.com/enthought/wiki/TVTK
+
+A key feature of VTK is the inherent ability to use MPI_ for parallel rending,
+which is provided with built-in parallel composite rendering objects to handle
+domain decomposition and subsequent recombination of the raster information.
+This is particularly useful for non-interactive ray tracing, batch isosurface
+generation and in-situ visualization of simulation data in cluster computing.
+
+.. seealso::
+	ParaView_ is a VTK-based open-source, multi-platform data analysis and
+	visualization application for extremely large data-sets using distributed 
+	memory computing resources and parallel rendering through MPI_.
+
+.. _MPI: http://www.mpi-forum.org
+.. _ParaView: http://www.paraview.org
+
+
+
+PrimiPlotter
+------------
+
+The PrimiPlotter is intended to do on-the-fly plotting of the
+positions of the atoms during long molecular dynamics simulations.
+The module :mod:`ase.visualize.primiplotter` contains the PrimiPlotter
+and the various output modules, see below.
+
+
+.. autoclass:: ase.visualize.primiplotter.PrimiPlotter
+   :inherited-members:
+
+
+
+FieldPlotter
+------------
+
+The FieldPlotter is intended to plot fields defined on the atoms in
+large-scale simulations.  The fields could be e.g. pressure, stress or
+temperature (kinetic energy), i.e. any quantity that in a given
+simulation is best defined on a per-atom basis, but is best
+interpreted as a continuum field.
+
+The current version of FieldPlotter only works if the number of atoms
+is at least 5-10 times larger than the number of pixels in the plot.
+
+.. autoclass:: ase.visualize.fieldplotter.FieldPlotter
+   :inherited-members:
+
+
+
+
diff --git a/doc/ase/visualize/vtk.rst b/doc/ase/visualize/vtk.rst
new file mode 100644
index 0000000..4adb7f5
--- /dev/null
+++ b/doc/ase/visualize/vtk.rst
@@ -0,0 +1,52 @@
+.. module:: vtk
+   :synopsis: Use of the Visualization Toolkit.
+
+.. index:: vtk
+
+===========
+ASE-VTK
+===========
+
+For ASE, the :mod:`~visualize.vtk` interface consists of Python modules for 
+automatic visualization of positions, bonds, forces and volume data (e.g. wave
+functions) from an :class:`~atoms.Atoms` object, provided such data is made
+available by the calculator.
+
+.. note::
+
+	The Python modules in ASE are intended to wrap lower-level functionality
+	of the VTK object models in small and easy-to-comprehend classes. To be able
+	to distinguish between build-in VTK objects and their wrappers, and because
+	VTK uses the CamelCase naming convention whereas ASE uses lower-case cf. 
+	our :ref:`coding conventions <python_codingstandard>`, all variables
+	referring to VTK built-in types are prefixed by ``vtk_``. However, both VTK
+	and wrapper classes are named according to the standard ``vtkFooBar``.
+
+
+Representing atoms
+==================
+.. autoclass:: ase.visualize.vtk.atoms.vtkAtoms
+   :members:
+   :show-inheritance:
+
+Atom-centered data
+------------------
+
+The superclass :class:`~ase.visualize.vtk.grid.vtkAtomicPositions` implements
+the basic concepts for representing atomic-centered data in VTK.
+
+.. autoclass:: ase.visualize.vtk.grid.vtkAtomicPositions
+   :members:
+..   :show-inheritance:
+
+Predefined shapes
+------------------
+
+The class :class:`~ase.visualize.vtk.module.vtkGlyphModule` implements
+the lower-level objects for representing predefined shapes (glyphs) in VTK.
+
+.. autoclass:: ase.visualize.vtk.module.vtkGlyphModule
+   :members:
+   :inherited-members:
+..   :show-inheritance:
+
diff --git a/doc/bugs.rst b/doc/bugs.rst
new file mode 100644
index 0000000..1e7bc48
--- /dev/null
+++ b/doc/bugs.rst
@@ -0,0 +1,38 @@
+.. _bugs:
+
+Bugs!
+=====
+
+If you find a bug in the ASE software, please report it to the
+developers so it can be fixed.  We need that feedback from the
+community to maintain the quality of the code.
+
+Bug report
+----------
+
+* If you are unsure if it is a real bug, or a usage problem, it is
+  probably best to report the problem on the ``ase-users``
+  mailing list (see :ref:`mailing_lists`).
+
+  Please provide the failing script as well as the information about your
+  environment (processor architecture, versions of python and numpy).
+  Then we (or other users) can help you to find out if it is a bug.
+
+  Another advantage of reporting bugs on the mailing list: often other
+  users will tell you how to work around the bug (until it is solved).
+
+* If you think it is a bug, you can also report it directly on our
+  `bug tracking system`_.  The advantage of reporting bugs
+  here is that it is not forgotten (which may be a risk on the mailing
+  list).
+
+We do not guarantee to fix all bugs, but we will do our best.
+
+
+Known bugs
+----------
+
+A list of known bugs (tickets) is on our Trac_.
+
+.. _bug tracking system: http://trac.fysik.dtu.dk/projects/ase/wiki/TracTickets
+.. _Trac: http://trac.fysik.dtu.dk/projects/ase/report/1
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..593ff7a
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+#
+# ASE documentation build configuration file, created by
+# sphinx-quickstart on Fri Jun 20 09:39:26 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('some/directory'))
+sys.path.append('.')
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+try:
+    from sphinx.ext import pngmath
+    ext_png_math = 'sphinx.ext.pngmath'
+except ImportError:
+    ext_png_math = 'mathpng'
+    print 'Warning: sphinx uses custom mathpng.py: please update to sphinx >= 5.0'
+
+
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['ext', 'images',
+              'sphinx.ext.autodoc',
+              ext_png_math]
+
+try:
+    from sphinx.ext import intersphinx
+    extensions.append('sphinx.ext.intersphinx')
+except ImportError:
+    print 'Warning: no sphinx.ext.intersphinx available: please update to sphinx >= 5.0'
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'contents'
+
+# General substitutions.
+project = 'ASE'
+copyright = '2008, CAMd'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+try:
+    from ase.version import version
+except ImportError:
+    version = '3.0.0'
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'ase.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+html_logo = '_static/ase.ico'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+html_favicon = '_static/ase.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+html_file_suffix = '.html'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ASEdoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+latex_paper_size = 'a4'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('contents', 'ase-manual.tex', 'ASE Manual', 'CAMd', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+latex_preamble = '\usepackage{amsmath}'
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+# Example configuration for intersphinx: refer to gpaw.
+intersphinx_mapping = {'http://wiki.fysik.dtu.dk/gpaw': None}
+
+# sphinx.ext.pngmath manual configuration
+# ---------------------------------------
+
+pngmath_latex_preamble = '\usepackage{amsmath}\usepackage{amsfonts}\usepackage[active]{preview}'
+
+# Additional arguments to give to dvipng, as a list.
+# The default value is ['-gamma 1.5', '-D 110']
+pngmath_dvipng_args = [
+    '-bgTransparent',
+    '-Ttight',
+    '--noghostscript',
+    '-l10',
+    '--depth',
+    '-D 136',
+    ]
+
+# correctly aligns the baselines
+pngmath_use_preview = True
diff --git a/doc/contents.rst b/doc/contents.rst
new file mode 100644
index 0000000..7b8935c
--- /dev/null
+++ b/doc/contents.rst
@@ -0,0 +1,36 @@
+========
+Contents
+========
+
+* :ref:`Introduction  to ASE - what is it? <overview>`
+* :ref:`Download and installation instructions <download_and_install>`
+* :ref:`tutorials`
+* :ref:`Documentation for modules in ASE <ase>`
+* :ref:`faq`
+* :ref:`mailing_lists`
+* :ref:`glossary`
+
+* :ref:`devel`
+* :ref:`bugs`
+
+* :ref:`ase2`
+
+
+The complete table of contents:
+
+.. toctree::
+
+   index
+   overview
+   download
+   tutorials/tutorials
+   ase/ase
+   faq
+   glossary
+   mailinglists
+
+   development/development
+   bugs
+
+   oldase
+
diff --git a/doc/development/contribute.rst b/doc/development/contribute.rst
new file mode 100644
index 0000000..f3a2571
--- /dev/null
+++ b/doc/development/contribute.rst
@@ -0,0 +1,100 @@
+=================
+How to contribute
+=================
+
+Discussion of ASE development takes place on the :ref:`ase-developer
+<mailing_lists>` mailing list, and also sometimes on the #gpaw IRC
+channel on freenode.
+
+We welcome new developers who would like to help work on improving
+ASE.  If you would like to contribute, your should first tell us what
+you want to work on.  Use the mailing list for that.
+
+
+SVN access
+==========
+
+We don't give new contributers write access to our SVN repository from
+day one.  So, you will have to create a patch and send it to the
+mailing list::
+
+  $ svn checkout https://svn.fysik.dtu.dk/projects/ase/trunk myase
+  $ cd myase
+  $ # do your thing ...
+  $ svn diff > patch.txt
+
+Before you send the patch, *please* read our
+:ref:`python_codingstandard` and learn how to use pep8.py and pylint:
+
+* :ref:`pep8py`
+* :ref:`pylint`
+
+One of the current committers will look at the patch and give you some
+feedback.  Maybe the patch is fine and the committer will commit it to
+trunk.  There could also be some more work to do like:
+
+* make it compatible with all supported pythons (see :ref:`download_and_install`).
+* write more comments
+* fix docstrings
+* write a test
+* add some documentation
+
+Once everyone is happy, the patch can be appied.  This patch-feedback
+loop is not something we have invented to prevent you from
+contributing - it should be viewed as an opportunity for you to learn
+how to write a code that fits into the ASE codebase.  
+
+After a couple of contributions, we will probably trust you enough to
+add you as a committer.
+
+
+Committers
+==========
+
+Here is the list of current committers:
+
+==========  ======================  ===================================
+user name   real name
+==========  ======================  ===================================
+anpet       Andrew Peterson         andy,peterson:stanford,edu
+askhl       Ask Hjorth Larsen       askhl:fysik,dtu,dk
+bjork       Jonas Bjork             J,Bjork:liverpool,ac,uk
+dlandis     David Landis            dlandis:fysik,dtu,dk
+dulak       Marcin Dulak            dulak:fysik,dtu,dk
+getri       George Tritsaris        getri:fysik,dtu,dk
+grabow      Lars Grabow             grabow:fysik,dtu,dk
+hahansen    Heine Anton Hansen      hahansen:fysik,dtu,dk
+hanke       Felix Hanke             F,Hanke:liverpool,ac,uk
+ivca        Ivano Eligio Castelli   ivca:fysik,dtu,dk
+jakobb      Jakob Blomquist         jakobb:fysik,dtu,dk
+jber        Jon Bergmann Maronsson  jber:fysik,dtu,dk
+jblomqvist  Janne Blomqvist         Janne,Blomqvist:tkk,fi
+jensj       Jens Jørgen Mortensen   jensj:fysik,dtu,dk
+jesperf     Jesper Friis            jesper,friis:sintef,no
+jingzhe     Jingzhe Chen            jingzhe:fysik,dtu,dk
+jkitchin    John Kitchin            jkitchin:andrew,cmu,edu
+jussie      Jussi Enkovaara         jussi,enkovaara:csc,fi
+kkaa        Kristen Kaasbjerg       kkaa:fysik,dtu,dk
+kleis       Jesper Kleis            kleis:fysik,dtu,dk
+kwj         Karsten Wedel Jacobsen  kwj:fysik,dtu,dk
+markus      Markus Kaukonen         markus,kaukonen:iki,fi
+miwalter    Michael Walter          Michael,Walter:fmf,uni-freiburg,de
+moses       Poul Georg Moses        poulgeorgmoses:gmail,com
+mvanin      Marco Vanin             mvanin:fysik,dtu,dk
+s032082     Christian Glinsvad      s032082:fysik,dtu,dk
+schiotz     Jakob Schiotz           schiotz:fysik,dtu,dk
+slabanja    Mattias Slabanja        slabanja:chalmers,se
+strange     Mikkel Strange          strange:fysik,dtu,dk
+tjiang      Tao Jiang               tjiang:fysik,dtu,dk
+tolsen      Thomas Olsen            tolsen:fysik,dtu,dk
+==========  ======================  ===================================
+
+
+Former committers:
+
+==========  ======================  ===================================
+anro        Anthony Goodrow         anro:fysik,dtu,dk 
+carstenr    Carsten Rostgaard       carstenr:fysik,dtu,dk
+s042606     Janosch Michael Rauba   s042606:fysik,dtu,dk
+s052580     Troels Kofoed Jacobsen  s052580:fysik,dtu,dk
+==========  ======================  ===================================
diff --git a/doc/development/development.rst b/doc/development/development.rst
new file mode 100644
index 0000000..3dfcdd5
--- /dev/null
+++ b/doc/development/development.rst
@@ -0,0 +1,42 @@
+.. _devel:
+
+===============
+ASE development
+===============
+
+As a developer,
+you should subscribe to all ASE related :ref:`mailing_lists`.
+
+
+Development topics
+==================
+
+.. toctree::
+
+   releasenotes
+   contribute
+   py3k
+   versioncontrol
+   python_codingstandard
+   writing_documentation_ase
+   making_movies
+   newrelease
+   tests
+   todo
+
+
+Creating an encrypted password for SVN access
+=============================================
+
+Use this cammand::
+
+  htpasswd -nm <your-desired-user-name>
+
+and type a good password twice.  The encrypted password will be
+printed on the screen.
+
+If you don't have the ``htpasswd`` command, then use Python:
+
+>>> import crypt
+>>> passwd = '<your-password>'
+>>> print crypt.crypt(passwd, passwd)
diff --git a/doc/development/making_movies.rst b/doc/development/making_movies.rst
new file mode 100644
index 0000000..f01378e
--- /dev/null
+++ b/doc/development/making_movies.rst
@@ -0,0 +1,61 @@
+.. _making_movies_ase:
+
+=============
+Making movies
+=============
+
+A video tutorial can be produced in the following way:
+
+- change the screen resolution to 1280x1024,
+
+- record the movie of the screen (without sound)
+  using recordmydesktop_ (gtk-recordMyDesktop),
+
+- convert the resulting ogv into avi using mencoder::
+
+   mencoder video.ogv -o video.avi -oac copy -ovc lavc
+
+- record and edit the sound track using audacity_:
+
+  - use 44100 Hz for recording and save the final file as `sound.wav`,
+
+  - make sure not to keep the microphone to close to avoid signal peaks,
+
+  - cut microphone signal peaks, insert silences, ...
+
+- edit the movie using avidemux_ (to match the sound track):
+
+  - load the `video.avi`: `File->Open`, make sure to use
+    the following options when editing and saving:
+    `Video->Copy`, `Audio->Copy`, `Format->AVI`,
+
+  - add the `sound.avi`: `Audio->Main Track->Audio Source->External WAV`,
+
+  - cut video frames (or copy and insert still frames to extend the video)
+    to match the sound track.
+    Set beginning mark and end mark -
+    the cut or copy/paste operation applies to the selected region,
+
+  - make sure to save intermediate stages
+    when working on the video `File->Save->Save Video` (as AVI):
+
+    - avidemux caches the audio track so to match the audio
+      to a freshly cut video you can copy the audio file into another name,
+      and add the sound track from that name,
+
+    - sometimes when cutting frames avidemux does not allow to set the markers
+      correctly, and there is **no** undo the last step in avidemux!
+
+  - save the `video_final.avi` (that matches the sound track),
+    and encode it into mpeg4 format using mencoder, using two passes::
+
+     opt="vbitrate=550:mbd=2:dc=10 -vf unsharp=l:0.4:c:0.0:hqdn3d"
+     mencoder -ovc lavc -lavcopts vcodec=msmpeg4v2:vpass=1 -nosound -o /dev/null video_final.avi
+     mencoder video_final.avi -oac mp3lame -af resample=32000:0:2 -lameopts vbr=3:br=80:mode=3 \
+             -ovc lavc -lavcopts acodec=mp3lame:vcodec=msmpeg4v2:vpass=2:$opt \
+             -info name="Overview and installation of ASE":artist=CAMd:copyright="CAMd 2009" -o video_final_mpeg4.avi
+
+.. _recordmydesktop: http://recordmydesktop.sourceforge.net/
+.. _audacity: http://audacity.sourceforge.net/
+.. _avidemux: http://www.avidemux.org/
+
diff --git a/doc/development/newrelease.rst b/doc/development/newrelease.rst
new file mode 100644
index 0000000..b9f1081
--- /dev/null
+++ b/doc/development/newrelease.rst
@@ -0,0 +1,53 @@
+.. _newrelease:
+
+===========
+New release
+===========
+
+When it is time for a new release of the code, here is what you have to do:
+
+** **Warning:** use only three digits release numbers, e.g. *3.1.0*,
+
+* Checkout the :ref:`latest_development_release`.
+
+* :ref:`running_tests`.
+
+* Make sure version.py has the correct version number.
+
+* Make a tag in svn, using the current version number
+  (to make sure **not** to include changes done by other developers
+  in the meantime!)::
+
+    svn copy -r 845 https://svn.fysik.dtu.dk/projects/ase/trunk https://svn.fysik.dtu.dk/projects/ase/tags/3.1.0 -m "Version 3.1.0"
+
+  **Note** the resulting tag's revision ``tags_revision``.
+
+* **Checkout** the source, specyfing the version number in the directory name::
+
+   svn co -r tags_revision https://svn.fysik.dtu.dk/projects/ase/tags/3.1.0 ase-3.1.0
+
+* Create the tar file::
+
+   cd ase-3.1.0
+   rm -f MANIFEST ase/svnrevision.py*; python setup.py sdist
+
+  Note that the ``tags_revision`` is put into the name of the
+  tar file automatically. Make sure that you are getting only
+  ``tags_revision`` in the tar file name! Any changes to the source
+  will be reflected as a mixed or modified revision tag!
+
+* Put the tar file on web2 (set it read-able for all)::
+
+   scp dist/python-ase-3.1.0."tags_revision".tar.gz root at web2:/var/www/wiki/ase-files
+
+* Add a link on :ref:`news` and update the information
+  on the :ref:`download_and_install` page and the :ref:`releasenotes` page.
+
+* Increase the version number in ase/version.py, and commit the change::
+
+    cd ~/ase
+    svn ci -m "Version 3.2.0"
+
+  Now the trunk is ready for work on the new version.
+
+* Send announcement email to the ``ase-users`` mailing list (see :ref:`mailing_lists`).
diff --git a/doc/development/py3k.rst b/doc/development/py3k.rst
new file mode 100644
index 0000000..f31b2ba
--- /dev/null
+++ b/doc/development/py3k.rst
@@ -0,0 +1,16 @@
+Python 3 strategy
+=================
+
+* One codebase for both 2 and 3.
+
+* Use "print(...)" if possible::
+
+    print 'bla bla'   # no
+    print('bla bla')  # yes
+    print 'bla bla:', x       # no
+    print('bla bla: %s' % x)  # yes
+
+* Don't do this: ``print >> f, ...``.  Use ``f.write(... + '\n')`` or
+  ``ase.utils.prnt(..., file=f)``.
+
+* More help here: http://packages.python.org/six/
diff --git a/doc/development/pylintrc b/doc/development/pylintrc
new file mode 100644
index 0000000..f8232b0
--- /dev/null
+++ b/doc/development/pylintrc
@@ -0,0 +1,336 @@
+# lint Python modules using external checkers.
+# 
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+# 
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=.svn
+
+# Pickle collected data for later comparisons.
+persistent=no
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories (IRCWEF).
+#enable-msg-cat=
+
+# Disable all messages in the listed categories (IRCWEF).
+disable-msg-cat=IR
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=W0142,W0201,W0614,W0703,W0704
+#C0112: *Empty docstring*
+#   Used when a module, function, class or method has an empty docstring (it would
+#   be too easy ;).
+#W0142: *Used * or ** magic*
+#   Used when a function or method is called using `*args` or `**kwargs` to
+#   dispatch arguments. This doesn't improve readability and should be used with
+#   care.
+#W0201: *Attribute %r defined outside __init__*
+#   Used when an instance attribute is defined outside the __init__ method.
+#W0614: *Unused import %s from wildcard import*
+#   Used when an imported module or variable is not used from a 'from X import *'
+#   style import. Already covered by the W0401 wildcard import warning.
+#W0703: *Catch "Exception"*
+#   Used when an except catches Exception instances.
+#W0704: *Except doesn't do anything*
+#   Used when an except clause does nothing but "pass" and there is no "else"
+#   clause.
+
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=parseable
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+disable-report=R0001,R0101,R0401,R0402,R0801
+#R0001: Messages by category
+#R0101: Statistics by type
+#R0401: External dependencies
+#R0402: Modules dependencies graph
+#R0801: Duplication
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+# 
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=[a-z_][a-z0-9_]*(module)?$
+
+# Regular expression which should only match correct module level names
+const-rgx=[a-z_][a-z0-9_]*(const)?$
+
+# Regular expression which should only match correct class names
+class-rgx=([A-Z][a-z]*)*(class)?$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]*(function)?$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]*(method)?$
+
+# Regular expression which should only match correct instance attribute names
+#attr-rgx=[a-z_][a-z0-9_]*$
+attr-rgx=([a-z_]+[a-z0-9_]*_[A-Za-z0-9]+)|([A-Z_]+[A-Z0-9_]*_[A-Za-z0-9]+)|([a-z_]+[a-z0-9_]*)|([A-Z_]+[A-Z0-9_]*)$
+
+# Regular expression which should only match correct argument names
+#argument-rgx=[a-z_][a-z0-9_]*$
+argument-rgx=([a-z_]+[a-z0-9_]*_[A-Za-z0-9]+)|([A-Z_]+[A-Z0-9_]*_[A-Za-z0-9]+)|([a-z_]+[a-z0-9_]*)|([A-Z_]+[A-Z0-9_]*)$
+
+# Regular expression which should only match correct variable names
+#variable-rgx=[a-z_][a-z0-9_]*$
+variable-rgx=([a-z_]+[a-z0-9_]*_[A-Za-z0-9]+)|([A-Z_]+[A-Z0-9_]*_[A-Za-z0-9]+)|([a-z_]+[a-z0-9_]*)|([A-Z_]+[A-Z0-9_]*)$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+#inlinevar-rgx=[a-z_][a-z0-9_]*(inline)?$
+inlinevar-rgx=([a-z_]+[a-z0-9_]*_[A-Za-z0-9]+)|([A-Z_]+[A-Z0-9_]*_[A-Za-z0-9]+)|([a-z_]+[a-z0-9_]*)|([A-Z_]+[A-Z0-9_]*)$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+# 
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# try to find bugs in the code using type inference
+# 
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamicaly set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+# 
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+# 
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+# 
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+# 
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+# 
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+# 
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=78
+
+# Maximum number of lines in a module
+#max-module-lines=1000 XXX
+max-module-lines=2000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
diff --git a/doc/development/python_codingstandard.rst b/doc/development/python_codingstandard.rst
new file mode 100644
index 0000000..2cb8a70
--- /dev/null
+++ b/doc/development/python_codingstandard.rst
@@ -0,0 +1,152 @@
+.. _python_codingstandard:
+
+==================
+Coding Conventions
+==================
+
+
+Importing modules
+=================
+
+In code, like the implementation of ASE, we must *not* use the
+``import *`` syntax.  Import everything explicitly from exactly the
+place where it's defined::
+
+  from ase.io import read, write
+
+We distinguish between scripts and code.  In your own scripts, it's OK
+to use::
+
+  from ase.all import *
+
+which will give you the most used symbols.
+
+
+Python Coding Conventions
+=========================
+
+Please run :ref:`pep8.py <pep8py>` and :ref:`pylint <pylint>` on your
+code before committing.
+
+The rules for the Python part are almost identical
+to those used by the `Docutils project`_:
+
+Contributed code will not be refused merely because it does not
+strictly adhere to these conditions; as long as it's internally
+consistent, clean, and correct, it probably will be accepted.  But
+don't be surprised if the "offending" code gets fiddled over time to
+conform to these conventions.
+
+The project shall follow the generic coding conventions as
+specified in the `Style Guide for Python Code`_ and `Docstring
+Conventions`_ PEPs, summarized, clarified, and extended as follows:
+
+* 4 spaces per indentation level.  No hard tabs.
+
+* Very important:  Read the *Whitespace in Expressions and Statements*
+  section of PEP8_.
+
+* Avoid introducing `trailing whitespaces`_.
+
+* Try to use only 7-bit ASCII, no 8-bit strings.
+
+* No one-liner compound statements (i.e., no ``if x: return``: use two
+  lines & indentation), except for degenerate class or method
+  definitions (i.e., ``class X: pass`` is OK.).
+
+* Lines should be no more than 78 characters long.
+
+* Use "StudlyCaps" for class names.
+
+* Use "lowercase" or "lowercase_with_underscores" for function,
+  method, and variable names.  For short names, maximum two words,
+  joined lowercase may be used (e.g. "tagname").  For long names with
+  three or more words, or where it's hard to parse the split between
+  two words, use lowercase_with_underscores (e.g.,
+  "note_explicit_target", "explicit_target").  If in doubt, use
+  underscores.
+
+* Avoid lambda expressions, which are inherently difficult to
+  understand.  Named functions are preferable and superior: they're
+  faster (no run-time compilation), and well-chosen names serve to
+  document and aid understanding.
+
+* Avoid functional constructs (filter, map, etc.).  Use list
+  comprehensions instead.
+
+* Avoid ``from __future__ import`` constructs.  They are inappropriate
+  for production code.
+
+* Use 'single quotes' for string literals, and """triple double
+  quotes""" for :term:`docstring`\ s.  Double quotes are OK for
+  something like ``"don't"``.
+
+.. _PEP8:
+.. _Style Guide for Python Code: http://www.python.org/peps/pep-0008.html
+.. _Docstring Conventions: http://www.python.org/peps/pep-0257.html
+.. _Docutils project: http://docutils.sourceforge.net/docs/dev/policies.html#python-coding-conventions
+.. _trailing whitespaces: http://www.gnu.org/software/emacs/manual/html_node/emacs/Useless-Whitespace.html
+
+.. attention::
+
+   Thus spake the Lord: Thou shalt indent with four spaces. No more, no less.
+   Four shall be the number of spaces thou shalt indent, and the number of thy
+   indenting shall be four. Eight shalt thou not indent, nor either indent thou
+   two, excepting that thou then proceed to four. Tabs are right out.
+
+                                          Georg Brandl
+
+
+General advice
+==============
+
+ * Get rid of as many ``break`` and ``continue`` statements as possible.
+
+
+Writing documentation in the code
+=================================
+
+Here is an example of how to write good docstrings:
+
+  http://projects.scipy.org/numpy/browser/trunk/doc/example.py
+
+
+.. _pep8py:
+
+Run pep8.py on your code
+========================
+
+The `pep8.py <https://github.com/jcrocholl/pep8>`_ program is
+installed together with ASE.  It will check the PEP8_ conventions for
+you.  Try::
+
+  $ pep8.py --help
+
+
+.. _pylint:
+
+Using pylint to check your code
+===============================
+
+A pylintrc trying to follow ASE :ref:`python_codingstandard` can be found here:
+:svn:`doc/development/pylintrc`
+
+
+Running pylint yourself
+-----------------------
+
+Run pylint on a single file like this::
+
+    [~]$ pylint mypythonfile.py
+
+Run pylint on a module like this::
+    
+    [~]$ pylint path/to/module/root/dir
+
+
+Output from pylint run on ASE
+-----------------------------
+
+* pylint_ase_
+
+.. _pylint_ase: http://dcwww.fys.dtu.dk/~s052580/pylint/ase
diff --git a/doc/development/releasenotes.rst b/doc/development/releasenotes.rst
new file mode 100644
index 0000000..4a470e2
--- /dev/null
+++ b/doc/development/releasenotes.rst
@@ -0,0 +1,94 @@
+.. _releasenotes:
+
+=============
+Release notes
+=============
+
+
+Development version in trunk
+============================
+
+:trac:`trunk <>`.
+
+* New function for making surfaces with arbitrary Miller indices with
+  the smallest possible surface unit cell:
+  ase.lattice.surface.surface()
+
+* New ase.lattice.bulk() function.  Will replace old
+  ase.structure.bulk() function.  The new one will produce a more
+  natural hcp lattice and it will use experimental data for crystal
+  structure and lattice constants if not provided explicitely.
+
+* New values for ase.data.covalent_radii from Cordeo *et al.*.
+
+* New command line tool: :ref:`command line tools` and tests based on it:
+  abinit, elk, fleur, nwchem.
+
+* New crystal builder for ag
+
+* Van der Waals radii in ase.data
+
+* ASE GUI (ag) now supports velocities for both graphs and coloring
+
+* Cleaned up some name-spaces:
+
+  * ``ase`` now contains only :class:`~ase.atoms.Atoms` and
+    :class:`~ase.atom.Atom`
+  * ``ase.calculators`` is now empty
+
+
+Version 3.5.1
+=============
+
+24 May 2011: :trac:`tags/3.5.1 <../tags/3.5.1>`.
+
+* Problem with parallel vibration calculations fixed:
+  `Ticket #80 <https://trac.fysik.dtu.dk/projects/ase/ticket/80>`_.
+
+
+Version 3.5.0
+=============
+
+13 April 2011: :trac:`tags/3.5.0 <../tags/3.5.0>`.
+
+* Improved EMT potential:  uses a
+  :class:`~ase.calculators.neighborlist.NeighborList` object and is
+  now ASAP_ compatible.
+
+* :mod:`BFGSLineSearch <optimize.bfgslinesearch>` is now the default
+  (``QuasiNewton==BFGSLineSearch``).
+
+* There is a new interface to the LAMMPS molecular dynamics code.
+
+* New :mod:`phonons` module.
+
+* Van der Waals corrections for DFT, see GPAW_ usage.
+
+* New :class:`~ase.io.bundletrajectory.BundleTrajectory` added.
+
+* Updated GUI interface:
+
+  * Stability and usability improvements.
+  * Povray render facility.
+  * Updated expert user mode.
+  * Enabled customization of colours and atomic radii.
+  * Enabled user default settings via :file:`~/.ase/gui.py`. 
+
+* :mod:`Database library <data>` expanded to include:
+  
+  * The s22, s26 and s22x5 sets of van der Waals bonded dimers and
+    complexes by the Hobza group.
+  * The DBH24 set of gas-phase reaction barrier heights by the Truhlar
+    group.
+
+* Implementation of the Dimer method.
+
+
+.. _ASAP: http://wiki.fysik.dtu.dk/asap
+.. _GPAW: https://wiki.fysik.dtu.dk/gpaw/documentation/xc/vdwcorrection.html
+
+
+Version 3.4.1
+=============
+
+11 August 2010: :trac:`tags/3.4.1 <../tags/3.4.1>`.
diff --git a/doc/development/tests.rst b/doc/development/tests.rst
new file mode 100644
index 0000000..9794432
--- /dev/null
+++ b/doc/development/tests.rst
@@ -0,0 +1,90 @@
+.. module:: test
+
+================
+Testing the code
+================
+
+All additions and modifications to ASE should be tested.
+
+.. index:: testase.py
+
+Test scripts should be put in the :trac:`ase/test` directory.  The
+scripts in this directory may be run by using the script
+:svn:`tools/testase.py` or by using the function:
+
+.. function:: test.test(verbosity=1, dir=None)
+    
+    Runs the test scripts in :trac:`ase/test`.
+
+
+.. important::
+
+  When you fix a bug, add a test to the test suite checking that it is
+  truly fixed.  Bugs sometimes come back, do not give it a second
+  chance!
+
+
+How to fail successfully
+========================
+
+The test suite provided by :func:`test.test` automatically runs all test
+scripts in the :trac:`ase/test` directory and summarizes the results.
+
+.. note::
+
+  Test scripts are run from within Python using the :func:`execfile` function.
+  Among other things, this provides the test scripts with an specialized global
+  :term:`namespace`, which means they may fail or behave differently if you try
+  to run them directly e.g. using :command:`python testscript.py`.
+
+If a test script causes an exception to be thrown, or otherwise terminates
+in an unexpected way, it will show up in this summary. This is the most
+effective way of raising awareness about emerging conflicts and bugs during
+the development cycle of the :ref:`latest revision <Latest_development_release>`.
+
+
+Remember, great tests should serve a dual purpose:
+
+**Working interface**
+    To ensure that the :term:`class`'es and :term:`method`'s in ASE are
+    functional and provide the expected interface. Empirically speaking, code
+    which is not covered by a test script tends to stop working over time.
+
+**Replicable results**
+    Even if a calculation makes it to the end without crashing, you can never
+    be too sure that the numerical results are consistent. Don't just assume 
+    they are, :func:`assert` it!
+
+.. function:: assert(expression)
+    
+    Raises an `AssertionError` if the `expression` does not evaluate to `True`.
+
+Example::
+
+  from ase import molecule
+  atoms = molecule('C60')
+  atoms.center(vacuum=4.0)
+  result = atoms.get_positions().mean(axis=0)
+  expected = 0.5*atoms.get_cell().diagonal()
+  tolerance = 1e-4
+  assert (abs(result - expected) < tolerance).all()
+
+
+Using functions to repeat calculations with different parameters::
+
+  def test(parameter):
+      # setup atoms here...
+      atoms.set_something(parameter)
+      # calculations here...
+      assert everything_is_going_to_be_alright
+
+  if __name__ in ['__main__', '__builtin__']:
+      test(0.1)
+      test(0.3)
+      test(0.7)
+          
+.. important::
+
+  Unlike normally, the module `__name__` will be set to ``'__builtin__'``
+  when a test script is run by the test suite.
+
diff --git a/doc/development/todo.rst b/doc/development/todo.rst
new file mode 100644
index 0000000..60d5bc1
--- /dev/null
+++ b/doc/development/todo.rst
@@ -0,0 +1,24 @@
+To do
+=====
+
+Check our `issue tracker`_.
+
+.. _issue tracker: http://trac.fysik.dtu.dk/projects/ase/report/1
+
+
+Documentation
+-------------
+
+* Put example of Verlet dynamics in ase/md
+* talk about writing files - pos, py and pckl
+* Write more about the parameters of the supported elements of the EMT
+  calculator.
+
+
+Code
+----
+
+* Could the *directions* argument of ase.lattice.FaceCenteredCubic etc.
+  have default values?
+
+
diff --git a/doc/development/versioncontrol.rst b/doc/development/versioncontrol.rst
new file mode 100644
index 0000000..0fae857
--- /dev/null
+++ b/doc/development/versioncontrol.rst
@@ -0,0 +1,61 @@
+.. _versioncontrol:
+
+===========================
+Using version control (SVN)
+===========================
+
+The version control system used in ASE development is subversion. A thorough
+subversion manual can be found at http://svnbook.red-bean.com/, here
+is a brief overview of the most basic features needed when developing ASE.
+
+* perform svn checkout of ase.
+
+  Place it, for example, in ``ase-svn`` directory::
+ 
+     cd
+     svn checkout https://svn.fysik.dtu.dk/projects/ase/trunk ase-svn
+
+  This retrieves the code tree from the subversion repository.
+  Prepend :envvar:`PYTHONPATH` and :envvar:`PATH` environment variables
+  as described at :ref:`installation`.
+
+* Updating the working copy of the code (in the directory ``ase-svn``)::
+
+    svn update
+
+  After each (important) update, remove ``ase/svnrevision.py*`` files,
+  and run::
+
+    python setup.py sdist
+
+  to keep the ``ase/svnrevision.py`` file up-to-date.
+
+* Checking the status of the working copy (in the directory ``ase-svn``)::
+
+    svn stat
+
+  The status about the files which are not in version control can be
+  surpassed with the ``-q`` flag, and the status with respect to latest
+  additions in server can be checked with the ``-u`` flag.
+
+* Committing the changes to the repository
+
+  Before sending the changes in the working copy to the repository, working
+  copy should be updated. After that, the changes can be send with::
+
+    svn commit -m "Message to describe the committed changes"
+
+  If the ``-m`` option is omitted, an editor is opened for writing the
+  log message.
+
+* Adding files or directories to version control::
+
+    svn add filename
+
+  If ``filename`` is directory, also all the files within the
+  directory are added. Note that ``svn commit`` is required before the
+  new files are actually included in version control.
+
+* ASE documentation resides under ``doc`` directory,
+  and is also under subversion control.
+  See more at :ref:`writing_documentation_ase`.
diff --git a/doc/development/writing_documentation_ase.rst b/doc/development/writing_documentation_ase.rst
new file mode 100644
index 0000000..bcde9aa
--- /dev/null
+++ b/doc/development/writing_documentation_ase.rst
@@ -0,0 +1,209 @@
+.. _writing_documentation_ase:
+
+=====================
+Writing documentation
+=====================
+
+We use the Sphinx_ tool to generate the documentation (both HTML
+and PDF_).  The documentation is stored in SVN as text files in the
+:trac:`doc` directory using the reStructuredText_ markup language.
+
+.. _reStructuredText: http://docutils.sf.net/rst.html
+.. _Sphinx: http://sphinx.pocoo.org
+.. _PDF: ../ase-manual.pdf
+
+
+Installing Docutils and Sphinx
+==============================
+
+The reStructuredText_ parser that Sphinx needs, is part of the Docutils_
+project.  So, we need to install docutils and sphinx (version>= 0.5).
+
+.. _Docutils: http://docutils.sf.net
+
+
+Other requirements
+==================
+
+When building the documentation, a number of png-files are generated.
+For that to work, you need the following installed:
+
+* matplotlib
+* povray
+* dvipng
+* pdflatex
+* bibtex
+* AUCTex
+* convert (ImageMagick)
+
+.. _using_sphinx:
+
+Using Sphinx
+============
+
+.. highlight:: bash
+
+
+First, you should take a look at the documentation for Sphinx_ and
+reStructuredText_.
+
+If you don't already have your own copy of the ASE package, then get
+the :ref:`latest_development_release` and install it.
+
+Then :command:`cd` to the :file:`doc` directory and build the html-pages::
+
+  $ cd ~/ase/doc
+  $ sphinx-build . _build
+
+.. Note::
+
+   Make sure that you build the Sphinx documentation using the corresponding ASE version
+   by setting the environment variables :envvar:`$PYTHONPATH` and :envvar:`$PATH`.
+
+Make your changes to the ``.rst`` files, run the
+:command:`sphinx-build` command again, check the results and if things
+looks ok, commit::
+
+  $ emacs index.rst
+  $ sphinx-build . _build
+  $ firefox _build/index.html
+  $ svn ci -m "..." index.rst
+
+To build a pdf-file, you do this::
+
+  $ sphinx-build -b latex . _build
+  $ cd _build
+  $ make ase-manual.pdf
+
+
+
+Extensions to Sphinx
+====================
+
+.. highlight:: rest
+
+We have a couple of extensions to Sphinx:
+
+**:mol:**
+
+   Use ``:mol:`CH_3OH``` to get :mol:`CH_3OH`.
+
+**:svn:**
+
+   A role for creating a link to a file in SVN.  If you write
+   ``:svn:`ase/atoms.py```, you
+   will get: :svn:`ase/atoms.py`.
+
+**:trac:**
+
+   A role for creating a link to a file in Trac.  If you write
+   ``:trac:`ase/atoms.py```, you
+   will get: :trac:`ase/atoms.py`.
+
+**:epydoc:**
+
+   A role for creating a link to the API-documentation generated with
+   epydoc_.  If you
+   write ``:epydoc:`ase.atoms.Atoms```, you will get:
+   :epydoc:`ase.atoms.Atoms`.
+
+**:math:**
+
+   This role is for inline LaTeX-style math.  Example:
+   ``:math:`\sin(x_n^2)``` gives you :math:`\sin(x_n^2)`.
+
+**.. math::**
+
+   Write displayed LaTeX-style math.  Example::
+
+     .. math:: \frac{1}{1+x^2}
+
+   gives you:
+
+   .. math:: \frac{1}{1+x^2}
+
+
+If you add the line ``.. default-role:: math``, then you can leave out
+the ``:math:`` part like here: ```\sin(x_n^2)```.
+
+The implementation of these roles is here: :svn:`doc/ext.py`.  Our
+custom, obsolete, implementation of the math role and directive is
+here: :svn:`doc/mathpng.py`.  With sphinx >= 0.5 please use
+``sphinx.ext.pngmath``.
+
+
+.. _epydoc:  http://epydoc.sf.net
+
+
+
+reStructedText in emacs
+=======================
+
+.. highlight:: common-lisp
+
+For people using emacs, the `reStructuredText extension`_ is highly
+recommended. The intallation procedure is described in the top of the
+file, but for most people, it is enough to place it in your emacs
+load-path (typically ``.emacs.d/``) and add the lines::
+
+  (add-to-list 'load-path "~/.emacs.d")
+  (require 'rst)
+
+somewhere in your ``.emacs`` file.
+
+To make the mode auto load for relevant file extension, you can write
+something like::
+
+  (setq auto-mode-alist
+        (append '(("\\.rst$" . rst-mode)
+                  ("\\.rest$" . rst-mode)) auto-mode-alist))
+
+In your ``.emacs`` file.
+
+.. _reStructuredText extension: http://docutils.sourceforge.net/tools/editors/emacs/rst.el
+
+Updating Sphinx
+===============
+
+Starting a new project with sphinx requires an initial configuration.
+This is achieved by running :command:`sphinx-quickstart`.
+When updating from a very old sphinx you may consider
+generating new configuration files and updating the old files accordingly.
+
+**Note** that the current project is configured up-to-date,
+so if you are "simply" writing the documentation
+you **must** skip the :command:`sphinx-quickstart` step
+and focus on :ref:`using_sphinx`.
+
+Here is how do you setup the GPAW project with sphinx:
+
+ - :command:`cd` to the :file:`doc` directory,
+
+ - run :command:`sphinx-quickstart`
+   and answer the questions (example given for GPAW)::
+
+    > Root path for the documentation [.]:
+
+    > Separate source and build directories (y/N) [n]:
+
+    > Name prefix for templates and static dir [.]: _
+
+    > Project name: GPAW
+    > Author name(s): 2008, CAMd et al.
+  
+    > Project version: 0.5
+    > Project release [0.5]:
+
+    > Source file suffix [.rst]:
+
+    > Name of your master document (without suffix) [index]: contents
+
+    > autodoc: automatically insert docstrings from modules (y/N) [n]: y
+    > doctest: automatically test code snippets in doctest blocks (y/N) [n]:
+    > intersphinx: link between Sphinx documentation of different projects (y/N) [n]: y
+
+   This will create :file:`doc/conf.py` and :file:`doc/contents.rst`.
+   Both these files need to be edited further
+   (:file:`doc/conf.py` may for example include
+   options for ``sphinx.ext.pngmath``)
+
diff --git a/doc/download.rst b/doc/download.rst
new file mode 100644
index 0000000..2dd1906
--- /dev/null
+++ b/doc/download.rst
@@ -0,0 +1,196 @@
+.. _download_and_install:
+
+=================================
+Installation of ASE: Requirements
+=================================
+
+The following packages are required for basic ASE functionality:
+
+1) Python_ 2.4 - 2.7.
+2) NumPy_.
+
+.. _Python: http://www.python.org
+.. _NumPy: http://www.scipy.org/NumPy
+
+
+It is highly recommended (but not required) to install also these two:
+
+3) matplotlib_.
+4) pygtk_.
+
+Matplotlib is needed for :mod:`writing png and eps files <io>`, and
+both packages are needed for ASE's simple GUI (called **ag**, see :mod:`gui`).
+Some of these packages may already be installed on your system.
+
+
+.. _matplotlib: http://matplotlib.sourceforge.net
+.. _pygtk: http://www.pygtk.org
+
+========
+Download
+========
+
+.. highlight:: bash
+
+.. _latest_stable_release:
+
+Latest stable release
+=====================
+
+The latest stable release can be obtained from ``svn`` or as a ``tarball``.
+
+.. note::
+
+   The recommended installation path is :envvar:`$HOME`.
+
+When using svn please set the following variable:
+
+- bash::
+
+   export ASE_TAGS=https://svn.fysik.dtu.dk/projects/ase/tags/
+
+- csh/tcsh::
+
+   setenv ASE_TAGS https://svn.fysik.dtu.dk/projects/ase/tags/
+
+======= =========== ============================================ =============================
+Release Date        Retrieve as svn checkout                     Retrieve as tarball
+======= =========== ============================================ =============================
+ 3.5.1_ May 24 2011 ``svn co -r 2175 $ASE_TAGS/3.5.1 ase-3.5.1`` python-ase-3.5.1.2175.tar.gz_
+ 3.4.1_ Aug 11 2010 ``svn co -r 1765 $ASE_TAGS/3.4.1 ase-3.4.1`` python-ase-3.4.1.1765.tar.gz_
+ 3.4.0_ Apr 23 2010 ``svn co -r 1574 $ASE_TAGS/3.4.0 ase-3.4.0`` python-ase-3.4.0.1574.tar.gz_
+ 3.3.1_ Jan 20 2010 ``svn co -r 1390 $ASE_TAGS/3.3.1 ase-3.3.1`` python-ase-3.3.1.1390.tar.gz_
+ 3.2.0_ Sep 4 2009  ``svn co -r 1121 $ASE_TAGS/3.2.0 ase-3.2.0`` python-ase-3.2.0.1121.tar.gz_
+ 3.1.0_ Mar 27 2009 ``svn co -r 846 $ASE_TAGS/3.1.0 ase-3.1.0``  python-ase-3.1.0.846.tar.gz_
+ 3.0.0_ Nov 13 2008 ``svn co -r 657 $ASE_TAGS/3.0.0 ase-3.0.0``  python-ase-3.0.0.657.tar.gz_
+======= =========== ============================================ =============================
+
+.. _3.5.1:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.5.1
+
+.. _python-ase-3.5.1.2175.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.5.1.2175.tar.gz
+
+.. _3.4.1:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.4.1
+
+.. _python-ase-3.4.1.1765.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.4.1.1765.tar.gz
+
+.. _3.4.0:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.4.0
+
+.. _python-ase-3.4.0.1574.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.4.0.1574.tar.gz
+
+.. _3.3.1:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.3.1
+
+.. _python-ase-3.3.1.1390.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.3.1.1390.tar.gz
+
+.. _3.2.0:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.2.0
+
+.. _python-ase-3.2.0.1121.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.2.0.1121.tar.gz
+
+.. _3.1.0:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.1.0
+
+.. _python-ase-3.1.0.846.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.1.0.846.tar.gz
+
+.. _3.0.0:
+    https://trac.fysik.dtu.dk/projects/ase/browser/tags/3.0.0
+
+.. _python-ase-3.0.0.657.tar.gz:
+    https://wiki.fysik.dtu.dk/ase-files/python-ase-3.0.0.657.tar.gz
+
+
+
+.. _latest_development_release:
+
+Latest development release
+==========================
+
+The latest revision can be obtained like this::
+
+  $ svn checkout https://svn.fysik.dtu.dk/projects/ase/trunk ase
+
+or from the daily snapshot: `<python-ase-snapshot.tar.gz>`_.
+
+.. note::
+
+   The recommended checkout path is :envvar:`$HOME`.
+
+.. _installation:
+
+============
+Installation
+============
+
+After downloading create the link to the requested version, e.g.:
+
+- if retrieved from ``svn``::
+
+   $ cd $HOME
+   $ ln -s ase-3.5.1 ase
+    
+- if retrieved as ``tarball``::
+
+   $ cd $HOME
+   $ tar zxf python-ase-3.5.1.2175.tar.gz
+   $ ln -s python-ase-3.5.1.2175 ase
+
+It is sufficient to
+put the directory :file:`$HOME/ase` in your :envvar:`PYTHONPATH`
+environment variable, and the directory :file:`$HOME/ase/tools` in
+your :envvar:`PATH` environment variable.  Do this permanently in
+your :file:`~/.bashrc` file::
+
+  export PYTHONPATH=$HOME/ase:$PYTHONPATH
+  export PATH=$HOME/ase/tools:$PATH
+
+or your :file:`~/.cshrc` file::
+
+  setenv PYTHONPATH ${HOME}/ase:${PYTHONPATH}
+  setenv PATH ${HOME}/ase/tools:${PATH}
+
+Instead of :envvar:`HOME`, you may use any other directory.
+
+.. index:: test
+
+If you have root-permissions, you can install ASE system-wide::
+
+  $ cd ase
+  $ sudo python setup.py install
+
+.. _running_tests:
+
+Run the tests
+=============
+
+Make sure that everything works by running the :mod:`test
+suite <test>`.  This will create many files, so run the tests in a new
+directory (preferably using bash)::
+
+  $ bash
+  $ mkdir /tmp/testase.$$; cd /tmp/testase.*
+  $ python ~/ase/tools/testase.py 2>&1 | tee testase.log
+
+.. note::
+
+   The last test :trac:`ase/test/COCu111.py` requires closing
+   the graphics windows to terminate the whole test-suite.
+
+If any of the tests fail,
+then please send us :file:`testase.log` (see :ref:`bugs`).
+
+.. note::
+
+   If matplotlib_ or pygtk_ is not installed, one of the tests will
+   fail - avoid this with::
+
+     $ testase.py --no-display
+
diff --git a/doc/exercises/exercises.rst b/doc/exercises/exercises.rst
new file mode 100644
index 0000000..9fc305f
--- /dev/null
+++ b/doc/exercises/exercises.rst
@@ -0,0 +1,22 @@
+.. _exercises:
+
+---------
+Exercises
+---------
+
+.. note::
+
+   CAMd summer school participants should read `this page`_ before
+   doing the SIESTA exercises.
+
+.. _this page: https://wiki.fysik.dtu.dk/gpaw/exercises/summerschool.html#siesta
+
+Exercise 2 contains a script which takes somewhat long time.  For this
+reason you may want to submit the script to Niflheim in advance,
+before analyzing the results in Exercise 1.
+
+.. toctree::
+   :maxdepth: 2
+
+   siesta1/siesta1
+   siesta2/siesta2
diff --git a/doc/exercises/siesta1/answer1.py b/doc/exercises/siesta1/answer1.py
new file mode 100644
index 0000000..9f26dcb
--- /dev/null
+++ b/doc/exercises/siesta1/answer1.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# creates:  ener.png distance.png angle.png
+import os
+import matplotlib
+matplotlib.use('Agg')
+import pylab as plt
+
+
+e_s = [0.01,0.1,0.2,0.3,0.4,0.5]
+E = [-463.2160, -462.9633, -462.4891, -462.0551,
+     -461.5426, -461.1714]
+d = [1.1131, 1.1046, 1.0960, 1.0901,
+     1.0857, 1.0810]
+alpha = [100.832453365, 99.568214268, 99.1486065462,
+         98.873671379, 98.1726341945, 98.0535643778]
+
+fig=plt.figure(figsize=(3, 2.5))
+fig.subplots_adjust(left=.29, right=.96, top=.9, bottom=0.16)
+plt.plot(e_s, E, 'o-')
+plt.xlabel(u'Energy shift [eV]')
+plt.ylabel(u'Energy [eV]')
+plt.title('Total Energy vs Eshift')
+plt.savefig('ener.png')
+
+fig=plt.figure(figsize=(3, 2.5))
+fig.subplots_adjust(left=.24, right=.96, top=.9, bottom=0.16)
+plt.plot(e_s, d, 'o-')
+plt.xlabel(u'Energy shift [eV]')
+plt.ylabel(u'O-H distance [Å]')
+limits = plt.axis('tight')
+plt.title('O-H distance vs Eshift')
+plt.savefig('distance.png')
+
+fig=plt.figure(figsize=(3, 2.5))
+fig.subplots_adjust(left=.26, right=.96, top=.9, bottom=0.16)
+plt.plot(e_s, alpha, 'o-')
+plt.xlabel(u'Energy shift [eV]')
+plt.ylabel(u'H20 angle')
+limits = plt.axis('tight')
+plt.title('O-H distance vs Eshift')
+plt.savefig('angle.png')
diff --git a/doc/exercises/siesta1/h2o.py b/doc/exercises/siesta1/h2o.py
new file mode 100644
index 0000000..8a2a7e3
--- /dev/null
+++ b/doc/exercises/siesta1/h2o.py
@@ -0,0 +1,51 @@
+import time
+import numpy as np
+
+from ase import Atoms, units
+from ase.optimize import QuasiNewton
+from ase.calculators.siesta import Siesta
+
+# Set up a H2O molecule by specifying atomic positions
+h2o = Atoms(symbols='H2O',
+            positions=[( 0.776070, 0.590459, 0.00000),
+                       (-0.776070, 0.590459, 0.00000),
+                       (0.000000,  -0.007702,  -0.000001)],
+            pbc=(1,1,1))
+
+# Center the molecule in the cell with some vacuum around
+h2o.center(vacuum=6.0)
+
+# Select some energy-shifts for the basis orbitals
+e_shifts = [0.01,0.1,0.2,0.3,0.4,0.5]
+
+# Run the relaxation for each energy shift, and print out the
+# corresponding total energy, bond length and angle
+
+
+for e_s in e_shifts:
+    starttime = time.time()
+    calc = Siesta('h2o',meshcutoff=200.0 * units.Ry, mix=0.5, pulay=4)
+    calc.set_fdf('PAO.EnergyShift', e_s * units.eV)    
+    calc.set_fdf('PAO.SplitNorm', 0.15)
+    calc.set_fdf('PAO.BasisSize', 'SZ')
+    h2o.set_calculator(calc)
+    # Make a -traj file named h2o_current_shift.traj:      
+    dyn = QuasiNewton(h2o, trajectory='h2o_%s.traj' % e_s)
+    dyn.run(fmax=0.02)      # Perform the relaxation      
+    E = h2o.get_potential_energy()
+    print                                # Make the output more readable      
+    print "E_shift: %.2f" %e_s       
+    print "----------------"
+    print "Total Energy: %.4f" % E       # Print total energy      
+    d = h2o.get_distance(0,2)
+    print "Bond length: %.4f" % d        # Print bond length      
+    p = h2o.positions
+    d1 = p[0] - p[2]
+    d2 = p[1] - p[2]
+    r = np.dot(d1, d2) / (np.linalg.norm(d1) * np.linalg.norm(d2))
+    angle = np.arccos(r) / pi * 180
+    print "Bond angle: %.4f" % angle      # Print bond angle
+    endtime = time.time()
+    walltime = endtime - starttime
+    print 'Wall time: %.5f' % walltime
+    print                                # Make the output more readable      
diff --git a/doc/exercises/siesta1/siesta1.rst b/doc/exercises/siesta1/siesta1.rst
new file mode 100644
index 0000000..d6d6ff1
--- /dev/null
+++ b/doc/exercises/siesta1/siesta1.rst
@@ -0,0 +1,139 @@
+.. _siesta1:
+
+--------------------
+Siesta 1: Basis Sets
+--------------------
+
+This exercise is intended to illustrate the definition of basis sets
+with different numbers of orbitals and localization radii in
+SIESTA. It is based on the :mol:`H_2O` molecule. You will first use
+standard basis sets generated by SIESTA, and then a basis set
+specifically optimized for the :mol:`H_2O` molecule. The tips at
+the end of this page will help you in analyzing the results of the
+calculations.
+
+We are going to use the ASE interface to the :mod:`siesta` calculator,
+for the official SIESTA documentation see here_.
+
+.. _here: http://www.uam.es/siesta/
+
+
+Part 1: Standard basis sets
+---------------------------
+
+In the following script you will relax the structure of the :mol:`H_2O`
+molecule to find its equilibrium structure. 
+
+.. literalinclude:: h2o.py
+
+Run the script using three different basis sets: single-Z (SZ),
+double-Z (DZ) and double-Z plus polarization (DZP). Run it in
+different directories for each basis set, since the run will produce a
+large amount of output files. The bases are defined in the script
+through these three lines::
+
+  calc.set_fdf('PAO.BasisSize','SZ')
+  calc.set_fdf('PAO.EnergyShift',e_s * eV)
+  calc.set_fdf('PAO.SplitNorm',0.15)
+
+For each of the basis sets the script will run the relaxation for
+different ``EnergyShift`` parameters. Look at the results as a
+function of basis size and localization radius (or energy shift).
+
+In particular, you should look at:
+
+* Total energy
+* Bond lenghts at the relaxed structure
+* Bond angles at the relaxed structure
+* Execution time
+* Radius of each of the orbitals
+* Shape of the orbitals
+
+The script will print bond lengths and angles, total energy plus the
+wall time for each energy shift.  The radii of the orbitals from the
+last run can be found in the file ``h2o.txt`` along with miscellaneous
+other informations.  Orbital shapes from the last run are stored in
+the ``ORB.*`` files, which contain the value of the orbitals
+versus radius. (Note that the standard way to plot the orbitals
+is to multiply the orbital by :math:`r^l`).
+
+
+You can visualize both the static atoms and
+the relaxation trajectory using the the ASE :mod:`gui`. For example
+you can visualize the relaxation trajectory from the command line by
+doing::
+
+  $ ag h2o_0.1.traj
+
+Note that you will get a trajectory file ``.traj`` for each energy
+shift in the loop.
+
+Try to get plots like the following, obtained for a SZ basis:
+
+|en| |dist| |ang|
+
+.. |en| image:: ener.png
+.. |dist| image:: distance.png
+.. |ang| image:: angle.png
+
+
+Try to answer these questions:
+
+1. What is the best basis set that you have found? Why?
+2. How do the results compare with experiment?  
+3. What do you consider a reasonable basis for the molecule, if you need an accuracy in the geometry of about 1%?  
+4. In order to assess convergence with respect to basis set size, should you compare the results with the experimental ones, or with those of a converged basis set calculation?
+
+
+Part 2: Optimized basis sets
+----------------------------
+
+You will now do the same calculations as in Part 1, but now using a
+basis set specifically optimized for the :mol:`H_2O` molecule. Use the
+following optimized block instead of the basis-definition lines in
+Part1::
+
+ calc.set_fdf('PAO.Basis',
+                 ["""H     2      0.22
+                 n=1    0    2   E      2.07      0.00
+                 4.971   1.771
+                 1.000   1.000
+                 n=2    1    1   E      0.89      0.01
+                 4.988
+                 1.000
+                 O     3     -0.20
+                 n=2    0    2   E      0.00      1.31
+                 5.000   2.581
+                 1.000   1.000
+                 n=2    1    2   E      0.00      5.28
+                 6.500   2.487
+                 1.000   1.000
+                 n=3    2    1   E    104.31      0.00
+                 3.923
+                 1.000"""])
+
+Compare the parameters with those of the calculations in Part 1.
+
+Do the runs with this basis set, and compare the results with the
+previous ones.
+
+
+Tips
+----
+
+Tip 1: You can see the radii of the orbitals in the output file, just after the line reading::
+
+  printput: Basis input ----------------------------------------------
+
+Tip 2: You can look at the shape of the orbitals by plotting the contents of the ORB* files generated by SIESTA.
+
+Tip 3: You will find a lot of information on the run in the ``.txt`` output file
+
+Tip 4: In ``ag`` you can select :menuselection:`View --> VMD` to start
+the VMD viewer. There you can change the atom representation to what
+you feel is more convenient. Do that by selecting
+:menuselection:`Graphics --> Representations` in the top bar.
+
+Tip 5: SIESTA will store basis functions in ``ORB.*`` files.  These files can be plotted using the :command:`xmgrace` command on the GBAR like this::
+
+  $ xmgrace ORB.S1.1.O ORB.S2.1.O
diff --git a/doc/exercises/siesta2/geom.xyz b/doc/exercises/siesta2/geom.xyz
new file mode 100644
index 0000000..6b8197f
--- /dev/null
+++ b/doc/exercises/siesta2/geom.xyz
@@ -0,0 +1,41 @@
+   38
+ 
+H       6.058652    1.915727   -4.631363
+H       6.935557    1.916158   -4.638525
+Si      5.052343    1.915094   -8.752970
+Si      7.311756    1.916045   -8.100813
+Si      5.041784    5.750281   -8.773724
+Si      7.297518    5.739617   -8.100675
+Si      3.918967    0.000013   -9.554657
+Si      7.550697   -0.000414   -9.520436
+Si      3.916151    3.831464   -9.573533
+Si      7.550925    3.830172   -9.516466
+Si      1.889783    0.000004  -10.794937
+Si      5.759494   -0.000324  -11.076500
+Si      1.890663    3.833056  -10.808745
+Si      5.762755    3.829004  -11.066801
+Si      1.916838    1.917154  -12.211988
+Si      5.750117    1.914811  -12.374348
+Si      1.915860    5.747637  -12.213599
+Si      5.749943    5.748165  -12.374435
+Si      3.830647    1.915329  -13.619311
+Si      7.661294    1.915329  -13.619311
+Si      3.830190    5.745003  -13.619694
+Si      7.663737    5.747074  -13.619579
+Si      3.830647    0.000000  -14.981255
+Si      7.661294    0.000000  -14.981255
+Si      3.829498    3.829663  -14.981428
+Si      7.659365    3.829663  -14.981413
+Si      1.915329    0.000000  -16.343186
+Si      5.745976    0.000000  -16.343186
+Si      1.915316    3.829668  -16.342640
+Si      5.750111    3.829668  -16.342646
+H       1.915375    1.072918  -17.317827
+H       1.915376   -1.072897  -17.317836
+H       5.745901    1.072393  -17.319685
+H       5.745901   -1.072379  -17.319678
+H       1.915361    4.913391  -17.321812
+H       1.916398    2.750110  -17.321821
+H       5.750110    4.908576  -17.315062
+H       5.748038    2.751809  -17.323343
+
diff --git a/doc/exercises/siesta2/siesta2.py b/doc/exercises/siesta2/siesta2.py
new file mode 100644
index 0000000..f551e87
--- /dev/null
+++ b/doc/exercises/siesta2/siesta2.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+from ase.io import read
+from ase.constraints import FixAtoms
+from ase.calculators.siesta import Siesta
+from ase.md import VelocityVerlet
+from ase import units
+
+# Read in the geometry from a xyz file, set the cell, boundary conditions and center
+atoms = read('geom.xyz')
+atoms.set_cell([7.66348,7.66348,7.66348*2])
+atoms.set_pbc((1,1,1))
+atoms.center()
+
+# Set initial velocities for hydrogen atoms along the z-direction
+p = atoms.get_momenta()
+p[0,2]= -1.5
+p[1,2]= -1.5
+atoms.set_momenta(p)
+
+# Keep some atoms fixed during the simulation
+atoms.set_constraint(FixAtoms(indices=range(18,38)))
+
+# Set the calculator and attach it to the system
+calc = Siesta('si001+h2',basis='SZ',xc='PBE',meshcutoff=50*units.Ry)
+calc.set_fdf('PAO.EnergyShift', 0.25 * units.eV) 
+calc.set_fdf('PAO.SplitNorm', 0.15)       
+atoms.set_calculator(calc)
+
+# Set the VelocityVerlet algorithm and run it
+dyn = VelocityVerlet(atoms,dt=1.0 * units.fs,trajectory='si001+h2.traj')
+dyn.run(steps=100)
+
diff --git a/doc/exercises/siesta2/siesta2.rst b/doc/exercises/siesta2/siesta2.rst
new file mode 100644
index 0000000..ba92f82
--- /dev/null
+++ b/doc/exercises/siesta2/siesta2.rst
@@ -0,0 +1,58 @@
+.. _siesta2:
+
+----------------------------
+Siesta 2: Molecular Dynamics
+----------------------------
+
+This exercise is intended to illustrate a Molecular Dynamics run with
+the SIESTA calculator. The system is a Si(001) surface, in the 2x1
+reconstruction with asymmetric dimers. The simulation cell contains
+two dimers. An H2 molecule approaches the surface, above one of the
+dimers, and dissociates, ending up with a H atom bonded to each of the
+Si atoms in the dimer (and thus leading to a symmetric dimer). You can
+get the ``xyz`` file with the initial geometry :svn:`doc/exercises/siesta2/geom.xyz`.
+
+.. literalinclude:: siesta2.py
+
+Note that both H atoms are given an initial velocity towards the
+surface through the lines::
+
+  p = atoms.get_momenta()
+  p[0,2]= -1.5 
+  p[1,2]= -1.5 
+  atoms.set_momenta(p)
+
+Run the program, and check the results. You can visualize the dynamics
+using the trajectory file with the help of the ASE :mod:`gui`. For
+example you can visualize the behaviour of the potential and total
+energies during the dynamics by doing::
+
+  $ ag -b si001+h2.traj -g i,e,e+ekin
+
+The :option:`-b` option turns on plotting of bonds between the atoms.
+Check that the total energy is a conserved quantity in this
+microcanonical simulation.
+
+You can also use VMD to visualize the trajectory.  To do this, you
+need a ``.xyz`` file that you can write using :command:`ag`::
+
+  $ ag si001+h2.traj -o si.xyz -r 2,2,1
+
+Then simply open the file using VMD::
+
+  $ vmd si.xyz
+
+and set Graphics/Representations from the main menu in order to get a
+nice picture.
+
+You can also try to repeat the simulation with a different
+:mod:`dynamics <md>`. For instance you can model a canonical ensemble
+with the Langevin dynamics, which couples the system to a heat bath::
+
+  dyn = Langevin(atoms, 1 * fs, kB * 300, 0.002, trajectory=traj)
+
+
+
+
+
+
diff --git a/doc/ext.py b/doc/ext.py
new file mode 100644
index 0000000..2904428
--- /dev/null
+++ b/doc/ext.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+import os
+import types
+from os.path import join
+from sys import executable
+from stat import ST_MTIME
+from docutils import nodes, utils
+from docutils.parsers.rst.roles import set_classes
+
+
+def mol_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+    n = []
+    t = ''
+    while text:
+        if text[0] == '_':
+            n.append(nodes.Text(t))
+            t = ''
+            n.append(nodes.subscript(text=text[1]))
+            text = text[2:]
+        else:
+            t += text[0]
+            text = text[1:]
+    n.append(nodes.Text(t))
+    return n, []
+
+def svn_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+    if text[-1] == '>':
+        i = text.index('<')
+        name = text[:i - 1]
+        text = text[i + 1:-1]
+    else:
+        name = text
+    ref = 'http://svn.fysik.dtu.dk/projects/ase/trunk/' + text
+    set_classes(options)
+    node = nodes.reference(rawtext, name, refuri=ref,
+                           **options)
+    return [node], []
+
+def trac_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+    if text[-1] == '>':
+        i = text.index('<')
+        name = text[:i - 1]
+        text = text[i + 1:-1]
+    else:
+        name = text
+    ref = 'http://trac.fysik.dtu.dk/projects/ase/browser/trunk/' + text
+    set_classes(options)
+    node = nodes.reference(rawtext, name, refuri=ref,
+                           **options)
+    return [node], []
+
+def epydoc_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+    name = None
+    if text[-1] == '>':
+        i = text.index('<')
+        name = text[:i - 1]
+        text = text[i + 1:-1]
+
+    components = text.split('.')
+    if components[0] != 'ase':
+        components.insert(0, 'ase')
+
+    if name is None:
+        name = components[-1]
+
+    try:
+        for n in range(2, len(components) + 1):
+            module = __import__('.'.join(components[:n]))
+    except ImportError:
+        for component in components[1:n]:
+            module = getattr(module, component)
+            ref = '.'.join(components[:n])
+        if isinstance(module, (type, types.ClassType)):
+            ref += '-class.html'
+        else:
+            ref += '-module.html'
+        if n < len(components):
+            ref += '#' + components[-1]
+    else:
+        ref = '.'.join(components) + '-module.html'
+
+    ref = 'http://wiki.fysik.dtu.dk/ase/epydoc/' + ref
+    set_classes(options)
+    node = nodes.reference(rawtext, name,
+                           refuri=ref,
+                           **options)
+    return [node], []
+
+def setup(app):
+    app.add_role('mol', mol_role)
+    app.add_role('svn', svn_role)
+    app.add_role('trac', trac_role)
+    app.add_role('epydoc', epydoc_role)
+    #import atexit
+    #atexit.register(fix_sidebar)
+    create_png_files()
+
+def create_png_files():
+    for dirpath, dirnames, filenames in os.walk('.'):
+        for filename in filenames:
+            if filename.endswith('.py'):
+                path = join(dirpath, filename)
+                lines = open(path).readlines()
+                line = lines[0]
+                if 'coding: utf-8' in line:
+                    line = lines[1]
+                if line.startswith('# creates:'):
+                    t0 = os.stat(path)[ST_MTIME]
+                    run = False
+                    for file in line.split()[2:]:
+                        try:
+                            t = os.stat(join(dirpath, file))[ST_MTIME]
+                        except OSError:
+                            run = True
+                            break
+                        else:
+                            if t < t0:
+                                run = True
+                                break
+                    if run:
+                        print 'running:', join(dirpath, filename)
+                        e = os.system('cd %s; %s %s' % (dirpath, executable, filename))
+                        if e != 0:
+                            raise RuntimeError('FAILED!')
+                        for file in line.split()[2:]:
+                            print dirpath, file
+                            #os.rename(join(dirpath, file),
+                            #          join('_static', file))
+        if '.svn' in dirnames:
+            dirnames.remove('.svn')
diff --git a/doc/faq.rst b/doc/faq.rst
new file mode 100644
index 0000000..d1bc296
--- /dev/null
+++ b/doc/faq.rst
@@ -0,0 +1,76 @@
+.. _faq:
+
+==========================
+Frequently Asked Questions
+==========================
+
+
+ASE-GUI
+=======
+
+See also the :mod:`documantation for ag <gui>`.
+
+How do I export images from a trajectory to png or pov files?
+-------------------------------------------------------------
+
+With ag, you can choose :menuselection:`File --> Save`, but this is
+not fun if you need to do it for many images.  Here is how to do it on
+the command line for a number of images::
+
+  ag images.traj at 0 -o image0.pov 
+  ag images.traj at 1 -o image1.pov 
+  ag images.traj at 2 -o image2.pov 
+
+If you have many images, it will be easier to do it using the Python
+interpreter:
+
+>>> from ase import *
+>>> for n, image in enumerate(read('images.traj@:3')):
+...     write('image%d.pov' % n, image, run_povray=True, pause=False,
+...           rotation='-90x,10z')
+
+Here, we also:
+
+* run povray to generate png files
+
+* disable pausing between the images
+
+* set a rotation (choose :menuselection:`View --> Rotate ...` in ag to select
+  the best rotation angles)
+
+Try:
+
+>>> help(write)
+
+to see all possibilities or read more :func:`here <ase.io.write>`.
+
+
+
+General
+=======
+
+Citation: how should I cite ASE?
+--------------------------------
+
+If you find ASE useful in your research please cite:
+
+   | S. R. Bahn and K. W. Jacobsen
+   | `An object-oriented scripting interface to a legacy electronic structure code`__
+   | Comput. Sci. Eng., Vol. **4**, 56-66, 2002
+
+   __ http://dx.doi.org/10.1109/5992.998641
+
+BibTex (:svn:`doc/ASE.bib`):
+
+.. literalinclude:: ASE.bib
+
+
+Download
+========
+
+Trying to checkout the code via SVN resulted::
+
+ [~]$ svn checkout "https://svn.fysik.dtu.dk/projects/ase/trunk"
+ svn: Unrecognized URL scheme 'https://svn.fysik.dtu.dk/projects/ase/trunk'
+
+This error is diplayed in case the library 'libsvn_ra_dav' is missing on your system. The library is used by SVN, but is not installed by default. 
diff --git a/doc/glossary.rst b/doc/glossary.rst
new file mode 100644
index 0000000..d4a223d
--- /dev/null
+++ b/doc/glossary.rst
@@ -0,0 +1,69 @@
+.. _glossary:
+
+Glossary
+========
+
+.. glossary::
+
+  API 
+    Application Programming Interface.  Automatically generated
+    documentation from :term:`docstring`\ s in the source code using Epydoc_.
+    See :epydoc:`here <ase>`.
+
+  ASE
+    Atomic Simulation Environment.
+
+  Class
+    XXX
+
+  Constructor
+    XXX
+
+  Docstring
+    XXX
+
+  DFT
+    Density Functional Theory.
+
+  EMT
+    Effective Medium Theory.
+
+  HF
+    Hartree-Fock approximation XXX ...
+
+  Instance
+    XXX
+ 
+  LAPW
+    In the linearized augmented plane wave (LAPW) method the unit cell
+    of the crystal is divided into two different types of regions.
+    The first one, called the muffin-tin region, consists of spheres
+    centered around the atoms, while the second one, called the
+    interstitial region consists of the remaining part of the unit
+    cell. In the muffin-tin spheres the basis consists of atomic like
+    functions to account for the rapid changes of the wave function in
+    this area, whereas in the interstitial region the basis functions
+    are plane waves, since the wave function changes only slowly at
+    some distance from the atomic sites.
+    
+  Namespace
+    An abstract container of the current :term:`scope`'s variables.
+
+  ndarray
+    :term:`NumPy`'s *n*-dimensional array.  See :ref:`numpy`.
+
+  NumPy
+    XXX   See :ref:`numpy`.
+
+  Method
+    XXX
+
+  Python
+    XXX :ref:`python_info`.
+
+  Scope
+    An enclosing context where values and expressions are associated.
+
+
+    
+.. _Epydoc: http://epydoc.sourceforge.net
diff --git a/doc/images.py b/doc/images.py
new file mode 100644
index 0000000..5b711a3
--- /dev/null
+++ b/doc/images.py
@@ -0,0 +1,7 @@
+# creates: ase/ag.png ase/dft/water_divide_surf.png
+from urllib import urlretrieve
+def setup(app):
+    pass
+urlretrieve('http://wiki.fysik.dtu.dk/ase-files/ag.png', 'ase/ag.png')
+urlretrieve('http://wiki.fysik.dtu.dk/ase-files/water_divide_surf.png',
+            'ase/dft/water_divide_surf.png')
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..09fabfc
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,116 @@
+=============================
+Atomic Simulation Environment
+=============================
+
+The Atomic Simulation Environment (ASE) is the common part of the
+simulation tools developed at CAMd_.  ASE provides Python_ modules
+for manipulating atoms, analyzing simulations, visualization etc.
+
+.. note::
+
+  The old ASE-2 webpage has moved to http://wiki.fysik.dtu.dk/ase2.
+
+Supported :mod:`calculators`:
+
+   |abinit| |Asap| |CASTEP| |dftb| |elk| |exciting| |EMT| |fhi-aims| 
+   |fleur| |gpaw| |hotbit| |jacapo| |lammps| |siesta| |turbomole| |vasp| 
+
+.. |abinit| image:: _static/abinit.png
+   :target: ase/calculators/abinit.html
+   :align: middle
+.. |Asap| image:: _static/asap.png
+   :target: http://wiki.fysik.dtu.dk/asap
+   :align: middle
+.. |CASTEP| image:: _static/castep.png
+   :target: ase/calculators/castep.html
+   :align: middle
+.. |elk| image:: _static/elk.png
+   :target: http://elk.sourceforge.net/
+   :align: middle
+.. |EMT| image:: _static/emt.png
+   :target: ase/calculators/emt.html
+   :align: middle
+.. |exciting| image:: _static/exciting.png
+   :target: ase/calculators/exciting.html
+   :align: middle   
+.. |dftb| image:: _static/dftb.png
+   :target: ase/calculators/dftb.html
+   :align: middle
+.. |fhi-aims| image:: _static/fhi-aims.png
+   :target: ase/calculators/FHI-aims.html
+   :align: middle
+.. |fleur| image:: _static/fleur.png
+   :target: ase/calculators/fleur.html
+   :align: middle
+.. |gpaw| image:: _static/gpaw.png
+   :target: http://wiki.fysik.dtu.dk/gpaw
+   :align: middle
+.. |hotbit| image:: _static/hotbit.png
+   :target: https://trac.cc.jyu.fi/projects/hotbit
+   :align: middle
+.. |jacapo| image:: _static/jacapo.png
+   :target: ase/calculators/jacapo.html
+   :align: middle
+.. |lammps| image:: _static/lammps.png
+   :target: ase/calculators/lammps.html
+   :align: middle
+.. |siesta| image:: _static/siesta.png
+   :target: ase/calculators/siesta.html
+   :align: middle
+.. |turbomole| image:: _static/tm_logo_l.png
+   :target: ase/calculators/turbomole.html
+   :align: middle 
+.. |vasp| image:: _static/vasp.png
+   :target: ase/calculators/vasp.html
+   :align: middle
+
+.. _Asap: http://wiki.fysik.dtu.dk/asap
+.. _MMTK: http://dirac.cnrs-orleans.fr/MMTK
+.. _Python: http://www.python.org
+.. _Trac: http://trac.fysik.dtu.dk/projects/ase/report/1
+
+.. _news:
+
+
+News
+====
+
+* Bugfix release: :ref:`ASE version 3.5.1 <releasenotes>` (24 May 2011).
+
+* :ref:`ASE version 3.5.0 <releasenotes>` released (13 April 2011).
+
+* :ref:`ASE version 3.4.1 <download_and_install>` released (11 August 2010).
+
+* :ref:`ASE version 3.4 <download_and_install>` released (23 April 2010).
+
+* :ref:`ASE version 3.3 <download_and_install>` released (11 January 2010).
+
+* :ref:`ASE version 3.2 <download_and_install>` released (4 September 2009).
+
+* ASE has reached `revision 1000`_ (16 July 2009).
+
+* :ref:`ASE version 3.1.0 <download_and_install>` released (27 March 2009).
+
+* Improved :mod:`vibrations` module: More accurate and
+  possibility to calculate :mod:`infrared intensities <infrared>` (13
+  March 2009).
+
+* :ref:`ASE version 3.0.0 <download_and_install>` released (13 November 2008).
+
+* Asap_ version 3.0.2 released (15 October 2008).
+
+* An experimental abinit interface released (9 June 2008).
+
+* Thursday April 24 will be ASE documentation-day.  Ten people from
+  CAMd/Cinf will do a "doc-sprint" from 9 to 16.  (17 Apr 2008)
+
+* The new ASE-3.0 Sphinx_ page is now up and running!  (2 Apr 2008)
+
+* A beta version of the new ASE-3.0 will be used for the
+  electronic structure course at CAMd_.  (10 Jan 2008)
+
+
+.. _revision 1000: http://www.camd.dtu.dk/Events/Seneste_nyt.aspx?guid={08853DD1-D037-47C8-ACEF-1EA40A88BB6C}
+.. _Sphinx: http://sphinx.pocoo.org
+.. _CAMd: http://www.camd.dtu.dk
+
diff --git a/doc/logo.py b/doc/logo.py
new file mode 100644
index 0000000..a52a032
--- /dev/null
+++ b/doc/logo.py
@@ -0,0 +1,49 @@
+import os
+import numpy as np
+
+from ase import Atoms, Atom
+
+ase = """\
+ H   HH HHH
+H H H   H
+HHH  H  HH
+H H   H H
+H H HH  HHH"""
+
+d = 0.8
+
+logo = Atoms()
+for i, line in enumerate(ase.split('\n')):
+    for j, c in enumerate(line):
+        if c == 'H':
+            logo.append(Atom('H', [d * j, d * i, 0]))
+logo.center(vacuum=2.0)
+
+#view(logo)
+
+if 1:
+    from gpaw import GPAW
+    calc = GPAW()
+    logo.set_calculator(calc)
+    e = logo.get_potential_energy()
+    calc.write('logo2.gpw')
+if 0:
+    from gpaw import GPAW
+    calc = GPAW('logo2.gpw', idiotproof=0)
+
+
+if 1:
+    print calc.density.nt_sg.shape
+    n = calc.density.nt_sg[0, :, :, 10]
+    #1c4e63
+    c0 = np.array([19, 63, 82.0]).reshape((3, 1, 1)) / 255
+    c1 = np.array([1.0, 1, 0]).reshape((3, 1, 1))
+    a = c0 + n / n.max() * (c1 - c0)
+    import pylab as p
+    i = p.imshow(a.T, aspect=True)
+    i.write_png('ase.png')
+    os.system('convert ase.png -resize 50x25 ase.png')
+    #p.axis('off')
+    p.show()
+    #p.savefig('ase.png', dpi=8)
+
diff --git a/doc/mailinglists.rst b/doc/mailinglists.rst
new file mode 100644
index 0000000..9e659ac
--- /dev/null
+++ b/doc/mailinglists.rst
@@ -0,0 +1,40 @@
+.. _mailing_lists:
+
+=============
+Mailing Lists
+=============
+
+There is a mailing list for discussing ASE: 
+
+* ase-users_
+
+The mailing lists below are of interest for active ASE developers only:
+
+* ase-developers_
+* ase-svncheckins_
+
+or (mainly) `dacapo <http://wiki.fysik.dtu.dk/dacapo>`_
+/ `ASE-2 <http://wiki.fysik.dtu.dk/ase2>`_ users / developers:
+
+* campos_
+* campos-devel_
+
+.. _gridpaw-developers: http://lists.berlios.de/mailman/listinfo/gridpaw-developer
+.. _gpaw-svncheckins: https://listserv.fysik.dtu.dk/mailman/listinfo/gpaw-svncheckins
+.. _ase-developers: https://listserv.fysik.dtu.dk/mailman/listinfo/ase-developers
+.. _ase-svncheckins: https://listserv.fysik.dtu.dk/mailman/listinfo/ase-svncheckins
+.. _ase-users: https://listserv.fysik.dtu.dk/mailman/listinfo/ase-users
+.. _campos: https://listserv.fysik.dtu.dk/mailman/listinfo/campos
+.. _campos-devel: https://listserv.fysik.dtu.dk/mailman/listinfo/campos-devel
+.. _BerliOS: http://www.berlios.de
+
+.. _irc:
+
+Internet Relay Chat
+===================
+
+We have the IRC channel ``#campos`` on FreeNode.  Please join us if you
+have any questions. For easy access, you can use this webclient_.
+
+.. _webclient: http://webchat.freenode.net/?randomnick=0&channels=campos
+
diff --git a/doc/mathml.py b/doc/mathml.py
new file mode 100644
index 0000000..d0cd7cb
--- /dev/null
+++ b/doc/mathml.py
@@ -0,0 +1,552 @@
+from docutils import nodes
+from docutils.writers.html4css1 import HTMLTranslator
+from sphinx.latexwriter import LaTeXTranslator
+
+# Define LaTeX math node:
+class latex_math(nodes.General, nodes.Element):
+    pass
+
+def math_role(role, rawtext, text, lineno, inliner,
+              options={}, content=[]):
+    i = rawtext.find('`')
+    latex = rawtext[i+1:-1]
+    try:
+        mathml_tree = parse_latex_math(latex, inline=True)
+    except SyntaxError, msg:
+        msg = inliner.reporter.error(msg, line=lineno)
+        prb = inliner.problematic(rawtext, rawtext, msg)
+        return [prb], [msg]
+    node = latex_math(rawtext)
+    node['latex'] = latex
+    node['mathml_tree'] = mathml_tree
+    return [node], []
+
+
+try:
+    from docutils.parsers.rst import Directive
+except ImportError:
+    # Register directive the old way:
+    from docutils.parsers.rst.directives import _directives
+    def math_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+        latex = ''.join(content)
+        try:
+            mathml_tree = parse_latex_math(latex, inline=False)
+        except SyntaxError, msg:
+            error = state_machine.reporter.error(
+                msg, nodes.literal_block(block_text, block_text), line=lineno)
+            return [error]
+        node = latex_math(block_text)
+        node['latex'] = latex
+        node['mathml_tree'] = mathml_tree
+        return [node]
+    math_directive.arguments = None
+    math_directive.options = {}
+    math_directive.content = 1
+    _directives['math'] = math_directive
+else:
+    class math_directive(Directive):
+        has_content = True
+        def run(self): 
+            latex = ' '.join(self.content)
+            try:
+                mathml_tree = parse_latex_math(latex, inline=False)
+            except SyntaxError, msg:
+                error = self.state_machine.reporter.error(
+                    msg, nodes.literal_block(self.block_text, self.block_text),
+                    line=self.lineno)
+                return [error]
+            node = latex_math(self.block_text)
+            node['latex'] = latex
+            node['mathml_tree'] = mathml_tree
+            return [node]
+    from docutils.parsers.rst import directives
+    directives.register_directive('math', math_directive)
+
+def setup(app):
+    app.add_node(latex_math)
+    app.add_role('math', math_role)
+
+    # Add visit/depart methods to HTML-Translator:
+    def visit_latex_math_html(self, node):
+        mathml = ''.join(node['mathml_tree'].xml())
+        self.body.append(mathml)
+    def depart_latex_math_html(self, node):
+            pass
+    HTMLTranslator.visit_latex_math = visit_latex_math_html
+    HTMLTranslator.depart_latex_math = depart_latex_math_html
+
+    # Add visit/depart methods to LaTeX-Translator:
+    def visit_latex_math_latex(self, node):
+        inline = isinstance(node.parent, nodes.TextElement)
+        if inline:
+            self.body.append('$%s$' % node['latex'])
+        else:
+            self.body.extend(['\\begin{equation*}\\begin{split}',
+                              node['latex'],
+                              '\\end{split}\\end{equation*}'])
+    def depart_latex_math_latex(self, node):
+            pass
+    LaTeXTranslator.visit_latex_math = visit_latex_math_latex
+    LaTeXTranslator.depart_latex_math = depart_latex_math_latex
+
+
+# LaTeX to MathML translation stuff:
+class math:
+    """Base class for MathML elements."""
+    
+    nchildren = 1000000
+    """Required number of children"""
+    
+    def __init__(self, children=None, inline=None):
+        """math([children]) -> MathML element
+
+        children can be one child or a list of children."""
+        
+        self.children = []
+        if children is not None:
+            if type(children) is list:
+                for child in children:
+                    self.append(child)
+            else:
+                # Only one child:
+                self.append(children)
+
+        if inline is not None:
+            self.inline = inline
+
+    def __repr__(self):
+        if hasattr(self, 'children'):
+            return self.__class__.__name__ + '(%s)' % \
+                   ','.join([repr(child) for child in self.children])
+        else:
+            return self.__class__.__name__
+
+    def full(self):
+        """Room for more children?"""
+        
+        return len(self.children) >= self.nchildren
+    
+    def append(self, child):
+        """append(child) -> element
+
+        Appends child and returns self if self is not full or first
+        non-full parent."""
+        
+        assert not self.full()
+        self.children.append(child)
+        child.parent = self
+        node = self
+        while node.full():
+            node = node.parent
+        return node
+
+    def delete_child(self):
+        """delete_child() -> child
+
+        Delete last child and return it."""
+        
+        child = self.children[-1]
+        del self.children[-1]
+        return child
+
+    def close(self):
+        """close() -> parent
+
+        Close element and return first non-full element."""
+        
+        parent = self.parent
+        while parent.full():
+            parent = parent.parent
+        return parent
+
+    def xml(self):
+        """xml() -> xml-string"""
+        
+        return self.xml_start() + self.xml_body() + self.xml_end()
+    
+    def xml_start(self):
+        if not hasattr(self, 'inline'):
+            return ['<%s>' % self.__class__.__name__]
+        xmlns = 'http://www.w3.org/1998/Math/MathML'
+        if self.inline:
+            return ['<math xmlns="%s">' % xmlns]
+        else:
+            return ['<math xmlns="%s" mode="display">' % xmlns]
+
+    def xml_end(self):
+        return ['</%s>' % self.__class__.__name__]
+    
+    def xml_body(self):
+        xml = []
+        for child in self.children:
+            xml.extend(child.xml())
+        return xml
+
+class mrow(math): pass
+class mtable(math): pass
+class mtr(mrow): pass
+class mtd(mrow): pass
+
+class mx(math):
+    """Base class for mo, mi, and mn"""
+    
+    nchildren = 0
+    def __init__(self, data):
+        self.data = data
+
+    def xml_body(self):
+        return [self.data]
+
+class mo(mx):
+    translation = {'<': '<', '>': '>'}
+    def xml_body(self):
+        return [self.translation.get(self.data, self.data)]
+        
+class mi(mx): pass
+class mn(mx): pass
+
+class msub(math):
+    nchildren = 2
+
+class msup(math):
+    nchildren = 2
+
+class msqrt(math):
+    nchildren = 1
+    
+class mroot(math):
+    nchildren = 2
+    
+class mfrac(math):
+    nchildren = 2
+    
+class msubsup(math):
+    nchildren = 3
+    def __init__(self, children=None, reversed=False):
+        self.reversed = reversed
+        math.__init__(self, children)
+
+    def xml(self):
+        if self.reversed:
+##            self.children[1:3] = self.children[2:0:-1]
+            self.children[1:3] = [self.children[2], self.children[1]]
+            self.reversed = False
+        return math.xml(self)
+        
+class mfenced(math):
+    translation = {'\\{': '{', '\\langle': u'\u2329',
+                   '\\}': '}', '\\rangle': u'\u232A',
+                   '.': ''}
+    def __init__(self, par):
+        self.openpar = par
+        math.__init__(self)
+
+    def xml_start(self):
+        open = self.translation.get(self.openpar, self.openpar)
+        close = self.translation.get(self.closepar, self.closepar)
+        return ['<mfenced open="%s" close="%s">' % (open, close)]
+
+class mspace(math):
+    nchildren = 0
+
+class mstyle(math):
+    def __init__(self, children=None, nchildren=None, **kwargs):
+        if nchildren is not None:
+            self.nchildren = nchildren
+        math.__init__(self, children)
+        self.attrs = kwargs
+        
+    def xml_start(self):
+        return ['<mstyle '] + ['%s="%s"' % item
+                               for item in self.attrs.items()] + ['>']
+
+class mover(math):
+    nchildren = 2
+    def __init__(self, children=None, reversed=False):
+        self.reversed = reversed
+        math.__init__(self, children)
+
+    def xml(self):
+        if self.reversed:
+            self.children.reverse()
+            self.reversed = False
+        return math.xml(self)
+
+class munder(math):
+    nchildren = 2
+
+class munderover(math):
+    nchildren = 3
+    def __init__(self, children=None):
+        math.__init__(self, children)
+        
+class mtext(math):
+    nchildren = 0
+    def __init__(self, text):
+        self.text = text
+
+    def xml_body(self):
+        return [self.text]
+
+
+over = {'tilde': '~',
+        'hat': '^',
+        'bar': '_',
+        'vec': u'\u2192'}
+
+Greek = {
+    # Upper case greek letters:
+    'Phi': u'\u03a6', 'Xi': u'\u039e', 'Sigma': u'\u03a3', 'Psi': u'\u03a8', 'Delta': u'\u0394', 'Theta': u'\u0398', 'Upsilon': u'\u03d2', 'Pi': u'\u03a0', 'Omega': u'\u03a9', 'Gamma': u'\u0393', 'Lambda': u'\u039b'}
+greek = {
+    # Lower case greek letters:
+    'tau': u'\u03c4', 'phi': u'\u03d5', 'xi': u'\u03be', 'iota': u'\u03b9', 'epsilon': u'\u03f5', 'varrho': u'\u03f1', 'varsigma': u'\u03c2', 'beta': u'\u03b2', 'psi': u'\u03c8', 'rho': u'\u03c1', 'delta': u'\u03b4', 'alpha': u'\u03b1', 'zeta': u'\u03b6', 'omega': u'\u03c9', 'varepsilon': u'\u03b5', 'kappa': u'\u03ba', 'vartheta': u'\u03d1', 'chi': u'\u03c7', 'upsilon': u'\u03c5', 'sigma': u'\u03c3', 'varphi': u'\u03c6', 'varpi': u'\u03d6', 'mu': u'\u03bc', 'eta': u'\u03b7', 'theta': u'\u03b8', 'pi': u'\u03c0', 'varkappa': u'\u03f0', 'nu': u'\u03bd', 'gamma': u'\u03b3', 'lambda': u'\u03bb'}
+
+special = {
+    # Binary operation symbols:
+    'wedge': u'\u2227', 'diamond': u'\u22c4', 'star': u'\u22c6', 'amalg': u'\u2a3f', 'ast': u'\u2217', 'odot': u'\u2299', 'triangleleft': u'\u25c1', 'bigtriangleup': u'\u25b3', 'ominus': u'\u2296', 'ddagger': u'\u2021', 'wr': u'\u2240', 'otimes': u'\u2297', 'sqcup': u'\u2294', 'oplus': u'\u2295', 'bigcirc': u'\u25cb', 'oslash': u'\u2298', 'sqcap': u'\u2293', 'bullet': u'\u2219', 'cup': u'\u222a', 'cdot': u'\u22c5', 'cap': u'\u2229', 'bigtriangledown': u'\u25bd', 'times': u'\xd7', 'setminus': u'\u2216', 'circ': u'\u2218', 'vee': u'\u2228', 'uplus': u'\u228e', 'mp': u'\u2213', 'dagger': u'\u2020', 'triangleright': u'\u25b7', 'div': u'\xf7', 'pm': u'\xb1',
+    # Relation symbols:
+    'subset': u'\u2282', 'propto': u'\u221d', 'geq': u'\u2265', 'ge': u'\u2265', 'sqsubset': u'\u228f', 'Join': u'\u2a1d', 'frown': u'\u2322', 'models': u'\u22a7', 'supset': u'\u2283', 'in': u'\u2208', 'doteq': u'\u2250', 'dashv': u'\u22a3', 'gg': u'\u226b', 'leq': u'\u2264', 'succ': u'\u227b', 'vdash': u'\u22a2', 'cong': u'\u2245', 'simeq': u'\u2243', 'subseteq': u'\u2286', 'parallel': u'\u2225', 'equiv': u'\u2261', 'ni': u'\u220b', 'le': u'\u2264', 'approx': u'\u2248', 'precsim': u'\u227e', 'sqsupset': u'\u2290', 'll': u'\u226a', 'sqsupseteq': u'\u2292', 'mid': u'\u2223', 'prec': u'\u227a', 'succsim': u'\u227f', 'bowtie': u'\u22c8', 'perp': u'\u27c2', 'sqsubseteq': u'\u2291', 'asymp': u'\u224d', 'smile': u'\u2323', 'supseteq': u'\u2287', 'sim': u'\u223c', 'neq': u'\u2260',
+    # Arrow symbols:
+    'searrow': u'\u2198', 'updownarrow': u'\u2195', 'Uparrow': u'\u21d1', 'longleftrightarrow': u'\u27f7', 'Leftarrow': u'\u21d0', 'longmapsto': u'\u27fc', 'Longleftarrow': u'\u27f8', 'nearrow': u'\u2197', 'hookleftarrow': u'\u21a9', 'downarrow': u'\u2193', 'Leftrightarrow': u'\u21d4', 'longrightarrow': u'\u27f6', 'rightharpoondown': u'\u21c1', 'longleftarrow': u'\u27f5', 'rightarrow': u'\u2192', 'Updownarrow': u'\u21d5', 'rightharpoonup': u'\u21c0', 'Longleftrightarrow': u'\u27fa', 'leftarrow': u'\u2190', 'mapsto': u'\u21a6', 'nwarrow': u'\u2196', 'uparrow': u'\u2191', 'leftharpoonup': u'\u21bc', 'leftharpoondown': u'\u21bd', 'Downarrow': u'\u21d3', 'leftrightarrow': u'\u2194', 'Longrightarrow': u'\u27f9', 'swarrow': u'\u2199', 'hookrightarrow': u'\u21aa', 'Rightarrow': u'\u21d2',
+    # Miscellaneous symbols:
+    'infty': u'\u221e', 'surd': u'\u221a', 'partial': u'\u2202', 'ddots': u'\u22f1', 'exists': u'\u2203', 'flat': u'\u266d', 'diamondsuit': u'\u2662', 'wp': u'\u2118', 'spadesuit': u'\u2660', 'Re': u'\u211c', 'vdots': u'\u22ee', 'aleph': u'\u2135', 'clubsuit': u'\u2663', 'sharp': u'\u266f', 'angle': u'\u2220', 'prime': u'\u2032', 'natural': u'\u266e', 'ell': u'\u2113', 'neg': u'\xac', 'top': u'\u22a4', 'nabla': u'\u2207', 'bot': u'\u22a5', 'heartsuit': u'\u2661', 'cdots': u'\u22ef', 'Im': u'\u2111', 'forall': u'\u2200', 'imath': u'\u0131', 'hbar': u'\u210f', 'emptyset': u'\u2205',
+    # Variable-sized symbols:
+    'bigotimes': u'\u2a02', 'coprod': u'\u2210', 'int': u'\u222b', 'sum': u'\u2211', 'bigodot': u'\u2a00', 'bigcup': u'\u22c3', 'biguplus': u'\u2a04', 'bigcap': u'\u22c2', 'bigoplus': u'\u2a01', 'oint': u'\u222e', 'bigvee': u'\u22c1', 'bigwedge': u'\u22c0', 'prod': u'\u220f',
+    # Braces:
+    'langle': u'\u2329', 'rangle': u'\u232A'}
+
+sumintprod = ''.join([special[symbol] for symbol in
+                      ['sum', 'int', 'oint', 'prod']])
+
+functions = ['arccos', 'arcsin', 'arctan', 'arg', 'cos',  'cosh',
+             'cot',    'coth',   'csc',    'deg', 'det',  'dim',
+             'exp',    'gcd',    'hom',    'inf', 'ker',  'lg',
+             'lim',    'liminf', 'limsup', 'ln',  'log',  'max',
+             'min',    'Pr',     'sec',    'sin', 'sinh', 'sup',
+             'tan',    'tanh',
+             'injlim',  'varinjlim', 'varlimsup',
+             'projlim', 'varliminf', 'varprojlim']
+
+
+def parse_latex_math(string, inline=True):
+    """parse_latex_math(string [,inline]) -> MathML-tree
+
+    Returns a MathML-tree parsed from string.  inline=True is for
+    inline math and inline=False is for displayed math.
+
+    tree is the whole tree and node is the current element."""
+    
+    # Normalize white-space:
+    string = ' '.join(string.split())
+
+    if inline:
+        node = mrow()
+        tree = math(node, inline=True)
+    else:
+        node = mtd()
+        tree = math(mtable(mtr(node)), inline=False)
+
+    while len(string) > 0:
+        n = len(string)
+        c = string[0]
+        skip = 1  # number of characters consumed
+        if n > 1:
+            c2 = string[1]
+        else:
+            c2 = ''
+##        print n, string, c, c2, node.__class__.__name__
+        if c == ' ':
+            pass
+        elif c == '\\':
+            if c2 in '{}':
+                node = node.append(mo(c2))
+                skip = 2
+            elif c2 == ' ':
+                node = node.append(mspace())
+                skip = 2
+            elif c2.isalpha():
+                # We have a LaTeX-name:
+                i = 2
+                while i < n and string[i].isalpha():
+                    i += 1
+                name = string[1:i]
+                node, skip = handle_keyword(name, node, string[i:])
+                skip += i
+            elif c2 == '\\':
+                # End of a row:
+                entry = mtd()
+                row = mtr(entry)
+                node.close().close().append(row)
+                node = entry
+                skip = 2
+            else:
+                raise SyntaxError('Syntax error: "%s%s"' % (c, c2))
+        elif c.isalpha():
+            node = node.append(mi(c))
+        elif c.isdigit():
+            node = node.append(mn(c))
+        elif c in "+-/()[]|=<>,.!'":
+            node = node.append(mo(c))
+        elif c == '_':
+            child = node.delete_child()
+            if isinstance(child, msup):
+                sub = msubsup(child.children, reversed=True)
+            elif isinstance(child, mo) and child.data in sumintprod:
+                sub = munder(child)
+            else:
+                sub = msub(child)
+            node.append(sub)
+            node = sub
+        elif c == '^':
+            child = node.delete_child()
+            if isinstance(child, msub):
+                sup = msubsup(child.children)
+            elif isinstance(child, mo) and child.data in sumintprod:
+                sup = mover(child)
+            elif (isinstance(child, munder) and
+                  child.children[0].data in sumintprod):
+                sup = munderover(child.children)
+            else:
+                sup = msup(child)
+            node.append(sup)
+            node = sup
+        elif c == '{':
+            row = mrow()
+            node.append(row)
+            node = row
+        elif c == '}':
+            node = node.close()
+        elif c == '&':
+            entry = mtd()
+            node.close().append(entry)
+            node = entry
+        else:
+            raise SyntaxError('Illegal character: "%s"' % c)
+        string = string[skip:]
+    return tree
+
+
+mathbb = {'A': u'\U0001D538',
+          'B': u'\U0001D539',
+          'C': u'\u2102',
+          'D': u'\U0001D53B',
+          'E': u'\U0001D53C', 
+          'F': u'\U0001D53D',
+          'G': u'\U0001D53E',
+          'H': u'\u210D',
+          'I': u'\U0001D540',
+          'J': u'\U0001D541',
+          'K': u'\U0001D542',
+          'L': u'\U0001D543',
+          'M': u'\U0001D544',
+          'N': u'\u2115',
+          'O': u'\U0001D546',
+          'P': u'\u2119',
+          'Q': u'\u211A',
+          'R': u'\u211D',
+          'S': u'\U0001D54A',
+          'T': u'\U0001D54B',
+          'U': u'\U0001D54C',
+          'V': u'\U0001D54D',
+          'W': u'\U0001D54E',
+          'X': u'\U0001D54F',
+          'Y': u'\U0001D550',
+          'Z': u'\u2124'}
+
+negatables = {'=': u'\u2260',
+              '\in': u'\u2209',
+              '\equiv': u'\u2262'}
+
+
+def handle_keyword(name, node, string):
+    skip = 0
+    if len(string) > 0 and string[0] == ' ':
+        string = string[1:]
+        skip = 1
+    if name == 'begin':
+        if not string.startswith('{matrix}'):
+            raise SyntaxError('Expected "\begin{matrix}"!')
+        skip += 8
+        entry = mtd()
+        table = mtable(mtr(entry))
+        node.append(table)
+        node = entry
+    elif name == 'end':
+        if not string.startswith('{matrix}'):
+            raise SyntaxError('Expected "\end{matrix}"!')
+        skip += 8
+        node = node.close().close().close()
+    elif name == 'text':
+        if string[0] != '{':
+            raise SyntaxError('Expected "\text{...}"!')
+        i = string.find('}')
+        if i == -1:
+            raise SyntaxError('Expected "\text{...}"!')
+        node = node.append(mtext(string[1:i]))
+        skip += i + 1
+    elif name == 'sqrt':
+        sqrt = msqrt()
+        node.append(sqrt)
+        node = sqrt
+    elif name == 'frac':
+        frac = mfrac()
+        node.append(frac)
+        node = frac
+    elif name == 'left':
+        for par in ['(', '[', '|', '\\{', '\\langle', '.']:
+            if string.startswith(par):
+                break
+        else:
+            raise SyntaxError('Missing left-brace!')
+        fenced = mfenced(par)
+        node.append(fenced)
+        row = mrow()
+        fenced.append(row)
+        node = row
+        skip += len(par)
+    elif name == 'right':
+        for par in [')', ']', '|', '\\}', '\\rangle', '.']:
+            if string.startswith(par):
+                break
+        else:
+            raise SyntaxError('Missing right-brace!')
+        node = node.close()
+        node.closepar = par
+        node = node.close()
+        skip += len(par)
+    elif name == 'not':
+        for operator in negatables:
+            if string.startswith(operator):
+                break
+        else:
+            raise SyntaxError('Expected something to negate: "\\not ..."!')
+        node = node.append(mo(negatables[operator]))
+        skip += len(operator)
+    elif name == 'mathbf':
+        style = mstyle(nchildren=1, fontweight='bold')
+        node.append(style)
+        node = style
+    elif name == 'mathbb':
+        if string[0] != '{' or not string[1].isupper() or string[2] != '}':
+            raise SyntaxError('Expected something like "\mathbb{A}"!')
+        node = node.append(mi(mathbb[string[1]]))
+        skip += 3
+    elif name in greek:
+        node = node.append(mi(greek[name]))
+    elif name in Greek:
+        node = node.append(mo(Greek[name]))
+    elif name in special:
+        node = node.append(mo(special[name]))
+    elif name in functions:
+        node = node.append(mo(name))
+    else:
+        chr = over.get(name)
+        if chr is not None:
+            ovr = mover(mo(chr), reversed=True)
+            node.append(ovr)
+            node = ovr
+        else:
+            raise SyntaxError('Unknown LaTeX command: ' + name)
+
+    return node, skip
diff --git a/doc/mathpng.py b/doc/mathpng.py
new file mode 100644
index 0000000..288dd44
--- /dev/null
+++ b/doc/mathpng.py
@@ -0,0 +1,155 @@
+import os
+try:
+    from hashlib import md5
+except ImportError:
+    from md5 import md5
+    
+from docutils import nodes
+from docutils.writers.html4css1 import HTMLTranslator
+from sphinx.latexwriter import LaTeXTranslator
+
+# Define LaTeX math node:
+class latex_math(nodes.General, nodes.Element):
+    pass
+
+def math_role(role, rawtext, text, lineno, inliner,
+              options={}, content=[]):
+    i = rawtext.find('`')
+    latex = rawtext[i+1:-1]
+    node = latex_math(rawtext)
+    node['latex'] = latex
+    return [node], []
+
+
+try:
+    from docutils.parsers.rst import Directive
+except ImportError:
+    # Register directive the old way:
+    from docutils.parsers.rst.directives import _directives
+    def math_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+        latex = ''.join(content)
+        node = latex_math(block_text)
+        node['latex'] = latex
+        return [node]
+    math_directive.arguments = None
+    math_directive.options = {}
+    math_directive.content = 1
+    _directives['math'] = math_directive
+else:
+    class math_directive(Directive):
+        has_content = True
+        def run(self): 
+            latex = ' '.join(self.content)
+            node = latex_math(self.block_text)
+            node['latex'] = latex
+            return [node]
+    from docutils.parsers.rst import directives
+    directives.register_directive('math', math_directive)
+
+def setup(app):
+    app.add_node(latex_math)
+    app.add_role('math', math_role)
+
+    # Add visit/depart methods to HTML-Translator:
+    def visit_latex_math_html(self, node):
+        source = self.document.attributes['source']
+        self.body.append(latex2html(node, source))
+    def depart_latex_math_html(self, node):
+            pass
+    HTMLTranslator.visit_latex_math = visit_latex_math_html
+    HTMLTranslator.depart_latex_math = depart_latex_math_html
+
+    # Add visit/depart methods to LaTeX-Translator:
+    def visit_latex_math_latex(self, node):
+        inline = isinstance(node.parent, nodes.TextElement)
+        if inline:
+            self.body.append('$%s$' % node['latex'])
+        else:
+            self.body.extend(['\\begin{equation*}\\begin{split}',
+                              node['latex'],
+                              '\\end{split}\\end{equation*}'])
+            
+    def depart_latex_math_latex(self, node):
+        pass
+
+    def visit_subscript(self, node):
+            self.body.append('$_{')
+    def depart_subscript(self, node):
+            self.body.append('}$')
+    def visit_superscript(self, node):
+            self.body.append('$^{')
+    def depart_superscript(self, node):
+            self.body.append('}$')
+        
+    LaTeXTranslator.visit_latex_math = visit_latex_math_latex
+    LaTeXTranslator.depart_latex_math = depart_latex_math_latex
+    LaTeXTranslator.visit_subscript = visit_subscript
+    LaTeXTranslator.depart_subscript = depart_subscript
+    LaTeXTranslator.visit_superscript = visit_superscript
+    LaTeXTranslator.depart_superscript = depart_superscript
+
+from os.path import isfile
+# LaTeX to HTML translation stuff:
+def latex2html(node, source):
+    inline = isinstance(node.parent, nodes.TextElement)
+    latex = node['latex']
+    name = 'math-' + md5(latex).hexdigest()[-10:]
+    pngname = '_static/%s.png' % name
+    txtname = '_static/%s.txt' % name
+
+    if not isfile(pngname):
+        depth = make_png(latex, pngname, inline)
+        txtfile = open(txtname, 'w')
+        print >> txtfile, depth
+        txtfile.close()
+    else:
+        depth = int(open(txtname).read().strip())
+
+    path = source.split('/doc/')[-1].count('/') * '../' + '_static'
+
+    if inline:
+        cls = ''
+        align = 'style="vertical-align: -%dpx" ' % depth
+        term = ''
+    else:
+        cls = 'class="center" '
+        align = ''
+        term = '<br>'
+    return '<img src="%s/%s.png" %s%s />%s' % (path, name, align,
+                                                           cls, term)
+
+
+def make_png(latex, name, inline):
+    """Make png file and return the depth relative to baseline."""
+    # Unfortunately we have to store depth info
+    print latex,
+    f = open('math.tex', 'w')
+    f.write(r"""\documentclass{article}
+                \usepackage[active]{preview}
+                \usepackage{amsmath,amssymb}
+                \begin{document}
+                \begin{preview}""")
+    if inline:
+        f.write(r'$%s$' % latex)
+    else:
+        f.write(r'\[ %s \]' % latex)
+    f.write(r'\end{preview}\end{document}')
+    f.close()
+
+    os.system('latex --interaction=nonstopmode math.tex > /dev/null')
+
+    cmd = ('dvipng -bgTransparent -Ttight --noghostscript -l10 ' +
+           '--depth -D 136 -o %s math.dvi' % name)
+    dvipng = os.popen(cmd, 'r')
+    output = dvipng.read()
+    depth = int(output.split('=')[-1].strip()[:-1])
+    return depth
+
+def main():
+    latex = r'\sum_{\mu\nu} T_{\mu \nu} \rho_{\nu\mu}'
+    #latex = r'$\beta_0$'
+    make_png(latex, 'pngtestfile.png', True)
+
+if __name__ == '__main__':
+    main()
diff --git a/doc/numpy.rst b/doc/numpy.rst
new file mode 100644
index 0000000..bc99c6a
--- /dev/null
+++ b/doc/numpy.rst
@@ -0,0 +1,102 @@
+.. _numpy:
+
+Numeric arrays in Python
+========================
+
+Links to NumPy's webpage:
+
+* `Numpy and Scipy Documentation`_
+* `Numpy guide book <http://www.tramy.us/numpybook.pdf>`_
+* `Numpy example list`_
+* `Numpy functions by category`_
+
+
+.. _Numpy and Scipy Documentation: http://docs.scipy.org/doc
+.. _Numpy example list: http://www.scipy.org/Numpy_Example_List_With_Doc
+.. _Numpy functions by category:
+                        http://www.scipy.org/Numpy_Functions_by_Category
+
+
+ASE makes heavy use of an extension to Python called NumPy.  The
+NumPy module defines an :term:`ndarray` type that can hold large arrays of
+uniform multidimensional numeric data.  An array is similar to a
+``list`` or a ``tuple``, but it is a lot more powerful and efficient.
+
+XXX More examples from everyday ASE-life here ...
+
+>>> import numpy as np
+>>> a = np.zeros((3, 2))
+>>> a[:, 1] = 1.0
+>>> a[1] = 2.0
+>>> a
+array([[ 0.,  1.],
+       [ 2.,  2.],
+       [ 0.,  1.]])
+>>> a.shape
+(3, 2)
+>>> a.ndim
+2
+
+
+.. note::
+
+  When you do ``from ase import *``, you will get the ``np`` alias
+  automatically, so there is no need to do a ``import numpy as np``
+  also.  Try this:
+
+  >>> from ase import *
+  >>> dir(np)
+
+The conventions of numpy's linear algebra package:
+
+>>> import numpy as np
+>>> 
+>>> # Make a random hermitian matrix, H
+>>> H = np.random.rand(6, 6) + 1.j * np.random.rand(6, 6)
+>>> H = H + H.T.conj()
+>>> 
+>>> # Determine eigenvalues and rotation matrix
+>>> eps, U = np.linalg.eigh(H)
+>>> 
+>>> # Sort eigenvalues
+>>> sorted_indices = eps.real.argsort()
+>>> eps = eps[sorted_indices]
+>>> U = U[:, sorted_indices]
+>>> 
+>>> # Make print of numpy arrays less messy:
+>>> np.set_printoptions(precision=3, suppress=True)
+>>> 
+>>> # Check that U diagonalizes H:
+>>> print np.dot(np.dot(U.T.conj(), H), U) - np.diag(eps)
+>>> print np.allclose(np.dot(np.dot(U.T.conj(), H), U), np.diag(eps))
+>>> 
+>>> # The eigenvectors of H are the *coloumns* of U:
+>>> np.allclose(np.dot(H, U[:, 3]), eps[3] * U[:, 3])
+>>> np.allclose(np.dot(H, U), eps * U)
+
+The rules for multiplying 1D arrays with 2D arrays:
+
+* 1D arrays and treated like shape (1, N) arrays (row vectors).
+* left and right multiplications are treated identically.
+* A length `m` *row* vector can be multiplied with an `n x m`
+  matrix, producing the same result as if replaced by a matrix with
+  `n` copies of the vector as rows.
+* A length `n` *column* vector can be multiplied with an `n x m`
+  matrix, producing the same result as if replaced by a matrix with
+  `m` copies of the vector as columns.
+
+Thus, for the arrays below:
+
+>>> M = np.arange(5 * 6).reshape(5, 6) # A matrix af shape (5, 6)
+>>> v5 = np.arange(5) + 10             # A vector of length 5
+>>> v51 = v5[:, None]                  # A length 5 coulomn vector
+>>> v6 = np.arange(6) - 12             # A vector of length 6
+>>> v16 = v6[None, :]                  # A length 6 row vector
+
+The following identities hold::
+
+  v6 * M == v16 * M == M * v6 == M * v16 == M * v16.repeat(5, 0)
+  v51 * M == M * v51 == M * v51.repeat(6, 1)
+
+The exact same rules apply for adding and subtracting 1D arrays to /
+from 2D arrays.
diff --git a/doc/oldase.rst b/doc/oldase.rst
new file mode 100644
index 0000000..7276b3a
--- /dev/null
+++ b/doc/oldase.rst
@@ -0,0 +1,46 @@
+.. _ase2:
+
+===================================
+Porting old ASE-2 code to version 3
+===================================
+
+The old Numeric-Python based ASE-2 can coexist with the new
+:term:`numpy`-based ASE-3, because the two packages have different
+names: *ASE* and *ase* respectively.  Here is an example of combining both::
+ 
+  from ASE.Trajectories.NetCDFTrajectory import NetCDFTrajectory
+  traj = NetCDFTrajectory('a.nc')
+  loa = traj.GetListOfAtoms(-1)
+  # Convert to new style Atoms object:
+  from ase import *
+  a = Atoms(loa)
+
+The new ASE can actually read old NetCDF trajectory files, so this
+would be simpler::
+
+  from ase import *
+  a = read('a.nc')
+
+.. note::
+
+   Reading old NetCDF files in the new ASE, works even without having
+   the *libnetcdf* and ``Scientific.IO.NetCDF`` libraries installed.
+
+
+
+.. index:: ASE2ase.py
+
+The ASE2ase.py tool
+===================
+
+Use the :program:`ASE2ase.py` tool (source code :svn:`tools/ASE2ase.py`) to convert old scripts::
+
+  $ ASE2ase.py oldscript.py
+  $ diff -u oldscript.py.bak oldscript.py
+
+Check that the differences look OK.  The conversion tool isn't clever
+enough to get everything right, so you will have to do some conversion
+manually also.  If you have any problems doing this, then you should
+not hesitate to contact the
+``campos-devel`` mailing list (see :ref:`mailing_lists`) and ask for help.
+
diff --git a/doc/overview.rst b/doc/overview.rst
new file mode 100644
index 0000000..607c03c
--- /dev/null
+++ b/doc/overview.rst
@@ -0,0 +1,58 @@
+.. _overview:
+
+========
+Overview
+========
+
+ASE is an Atomistic Simulation Environment written in the
+Python_ programming language with the aim of setting up, stearing, and
+analyzing atomistic simulations. The ASE has been constructed with a
+number of "design goals" that make it:
+
+
+- **Easy to use**:
+
+  Setting up an atomistic total energy calculation or molecular
+  dynamics simulation with ASE is simple and straightforward.  ASE can
+  be used via a :mod:`graphical user interface <gui>`, :ref:`command
+  line tools` and the Python language.  Python scripts are
+  easy to follow (see :ref:`python_info` for a short introduction).
+  It is simple for new users to get access to all of the functionality
+  of ASE.
+
+- **Flexible**:
+
+  Since ASE is based on the Python scripting language it is possible
+  to perform very complicated simulation tasks without any code modifications.
+  For example, a sequence of calculations may be performed with
+  the use of simple "for-loop" constructions. There exist ASE modules for 
+  performing many standard simulation tasks.
+
+- **Customizable**:
+
+  The Python code in ASE is structured in modules intended for
+  different purposes. There are :mod:`calculators` for calculating
+  energies, forces and stresses, :mod:`md` and :mod:`optimize` modules
+  for controlling the motion of atoms, :mod:`constraint <constraints>`
+  objects and filters for performing :mod:`nudged-elastic-band <neb>`
+  calculations etc. The modularity of the object-oriented code make it 
+  simple to contribute new functionality to ASE.
+
+- **Pythonic**:
+
+  It fits nicely into the rest of the Python world with
+  use of the popular :term:`NumPy` package for numerical work
+  (see :ref:`numpy` for a short introduction). The
+  use of the Python language allows ASE to be used both interactively
+  as well as in scripts.
+
+- **Open to participation**:
+
+  The CAMPOS Atomic Simulation Environment is released under the GNU
+  Lesser General Public License version 2.1 or any later version.  See
+  the files :trac:`COPYING` and :trac:`COPYING.LESSER` which accompany
+  the downloaded files, or see the license at GNU's web server at
+  http://www.gnu.org/licenses/.  Everybody is invited to
+  participate in using and :ref:`developing the code <devel>`.
+
+.. _Python: http://www.python.org
diff --git a/doc/python.rst b/doc/python.rst
new file mode 100644
index 0000000..edd750d
--- /dev/null
+++ b/doc/python.rst
@@ -0,0 +1,223 @@
+.. _python_info:
+
+---------------
+What is Python?
+---------------
+
+This section will give a very brief introduction to the Python
+language.
+
+.. seealso::
+
+   * The Python_ home page.
+   * Python Recipes_.
+   * Try a `Python quick reference card`_ or a `different reference card`_.
+
+
+.. _Recipes: http://code.activestate.com/recipes/langs/python
+.. _Python quick reference card: http://www.limsi.fr/Individu/pointal/python/pqrc
+.. _different reference card: http://rgruet.free.fr/
+.. _Python: http://www.python.org
+
+
+Executing Python code
+---------------------
+
+You can execute Python code interactively by starting the interpreter
+like this::
+
+  $ python
+  Python 2.5.1 (r251:54863, Mar  7 2008, 04:10:12) 
+  [GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
+  Type "help", "copyright", "credits" or "license" for more information.
+  >>> print 'hello'
+  hello
+
+You can also put the ``print 'hello'`` line in a file (``hello.py``)
+and execute it as a Python script::
+
+  $ python hello.py
+  hello
+
+Or like this::
+
+  $ python -i hello.py
+  hello
+  >>> print 'hi!'
+  hi!
+
+Finally, you can put ``#!/usr/bin/env python`` in the first line of
+the ``hello.py`` file, make it executable (``chmod +x hello.py``) and
+execute it like any other executable.
+
+.. tip::
+
+   For interactive Python sessions, it is very convenient to have a
+   personal ``.pythonrc`` file::
+
+     import rlcompleter
+     import readline
+     readline.parse_and_bind("tab: complete")
+     from ase import *
+
+   and point the :envvar:`PYTHONSTARTUP` environment variable at it (see
+   here_ for details).
+
+
+   .. _here: http://www.python.org/doc/current/lib/module-rlcompleter.html
+
+
+.. tip::
+
+   For an even better interactive experience, use ipython_.
+
+   .. _ipython: http://ipython.scipy.org
+
+
+
+Types
+-----
+
+Python has the following predefined types:
+
+===========  =====================  ==========================
+type         description            example
+===========  =====================  ==========================
+``bool``     boolean                ``False``
+``int``       integer                ``117``
+``float``    floating point number  ``1.78``
+``complex``  complex number         ``0.5 + 2.0j``
+``str``      string                 ``'abc'``
+``tuple``    tuple                  ``(1, 'hmm', 2.0)``
+``list``     list                   ``[1, 'hmm', 2.0]``
+``dict``     dictionary             ``{'a': 7.0, 23: True}``
+``file``     file                   ``open('stuff.dat', 'w')``
+===========  =====================  ==========================
+
+A ``dict`` object is mapping from keys to values:
+
+>>> d = {'s': 0, 'p': 1}
+>>> d['d'] = 2
+>>> d
+{'p': 1, 's': 0, 'd': 2}
+>>> d['p']
+1
+
+A ``list`` object is an ordered collection of arbitrary objects:
+
+>>> l = [1, ('gg', 7), 'hmm']
+>>> l[1]
+('gg', 7)
+>>> l.append(1.2)
+>>> l[-2]
+'hmm'
+
+A ``tuple`` behaves like a ``list`` - except that it can't be modified
+inplace.  Objects of types ``list`` and ``dict`` are *mutable* - all
+the other types listed in the table are *immutable*, which means that
+once an object has been created, it can not change.
+
+.. note::
+
+   List and dictionary objects *can* change.  Variables in
+   Python are references to objects.  This is demonstrated here:
+
+   >>> a = ['q', 'w']
+   >>> b = a
+   >>> a.append('e')
+   >>> a
+   ['q', 'w', 'e']
+   >>> b
+   ['q', 'w', 'e']
+
+
+.. note::
+
+   Another very important type is the :term:`ndarray` type described
+   here: :ref:`numpy`.
+
+
+
+Loops
+-----
+
+A loop in Python can be done like this:
+
+>>> things = ['a', 7]
+>>> for x in things:
+...     print x
+...
+a
+7
+
+The ``things`` object could be any sequence.  Strings, tuples, lists,
+dictionaries, ndarrays and files are sequences.
+
+
+Functions and classes
+---------------------
+
+A function is defined like this:
+
+>>> def f(x, m=2, n=1):
+...     y =  x + n
+...     return y**m
+
+Here ``f`` is a function, ``x`` is an argument, ``m`` and ``n`` are keywords with default values ``2`` and ``1`` and ``y`` is a variable.
+
+A :term:`class` is defined like this:
+
+>>> class A:
+...     def __init__(self, b):
+...         self.c = b
+...     def m(self):
+...         return f(self.c, n=0)
+
+The ``__init__()`` function is called a :term:`constructor`.  You can think
+of a class as a template for creating user defined objects.
+
+In the class ``A`` ``__init__`` is a constructor, ``c`` is an attribute and ``m`` is a method.
+
+>>> a = A(7)
+>>> a.c
+7
+>>> a.m()
+49
+>>> g = a.m
+>>> g()
+49
+
+Here we make an :term:`instance`/object ``a`` of type ``A`` and ``g`` is a :term:`method` bound to ``a``.
+
+
+Importing modules
+-----------------
+
+If you put the definitions of the function ``f`` and the class ``C``
+in a file ``stuff.py``, then you can use that code from another piece
+of code::
+
+  from stuff import f, C
+  print f(1, 2)
+  print C(1).m(2)
+
+or::
+
+  import stuff
+  print stuff.f(1, 2)
+  print stuff.C(1).m(2)
+
+or::
+
+  import stuff as st
+  print st.f(1, 2)
+  print st.C(1).m(2)
+
+
+Python will look for ``stuff.py`` in these directories:
+
+1) current working directory
+2) directories listed in your :envvar:`PYTHONPATH`
+3) Python's own system directory (typically :file:`/usr/lib/python2.5`)
+
+and import the first one found.
diff --git a/doc/tutorials/N2.py b/doc/tutorials/N2.py
new file mode 100644
index 0000000..6e93a17
--- /dev/null
+++ b/doc/tutorials/N2.py
@@ -0,0 +1,16 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+
+atom = Atoms('N', calculator=EMT())
+e_atom = atom.get_potential_energy()
+
+d = 1.1
+molecule = Atoms('2N', [(0., 0., 0.), (0., 0., d)])
+molecule.set_calculator(EMT())
+e_molecule = molecule.get_potential_energy()
+
+e_atomization = e_molecule - 2 * e_atom
+
+print 'Nitrogen atom energy: %5.2f eV' %e_atom
+print 'Nitrogen molecule energy: %5.2f eV' %e_molecule
+print 'Atomization energy: %5.2f eV' %-e_atomization
diff --git a/doc/tutorials/N2Cu.py b/doc/tutorials/N2Cu.py
new file mode 100644
index 0000000..09b3ccd
--- /dev/null
+++ b/doc/tutorials/N2Cu.py
@@ -0,0 +1,28 @@
+from ase import Atoms
+from ase.visualize import view
+from ase.calculators.emt import EMT
+from ase.constraints import FixAtoms
+from ase.optimize import QuasiNewton
+from ase.lattice.surface import fcc111,add_adsorbate
+
+h = 1.85
+d = 1.10
+
+slab = fcc111('Cu', size=(4,4,2), vacuum=10.0)
+
+slab.set_calculator(EMT())
+e_slab = slab.get_potential_energy()
+
+molecule = Atoms('2N', positions=[(0., 0., 0.), (0., 0., d)])
+molecule.set_calculator(EMT())
+e_N2 = molecule.get_potential_energy()
+
+add_adsorbate(slab, molecule, h, 'ontop')
+constraint = FixAtoms(mask=[a.symbol != 'N' for a in slab])
+slab.set_constraint(constraint)
+dyn = QuasiNewton(slab, trajectory='N2Cu.traj')
+dyn.run(fmax=0.05)
+
+print 'Adsorption energy:', e_slab + e_N2 - slab.get_potential_energy()
+
+#view(slab)
diff --git a/doc/tutorials/aneb.rst b/doc/tutorials/aneb.rst
new file mode 100644
index 0000000..f27ace8
--- /dev/null
+++ b/doc/tutorials/aneb.rst
@@ -0,0 +1,5 @@
+=======================
+Dissociation using ANEB
+=======================
+
+.. literalinclude:: ../../tutorials/N2Ru-Dissociation2-ANEB.py
\ No newline at end of file
diff --git a/doc/tutorials/association.rst b/doc/tutorials/association.rst
new file mode 100644
index 0000000..4d59ae8
--- /dev/null
+++ b/doc/tutorials/association.rst
@@ -0,0 +1,5 @@
+===========
+Association
+===========
+
+.. literalinclude:: ../../tutorials/N2Ru-Association.py
\ No newline at end of file
diff --git a/doc/tutorials/atomization.rst b/doc/tutorials/atomization.rst
new file mode 100644
index 0000000..aef8440
--- /dev/null
+++ b/doc/tutorials/atomization.rst
@@ -0,0 +1,25 @@
+==================
+Atomization energy
+==================
+
+The following script will calculate the atomization energy of a
+nitrogen molecule:
+
+ .. literalinclude:: N2.py
+
+First, an ``Atoms`` object containing one nitrogen is created and a
+fast EMT calculator is attached to it simply as an argument. The total
+energy for the isolated atom is then calculated and stored in the
+``e_atom`` variable.
+
+The ``molecule`` object is defined, holding the nitrogen molecule at
+the experimental bond length. The EMT calculator is then attached to
+the molecule and the total energy is extracted into the ``e_molecule``
+variable.
+
+Running the script will produce the output::
+
+  Nitrogen atom energy:  4.97 eV
+  Nitrogen molecule energy:  0.04 eV
+  Atomization energy:  9.90 eV
+
diff --git a/doc/tutorials/constraints/diffusion.py b/doc/tutorials/constraints/diffusion.py
new file mode 100644
index 0000000..4168c14
--- /dev/null
+++ b/doc/tutorials/constraints/diffusion.py
@@ -0,0 +1,12 @@
+# creates:  diffusion-path.png
+import os
+from ase.io import read, write
+if 1:
+    execfile('diffusion4.py')
+images = [read('mep%d.traj' % i) for i in range(5)]
+a = images[0] + images[1] + images[2] + images[3] + images[4]
+del a.constraints
+a *= (2, 1, 1)
+a.set_cell(images[0].get_cell())
+write('diffusion-path.pov', a, show_unit_cell=2, rotation='-90x',
+      transparent=False, display=False, run_povray=True)
diff --git a/doc/tutorials/constraints/diffusion.rst b/doc/tutorials/constraints/diffusion.rst
new file mode 100644
index 0000000..ec6281e
--- /dev/null
+++ b/doc/tutorials/constraints/diffusion.rst
@@ -0,0 +1,29 @@
+.. _constraints_diffusion_tutorial:
+
+======================================================
+Diffusion of gold atom on Al(100) surface (constraint)
+======================================================
+
+In this tutorial, we will calculate the energy barrier that was found
+using the :mod:`NEB <neb>` method in the :ref:`diffusion_tutorial`
+tutorial.  Here, we use a siple :class:`~ase.constraints.FixedPlane`
+constraint that forces the Au atom to relax in the *yz*-plane only:
+
+.. literalinclude:: diffusion4.py
+
+The result can be analysed with the command :command:`ag mep?.traj -n
+-1` (choose :menuselection:`Tools --> NEB`).  The barrier is found to
+be 0.35 eV - exactly as in the :ref:`NEB <diffusion_tutorial>`
+tutorial.
+
+Here is a side-view of the path (unit cell repeated twice):
+
+.. image:: diffusion-path.png
+
+
+.. seealso::
+
+   * :mod:`neb`
+   * :mod:`constraints`
+   * :ref:`diffusion_tutorial`
+   * :func:`~lattice.surface.fcc100`
diff --git a/doc/tutorials/constraints/diffusion4.py b/doc/tutorials/constraints/diffusion4.py
new file mode 100644
index 0000000..55ce924
--- /dev/null
+++ b/doc/tutorials/constraints/diffusion4.py
@@ -0,0 +1,32 @@
+from ase.lattice.surface import fcc100, add_adsorbate
+from ase.constraints import FixAtoms, FixedPlane
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+
+# 2x2-Al(001) surface with 3 layers and an
+# Au atom adsorbed in a hollow site:
+slab = fcc100('Al', size=(2, 2, 3))
+add_adsorbate(slab, 'Au', 1.7, 'hollow')
+slab.center(axis=2, vacuum=4.0)
+
+# Make sure the structure is correct:
+#view(slab)
+
+# Fix second and third layers:
+mask = [atom.tag > 1 for atom in slab]
+#print mask
+fixlayers = FixAtoms(mask=mask)
+
+# Constrain the last atom (Au atom) to move only in the yz-plane:
+plane = FixedPlane(-1, (1, 0, 0))
+
+slab.set_constraint([fixlayers, plane])
+
+# Use EMT potential:
+slab.set_calculator(EMT())
+
+# Initial state:
+for i in range(5):
+    qn = QuasiNewton(slab, trajectory='mep%d.traj' % i)
+    qn.run(fmax=0.05)
+    slab[-1].x += slab.get_cell()[0, 0] / 8
diff --git a/doc/tutorials/dissociation.rst b/doc/tutorials/dissociation.rst
new file mode 100644
index 0000000..d0b72b9
--- /dev/null
+++ b/doc/tutorials/dissociation.rst
@@ -0,0 +1,13 @@
+.. _neb1:
+.. _neb2:
+.. _mep2:
+
+============
+Dissociation
+============
+
+
+.. literalinclude:: ../../tutorials/N2Ru-Dissociation1.py
+
+
+.. literalinclude:: ../../tutorials/N2Ru-Dissociation2.py
\ No newline at end of file
diff --git a/doc/tutorials/eos/eos.py b/doc/tutorials/eos/eos.py
new file mode 100644
index 0000000..97b5922
--- /dev/null
+++ b/doc/tutorials/eos/eos.py
@@ -0,0 +1,5 @@
+# creates:  Ag-eos.png
+execfile('eos1.py')
+import matplotlib
+matplotlib.use('Agg')
+execfile('eos2.py')
diff --git a/doc/tutorials/eos/eos.rst b/doc/tutorials/eos/eos.rst
new file mode 100644
index 0000000..55ee970
--- /dev/null
+++ b/doc/tutorials/eos/eos.rst
@@ -0,0 +1,30 @@
+.. _eos:
+
+=================
+Equation of state
+=================
+
+First, do a bulk calculation for different lattice constants:
+
+.. literalinclude:: eos1.py
+
+This will write a trajectory file containing five configurations of
+FCC silver for five different lattice constans.  Now, analyse the
+result with the :class:`~ase.utils.eos.EquationOfState` class and this
+script:
+
+.. literalinclude:: eos2.py
+
+|eos|
+
+A quicker way to do this analysis, is to use the :mod:`gui` tool:
+
+.. highlight:: bash
+
+::
+
+  $ ag Ag.traj
+
+And then choose :menuselection:`Tools --> Bulk modulus`.
+
+.. |eos| image:: Ag-eos.png
diff --git a/doc/tutorials/eos/eos1.py b/doc/tutorials/eos/eos1.py
new file mode 100644
index 0000000..68ca31c
--- /dev/null
+++ b/doc/tutorials/eos/eos1.py
@@ -0,0 +1,17 @@
+import numpy as np
+
+from ase import Atoms
+from ase.io.trajectory import PickleTrajectory
+from ase.calculators.emt import EMT
+
+a = 4.0  # approximate lattice constant
+b = a / 2
+ag = Atoms('Ag', 
+           cell=[(0,b,b), (b,0,b), (b,b,0)],
+           pbc=1,
+           calculator=EMT())  # use EMT potential
+cell = ag.get_cell()
+traj = PickleTrajectory('Ag.traj', 'w')
+for x in np.linspace(0.95, 1.05, 5):
+    ag.set_cell(cell * x, scale_atoms=True)
+    traj.write(ag)
diff --git a/doc/tutorials/eos/eos2.py b/doc/tutorials/eos/eos2.py
new file mode 100644
index 0000000..6071a79
--- /dev/null
+++ b/doc/tutorials/eos/eos2.py
@@ -0,0 +1,11 @@
+from ase.io import read 
+from ase.units import kJ
+from ase.utils.eos import EquationOfState
+configs = read('Ag.traj at 0:5')  # read 5 configurations
+# Extract volumes and energies:
+volumes = [ag.get_volume() for ag in configs]
+energies = [ag.get_potential_energy() for ag in configs]
+eos = EquationOfState(volumes, energies)
+v0, e0, B = eos.fit()
+print B / kJ * 1.0e24, 'GPa'
+eos.plot('Ag-eos.png')
diff --git a/doc/tutorials/lattice_constant.rst b/doc/tutorials/lattice_constant.rst
new file mode 100644
index 0000000..832eec6
--- /dev/null
+++ b/doc/tutorials/lattice_constant.rst
@@ -0,0 +1,108 @@
+.. _lattice_constant:
+
+=========================
+Finding lattice constants
+=========================
+
+.. seealso::
+
+   :ref:`eos`.
+
+
+HCP
+===
+
+Let's try to find the `a` and `c` lattice constants for HCP nickel
+using the :mod:`EMT <emt>` potential.  
+
+First, we make a good intial guess for `a` and `c` using the FCC nearest
+neighbor distance and the ideal `c/a` ratio::
+
+  from numpy import sqrt
+  a0 = 3.52 / sqrt(2)
+  c0 = sqrt(8 / 3.0) * a0
+
+and create a trajectory for the results::
+
+  from ase.io import PickleTrajectory
+  traj = PickleTrajectory('Ni.traj', 'w')
+
+Finally, we do the 12 calculations (four values for `a` and three for `c`)::
+
+  import numpy as np
+  from ase.structure import bulk
+  from ase.calculators import EMT
+  eps = 0.01
+  for a in a0 * np.linspace(1 - eps, 1 + eps, 4):
+      for c in c0 * np.linspace(1 - eps, 1 + eps, 3):
+          ni = bulk('Ni', 'hcp', a=a, covera=c / a)
+          ni.set_calculator(EMT())
+          ni.get_potential_energy()
+          traj.write(ni)
+
+
+Analysis
+--------
+
+Now, we need to extract the data from the trajectory.  Try this:
+
+>>> from ase.structure import bulk
+>>> ni = bulk('Ni', 'hcp', a=2.5, covera=4.0 / 2.5)
+>>> ni.cell
+array([[ 2.5       ,  0.        ,  0.        ],
+       [ 1.25      ,  2.16506351,  0.        ],
+       [ 0.        ,  0.        ,  4.        ]])
+
+So, we can get `a` and `c` from ``ni.cell[0, 0]`` and ``ni.cell[2,
+2]``:
+
+>>> from ase.io import read
+>>> configs = read('Ni.traj@:')
+>>> energies = [config.get_potential_energy() for config in configs]
+>>> ac = [(config.cell[0, 0], config.cell[2, 2]) for config in configs]
+
+We fit the energy to this expression:
+
+.. math:: c_0 + c_1 a + c_2 c + c_3 a^2 + c_4 ac + c_5 c^2 +
+          c_6 a^3 + c_7 a^2c + c_8 ac^2 + c_9 c^3
+
+>>> from ase.optimize import polyfit
+>>> p = polyfit(ac, energies)
+
+using the function:
+
+.. autofunction:: ase.optimize.polyfit
+
+The minimum can be found using SciPy's fmin_bfgs_
+function:
+
+>>> from scipy.optimize import fmin_bfgs
+>>> a0 = 3.52 / sqrt(2)
+>>> c0 = sqrt(8 / 3.0) * a0
+>>> a, c = fmin_bfgs(p, (a0, c0))
+Warning: Desired error not necessarily achieveddue to precision loss
+         Current function value: 0.010030
+         Iterations: 7
+         Function evaluations: 425
+         Gradient evaluations: 106
+>>> print a, c
+2.46888950503 4.02027198125
+
+In (often) cases optimization fails, one may use
+another energy expression:
+
+.. math:: c_0 + c_1 a + c_2 c + c_3 a^2 + c_4 c^2
+
+>>> import numpy as np
+>>> sle = np.linalg.solve
+>>> E = np.array(energies)
+>>> A = np.array([(1, x, y, x**2, y**2)
+>>>               for x, y in ac]).T
+>>> C = sle(np.inner(A, A), np.dot(A, E))
+
+>>> a = - C[1] / (2 * C[3])
+>>> c = - C[2] / (2 * C[4])
+>>> print a, c
+2.46465936501 4.00438337976
+
+.. _fmin_bfgs: http://docs.scipy.org/doc/scipy/reference/optimize.html
diff --git a/doc/tutorials/manipulating_atoms.py b/doc/tutorials/manipulating_atoms.py
new file mode 100644
index 0000000..e65c997
--- /dev/null
+++ b/doc/tutorials/manipulating_atoms.py
@@ -0,0 +1,31 @@
+# creates: a1.png a2.png a3.png
+
+import numpy as np
+
+from ase import Atom,Atoms
+from ase.io import write
+
+atoms = Atoms('Ni4', positions= [(0, 0, 0),
+                                 (0.45, 0, 0),
+                                 (0, 0.5, 0),
+                                 (0.5, 0.5, 0),])
+atoms[1].x = 0.5
+a = 3.55
+cell = [(2 / np.sqrt(2.0) * a, 0, 0),
+        (1 / np.sqrt(2.0) * a, np.sqrt(3.0 / 2.0) * a, 0),
+        (0, 0, 10 * np.sqrt(3.0) / 3.0 * a)]
+atoms.set_cell(cell, scale_atoms=True)
+write('a1.png', atoms, rotation='-73x', show_unit_cell=2)
+
+a = atoms.repeat((3, 3, 2))
+a.set_cell(atoms.get_cell())
+write('a2.png', a, rotation='-73x', show_unit_cell=True)
+
+xyzcell = np.identity(3) # The 3x3 unit matrix
+atoms.set_cell(xyzcell, scale_atoms=True)  # Set the unit cell and rescale
+atoms.append(Atom('Ni', (1/6., 1/6., .1)))  
+atoms.set_cell(cell, scale_atoms=True)  # Set the unit cell and scale back
+a = atoms.repeat((3, 3, 1))
+a.set_cell(atoms.get_cell())
+write('a3.png', a, show_unit_cell=True)
+
diff --git a/doc/tutorials/manipulating_atoms.rst b/doc/tutorials/manipulating_atoms.rst
new file mode 100644
index 0000000..be02bb5
--- /dev/null
+++ b/doc/tutorials/manipulating_atoms.rst
@@ -0,0 +1,112 @@
+.. _atommanip:
+
+Manipulating atoms
+------------------
+**XXX rewrite this:  use fcc111() function and do some relaxation ...**
+
+We will set up a one layer slab of Ni atoms with one Ag adatom.
+
+Define the slab atoms:
+
+>>> from ase import Atoms
+>>> atoms = Atoms('Ni4', [(0, 0, 0),
+...                       (0.45, 0, 0),
+...                       (0, 0.5, 0),
+...                       (0.5, 0.5, 0)])
+
+Have a look at the individual atoms:
+
+>>> atoms[0]
+Atom('Ni', [0.0, 0.0, 0.0], atoms=..., index=0)
+>>> atoms[1]
+Atom('Ni', [0.45, 0.0, 0.0], atoms=..., index=1)
+>>> atoms[2]
+Atom('Ni', [0.0, 0.5, 0.0], atoms=..., index=2)
+>>> atoms[3]
+Atom('Ni', [0.5, 0.5, 0.0], atoms=..., index=3)
+
+Let us assume we forgot how many atoms we set up:
+
+>>> atoms[4]
+Traceback (most recent call last):
+File "<stdin>", line 1, in ?
+IndexError: list index out of range
+
+Wrong because we only have four atoms
+
+>>> len(atoms)
+4
+
+Change the position of the 2nd atom in the list
+
+>>> atoms[1].x = 0.5
+>>> atoms.get_positions()
+array([[ 0. ,  0. ,  0. ],
+       [ 0.5,  0. ,  0. ],
+       [ 0. ,  0.5,  0. ],
+       [ 0.5,  0.5,  0. ]])
+
+What is the unit cell so far?
+
+>>> atoms.get_cell()
+array([[ 1.,  0.,  0.],
+       [ 0.,  1.,  0.],
+       [ 0.,  0.,  1.]])
+
+Now, setup a p(2x2) cell in a hexagonal surface.
+Here, *a* is the fcc lattice constant, the cell is 10 layers high:
+
+>>> from numpy import sqrt
+>>> a = 3.55
+>>> cell = [(2/sqrt(2.)*a, 0, 0),
+...         (1/sqrt(2.)*a, sqrt(3./2.)*a, 0),
+...         (0, 0, 10*sqrt(3.)/3.*a)]
+>>> cell
+[(5.0204581464244864, 0, 0),
+(2.5102290732122432, 4.3478442934401409, 0),
+(0, 0, 20.495934556231713)]
+>>> atoms.set_cell(cell, scale_atoms=True)
+
+The argument *scale_atoms=True* indicates that the atomic positions should be
+scaled with the unit cell. The default is *scale_atoms=False* indicating that
+the cartesian coordinates remain the same when the cell is changed.
+
+>>> atoms.get_positions()
+array([[ 0.        ,  0.        ,  0.        ],
+       [ 2.51022907,  0.        ,  0.        ],
+       [ 1.25511454,  2.17392215,  0.        ],
+       [ 3.76534361,  2.17392215,  0.        ]])
+
+Plot the whole system by bringing up the :mod:`gui`:
+
+>>> from ase.visualize import view
+>>> view(atoms)
+
+.. image:: a1.png
+   :scale: 35
+
+Within the viewer (called :mod:`ag <gui>` or :mod:`ase.gui <gui>`) it
+is possible to repeat the unit cell in all three directions (using the
+:menuselection:`Repeat --> View` window).
+
+.. image:: a2.png
+   :scale: 35
+
+We now add an adatom.  Since the supercell is now declared as the unit
+cell for our atoms we can either add the atom using its cartesian
+coordinates in Angstrom or rescale the unit cell and use scaled
+coordinates. We try the latter:
+
+>>> from numpy import identity
+>>> from ase import Atom
+>>> xyzcell = identity(3) # The 3x3 unit matrix
+>>> atoms.set_cell(xyzcell, scale_atoms=True)  # Set the unit cell and rescale
+>>> atoms.append(Atom('Ni', (1/6., 1/6., .1)))  
+>>> atoms.set_cell(cell, scale_atoms=True)  # Set the unit cell and scale back
+
+The structure now looks like this:
+
+>>> view(atoms)
+
+.. image:: a3.png
+   :scale: 35
diff --git a/doc/tutorials/md/md.rst b/doc/tutorials/md/md.rst
new file mode 100644
index 0000000..da27a29
--- /dev/null
+++ b/doc/tutorials/md/md.rst
@@ -0,0 +1,96 @@
+.. _md_tutorial:
+
+==================
+Molecular dynamics
+==================
+
+.. note::
+
+  These examples *can* be used without Asap installed, then
+  the ase.EMT calculator (implemented in Python) is used, but nearly
+  superhuman patience is required.
+
+Here we demonstrate now simple molecular dynamics is performed.  A
+crystal is set up, the atoms are given momenta corresponding to a
+temperature of 300K, then Newtons second law is integrated numerically
+with a time step of 5 fs (a good choice for copper).
+
+.. literalinclude:: moldyn1.py
+
+Note how the total energy is conserved, but the kinetic energy quickly
+drops to half the expected value.  Why?
+
+
+Instead of printing within a loop, it is possible to use an "observer"
+to observe the atoms and do the printing (or more sophisticated
+analysis).
+
+.. literalinclude:: moldyn2.py
+
+Constant temperature MD
+=======================
+
+Often, you want to control the temperature of an MD simulation.  This
+can be done with the Langevin dynamics module.  In the previous
+examples, replace the line `dyn = VelocityVerlet(...)` with::
+
+  dyn = Langevin(atoms, 5*units.fs, T*units.kB, 0.002)
+
+where T is the desired temperature in Kelvin.  You also need to import
+Langevin, see the class below.
+
+The Langevin dynamics will then slowly adjust the total energy of the
+system so the temperature approaches the desired one.
+
+As a slightly less boring example, let us use this to melt a chunck of
+copper by starting the simulation without any momentum of the atoms
+(no kinetic energy), and with a desired temperature above the melting
+point.  We will also save information about the atoms in a trajectory
+file called moldyn3.traj.  
+
+.. literalinclude:: moldyn3.py
+
+After running the simulation, you can study the result with the
+command
+
+::
+
+  ag moldyn3.traj
+
+Try plotting the kinetic energy.  You will *not* see a well-defined
+melting point due to finite size effects (including surface melting),
+but you will probably see an almost flat region where the inside of
+the system melts.  The outermost layers melt at a lower temperature.
+
+.. note::
+
+  The Langevin dynamics will by default keep the position and momentum
+  of the center of mass unperturbed. This is another improvement over
+  just setting momenta corresponding to a temperature, as we did before.
+
+
+Isolated particle MD
+====================
+
+When simulating isolated particles with MD, it is sometimes preferable
+to set random momenta corresponding to a spefic temperature and let the
+system evolve freely. With a relatively high temperature, the is however
+a risk that the collection of atoms will drift out of the simulation box
+because the randomized momenta gave the center of mass a small but
+non-zero velocity too.
+
+Let us see what happens when we propagate a nanoparticle for a long time:
+
+.. literalinclude:: moldyn4.py
+
+After running the simulation, use :program:`ag` to compare the results
+with how it looks if you comment out either the line that says
+:epydoc:`ase.md.velocitydistribution.Stationary` (atoms),
+:epydoc:`ase.md.velocitydistribution.ZeroRotation` (atoms) or both.
+
+::
+
+  ag moldyn4.traj
+
+Try playing the movie with a high frame rate and set frame skipping to a
+low number. Can you spot the subtle difference?
diff --git a/doc/tutorials/md/moldyn1.py b/doc/tutorials/md/moldyn1.py
new file mode 100644
index 0000000..c448d35
--- /dev/null
+++ b/doc/tutorials/md/moldyn1.py
@@ -0,0 +1,42 @@
+"Demonstrates molecular dynamics with constant energy."
+
+from ase.calculators.emt import EMT
+from ase.lattice.cubic import FaceCenteredCubic
+from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
+from ase.md.verlet import VelocityVerlet
+from ase import units
+
+# Use Asap for a huge performance increase if it is installed
+useAsap = False
+
+if useAsap:
+    from asap3 import EMT
+    size = 10
+else:
+    size = 3
+    
+# Set up a crystal
+atoms = FaceCenteredCubic(directions=[[1,0,0],[0,1,0],[0,0,1]], symbol="Cu",
+                          size=(size,size,size), pbc=True)
+
+# Describe the interatomic interactions with the Effective Medium Theory
+atoms.set_calculator(EMT())
+
+# Set the momenta corresponding to T=300K
+MaxwellBoltzmannDistribution(atoms, 300*units.kB)
+
+# We want to run MD with constant energy using the VelocityVerlet algorithm.
+dyn = VelocityVerlet(atoms, 5*units.fs)  # 5 fs time step.
+
+#Function to print the potential, kinetic and total energy
+def printenergy(a):
+    epot = a.get_potential_energy() / len(a)
+    ekin = a.get_kinetic_energy() / len(a)
+    print ("Energy per atom: Epot = %.3feV  Ekin = %.3feV (T=%3.0fK)  Etot = %.3feV" %
+           (epot, ekin, ekin/(1.5*units.kB), epot+ekin))
+
+# Now run the dynamics
+printenergy(atoms)
+for i in range(20):
+    dyn.run(10)
+    printenergy(atoms)
diff --git a/doc/tutorials/md/moldyn2.py b/doc/tutorials/md/moldyn2.py
new file mode 100644
index 0000000..7cc0a28
--- /dev/null
+++ b/doc/tutorials/md/moldyn2.py
@@ -0,0 +1,42 @@
+"Demonstrates molecular dynamics with constant energy."
+
+from ase.calculators.emt import EMT
+from ase.lattice.cubic import FaceCenteredCubic
+from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
+from ase.md.verlet import VelocityVerlet
+from ase import units
+
+# Use Asap for a huge performance increase if it is installed
+useAsap = True
+
+if useAsap:
+    from asap3 import EMT
+    size = 10
+else:
+    size = 3
+    
+# Set up a crystal
+atoms = FaceCenteredCubic(directions=[[1,0,0],[0,1,0],[0,0,1]], symbol="Cu",
+                          size=(size,size,size), pbc=True)
+
+# Describe the interatomic interactions with the Effective Medium Theory
+atoms.set_calculator(EMT())
+
+# Set the momenta corresponding to T=300K
+MaxwellBoltzmannDistribution(atoms, 300*units.kB)
+
+# We want to run MD with constant energy using the VelocityVerlet algorithm.
+dyn = VelocityVerlet(atoms, 5*units.fs)  # 5 fs time step.
+
+#Function to print the potential, kinetic and total energy.
+def printenergy(a=atoms):    #store a reference to atoms in the definition.
+    epot = a.get_potential_energy() / len(a)
+    ekin = a.get_kinetic_energy() / len(a)
+    print ("Energy per atom: Epot = %.3feV  Ekin = %.3feV (T=%3.0fK)  Etot = %.3feV" %
+           (epot, ekin, ekin/(1.5*units.kB), epot+ekin))
+
+# Now run the dynamics
+dyn.attach(printenergy, interval=10)
+printenergy()
+dyn.run(200)
+
diff --git a/doc/tutorials/md/moldyn3.py b/doc/tutorials/md/moldyn3.py
new file mode 100644
index 0000000..e9e0de0
--- /dev/null
+++ b/doc/tutorials/md/moldyn3.py
@@ -0,0 +1,41 @@
+"Demonstrates molecular dynamics with constant energy."
+
+from ase.calculators.emt import EMT
+from ase.lattice.cubic import FaceCenteredCubic
+from ase.md.langevin import Langevin
+from ase.io.trajectory import PickleTrajectory
+from ase import units
+
+from asap3 import EMT   # Way too slow with ase.EMT !
+size = 10
+
+T = 1500 # Kelvin
+
+# Set up a crystal
+atoms = FaceCenteredCubic(directions=[[1,0,0],[0,1,0],[0,0,1]], symbol="Cu",
+                          size=(size,size,size), pbc=False)
+
+# Describe the interatomic interactions with the Effective Medium Theory
+atoms.set_calculator(EMT())
+
+# We want to run MD with constant energy using the Langevin algorithm
+# with a time step of 5 fs, the temperature T and the friction
+# coefficient to 0.02 atomic units.
+dyn = Langevin(atoms, 5*units.fs, T*units.kB, 0.002)
+
+#Function to print the potential, kinetic and total energy.
+def printenergy(a=atoms):    #store a reference to atoms in the definition.
+    epot = a.get_potential_energy() / len(a)
+    ekin = a.get_kinetic_energy() / len(a)
+    print ("Energy per atom: Epot = %.3feV  Ekin = %.3feV (T=%3.0fK)  Etot = %.3feV" %
+           (epot, ekin, ekin/(1.5*units.kB), epot+ekin))
+dyn.attach(printenergy, interval=50)
+
+#We also want to save the positions of all atoms after every 100th time step.
+traj = PickleTrajectory("moldyn3.traj", 'w', atoms)
+dyn.attach(traj.write, interval=50)
+
+# Now run the dynamics
+printenergy()
+dyn.run(5000)
+
diff --git a/doc/tutorials/md/moldyn4.py b/doc/tutorials/md/moldyn4.py
new file mode 100644
index 0000000..f2252fc
--- /dev/null
+++ b/doc/tutorials/md/moldyn4.py
@@ -0,0 +1,49 @@
+"Demonstrates molecular dynamics for isolated particles."
+
+from ase.calculators.emt import EMT
+from ase.cluster.cubic import FaceCenteredCubic
+from ase.optimize import QuasiNewton
+from ase.md.velocitydistribution import MaxwellBoltzmannDistribution, \
+                                        Stationary, ZeroRotation
+from ase.md.verlet import VelocityVerlet
+from ase import units
+
+# Use Asap for a huge performance increase if it is installed
+useAsap = True
+
+if useAsap:
+    from asap3 import EMT
+    size = 4
+else:
+    size = 2
+
+# Set up a nanoparticle
+atoms = FaceCenteredCubic('Cu', surfaces=[[1,0,0],[1,1,0],[1,1,1]],
+                          layers=(size,size,size), vacuum=4)
+
+# Describe the interatomic interactions with the Effective Medium Theory
+atoms.set_calculator(EMT())
+
+# Do a quick relaxation of the cluster
+qn = QuasiNewton(atoms)
+qn.run(0.001, 10)
+
+# Set the momenta corresponding to T=1200K
+MaxwellBoltzmannDistribution(atoms, 1200*units.kB)
+Stationary(atoms) # zero linear momentum
+ZeroRotation(atoms) # zero angular momentum
+
+# We want to run MD using the VelocityVerlet algorithm.
+dyn = VelocityVerlet(atoms, 5*units.fs, trajectory='moldyn4.traj') # save trajectory.
+
+#Function to print the potential, kinetic and total energy.
+def printenergy(a=atoms):    #store a reference to atoms in the definition.
+    epot = a.get_potential_energy() / len(a)
+    ekin = a.get_kinetic_energy() / len(a)
+    print ("Energy per atom: Epot = %.3feV  Ekin = %.3feV (T=%3.0fK)  Etot = %.3feV" %
+           (epot, ekin, ekin/(1.5*units.kB), epot+ekin))
+dyn.attach(printenergy, interval=10)
+
+# Now run the dynamics
+printenergy()
+dyn.run(2000)
diff --git a/doc/tutorials/neb/diffusion.py b/doc/tutorials/neb/diffusion.py
new file mode 100644
index 0000000..b1e8963
--- /dev/null
+++ b/doc/tutorials/neb/diffusion.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# creates:  diffusion-I.png  diffusion-T.png  diffusion-F.png diffusion-barrier.png
+import os
+import matplotlib
+matplotlib.use('Agg')
+import pylab as plt
+from ase.io import read, write
+from ase.neb import fit
+if 1:
+    execfile('diffusion1.py')
+    execfile('diffusion2.py')
+images = read('neb.traj at -5:')
+for name, a in zip('ITF', images[::2]):
+    cell = a.get_cell()
+    del a.constraints
+    a = a * (2, 2, 1)
+    a.set_cell(cell)
+    write('diffusion-%s.pov' % name, a, show_unit_cell=True,
+          transparent=False, display=False, run_povray=True)
+
+s, E, Sfit, Efit, lines = fit(images)
+assert abs(max(Efit) - 0.374) < 1e-3
+plt.figure(figsize=(4.5, 3))
+plt.plot(s, E, 'o')
+plt.plot(Sfit, Efit, 'k-')
+for x, y in lines:
+    plt.plot(x, y, 'g-')
+plt.xlabel(u'path [Å]')
+plt.ylabel(u'energy [eV]')
+plt.title('Maximum: %.3f eV' % max(Efit))
+plt.subplots_adjust(left=0.19, bottom=0.15)
+plt.savefig('diffusion-barrier.png')
diff --git a/doc/tutorials/neb/diffusion.rst b/doc/tutorials/neb/diffusion.rst
new file mode 100644
index 0000000..f98977c
--- /dev/null
+++ b/doc/tutorials/neb/diffusion.rst
@@ -0,0 +1,52 @@
+.. _diffusion_tutorial:
+
+===============================================
+Diffusion of gold atom on Al(100) surface (NEB)
+===============================================
+
+First, set up the initial and final states:
+
+|initial| |final|
+
+.. literalinclude:: diffusion1.py
+
+.. note::  Notice how the tags are used to select the constrained atoms
+
+Now, do the NEB calculation:
+
+.. literalinclude:: diffusion2.py
+
+|ts| |barrier|
+
+.. note::
+
+   For this reaction, the reaction coordinate is very simple: The
+   *x*-coordinate of the Au atom.  In such cases, the NEB method is
+   overkill, and a simple constraint method should be used like in this
+   tutorial: :ref:`constraints_diffusion_tutorial`.
+
+.. seealso::
+
+   * :mod:`neb`
+   * :mod:`constraints`
+   * :ref:`constraints_diffusion_tutorial`
+   * :func:`~lattice.surface.fcc100`
+   
+
+
+.. |initial| image:: diffusion-I.png
+.. |final| image:: diffusion-F.png
+.. |ts| image:: diffusion-T.png
+.. |barrier| image:: diffusion-barrier.png
+
+
+Parallelizing over images
+=========================
+
+Instead of having one process do the calculations for all three
+internal images in turn, it will be faster to have three processes do
+one image each.  This can be done like this:
+
+.. literalinclude:: diffusion3.py
+
+
diff --git a/doc/tutorials/neb/diffusion1.py b/doc/tutorials/neb/diffusion1.py
new file mode 100644
index 0000000..e0623d6
--- /dev/null
+++ b/doc/tutorials/neb/diffusion1.py
@@ -0,0 +1,30 @@
+from ase.lattice.surface import fcc100, add_adsorbate
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+
+# 2x2-Al(001) surface with 3 layers and an
+# Au atom adsorbed in a hollow site:
+slab = fcc100('Al', size=(2, 2, 3))
+add_adsorbate(slab, 'Au', 1.7, 'hollow')
+slab.center(axis=2, vacuum=4.0)
+
+# Make sure the structure is correct:
+#view(slab)
+
+# Fix second and third layers:
+mask = [atom.tag > 1 for atom in slab]
+#print mask
+slab.set_constraint(FixAtoms(mask=mask))
+
+# Use EMT potential:
+slab.set_calculator(EMT())
+
+# Initial state:
+qn = QuasiNewton(slab, trajectory='initial.traj')
+qn.run(fmax=0.05)
+
+# Final state:
+slab[-1].x += slab.get_cell()[0, 0] / 2
+qn = QuasiNewton(slab, trajectory='final.traj')
+qn.run(fmax=0.05)
diff --git a/doc/tutorials/neb/diffusion2.py b/doc/tutorials/neb/diffusion2.py
new file mode 100644
index 0000000..befdbef
--- /dev/null
+++ b/doc/tutorials/neb/diffusion2.py
@@ -0,0 +1,24 @@
+from ase.io import read
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.neb import NEB
+from ase.optimize import BFGS
+
+initial = read('initial.traj')
+final = read('final.traj')
+
+constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial])
+
+images = [initial]
+for i in range(3):
+    image = initial.copy()
+    image.set_calculator(EMT())
+    image.set_constraint(constraint)
+    images.append(image)
+
+images.append(final)
+
+neb = NEB(images)
+neb.interpolate()
+qn = BFGS(neb, trajectory='neb.traj')
+qn.run(fmax=0.05)
diff --git a/doc/tutorials/neb/diffusion3.py b/doc/tutorials/neb/diffusion3.py
new file mode 100644
index 0000000..822afce
--- /dev/null
+++ b/doc/tutorials/neb/diffusion3.py
@@ -0,0 +1,30 @@
+from ase.io import read
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.neb import NEB
+from ase.optimize import BFGS
+from ase.io.trajectory import PickleTrajectory
+from ase.parallel import rank, size
+
+initial = read('initial.traj')
+final = read('final.traj')
+
+constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial])
+
+images = [initial]
+j = rank * 3 // size  # my image number
+for i in range(3):
+    image = initial.copy()
+    if i == j:
+        image.set_calculator(EMT())
+    image.set_constraint(constraint)
+    images.append(image)
+images.append(final)
+
+neb = NEB(images, parallel=True)
+neb.interpolate()
+qn = BFGS(neb)
+if rank % (size // 3) == 0:
+    traj = PickleTrajectory('neb%d.traj' % j, 'w', images[1 + j], master=True)
+    qn.attach(traj)
+qn.run(fmax=0.05)
diff --git a/doc/tutorials/saving_graphics.py b/doc/tutorials/saving_graphics.py
new file mode 100644
index 0000000..a12785d
--- /dev/null
+++ b/doc/tutorials/saving_graphics.py
@@ -0,0 +1,60 @@
+# creates:  nice.png
+
+import numpy as np
+
+from ase import Atoms
+from ase.io import write
+
+atoms = Atoms('Ag', cell=(2.7, 2.7, 2.7), pbc=True) * (18, 8, 8)
+
+# view with ag
+#view(atoms)
+rotation = '-70x, -20y, -2z' # found using ag menu 'view -> rotate'
+
+#Make colors
+from ase.utils import hsv
+colors = hsv(atoms.positions[:, 0])
+
+# Textures
+tex = ['jmol',] * 288 + ['glass',] * 288+ ['ase3',] * 288 + ['vmd',] * 288
+
+
+# keywords
+kwargs = { # Keywords that exist for eps, png, and pov
+'rotation': rotation,
+'show_unit_cell': 2,
+'colors': colors,
+'radii': None,
+}
+
+extra_kwargs = { # For povray files only
+'display'      : False, # Display while rendering
+'pause'        : False, # Pause when done rendering (only if display)
+'transparent'  : False, # Transparent background
+'canvas_width' : None,  # Width of canvas in pixels
+'canvas_height': None,  # Height of canvas in pixels 
+'camera_dist'  : 50.,   # Distance from camera to front atom
+'image_plane'  : None,  # Distance from front atom to image plane
+                        # (focal depth for perspective)
+'camera_type'  : 'perspective', # perspective, ultra_wide_angle
+'point_lights' : [],             # [[loc1, color1], [loc2, color2],...]
+'area_light'   : [(2., 3., 40.) ,# location
+                  'White',       # color
+                  .7, .7, 3, 3], # width, height, Nlamps_x, Nlamps_y
+'background'   : 'White',        # color
+'textures'     : tex, # Length of atoms list of texture names
+'celllinewidth': 0.05, # Radius of the cylinders representing the cell
+}
+
+# Make flat png file
+#write('flat.png', atoms, **kwargs)
+
+# Make the color of the glass beads semi-transparent
+colors2 = np.zeros((1152, 4))
+colors2[:, :3] = colors
+colors2[288: 576, 3] = 0.95
+kwargs['colors'] = colors2
+kwargs.update(extra_kwargs)
+
+# Make the raytraced image
+write('nice.pov', atoms, run_povray=True, **kwargs)
diff --git a/doc/tutorials/spacegroup/spacegroup-al.py b/doc/tutorials/spacegroup/spacegroup-al.py
new file mode 100644
index 0000000..e15623b
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-al.py
@@ -0,0 +1,4 @@
+from ase.lattice.spacegroup import crystal
+
+a = 4.05
+al = crystal('Al', [(0,0,0)], spacegroup=225, cellpar=[a, a, a, 90, 90, 90])
diff --git a/doc/tutorials/spacegroup/spacegroup-cosb3.py b/doc/tutorials/spacegroup/spacegroup-cosb3.py
new file mode 100644
index 0000000..9997e45
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-cosb3.py
@@ -0,0 +1,32 @@
+import numpy as np
+import ase.utils.geometry as geometry
+import ase.io as io
+
+# Create a new atoms instance with Co at origo including all atoms on the 
+# surface of the unit cell
+cosb3 = geometry.cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01)
+
+# Define the atomic bonds to show
+bondatoms = []
+symbols = cosb3.get_chemical_symbols()
+for i in xrange(len(cosb3)):
+    for j in xrange(i):
+        if (symbols[i] == symbols[j] == 'Co' and 
+            cosb3.get_distance(i, j) < 4.53):
+            bondatoms.append((i, j))
+        elif (symbols[i] == symbols[j] == 'Sb' and 
+              cosb3.get_distance(i, j) < 2.99):
+            bondatoms.append((i, j))
+
+# Create nice-looking image using povray
+io.write('spacegroup-cosb3.pov', cosb3,         
+         transparent=False, 
+         display=False,
+         run_povray=True,
+         camera_type='perspective',
+         canvas_width=320,
+         radii=0.4,
+         rotation='90y',
+         bondlinewidth=0.07,
+         bondatoms=bondatoms,
+         )
diff --git a/doc/tutorials/spacegroup/spacegroup-diamond.py b/doc/tutorials/spacegroup/spacegroup-diamond.py
new file mode 100644
index 0000000..7f59474
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-diamond.py
@@ -0,0 +1,4 @@
+from ase.lattice.spacegroup import crystal
+
+a = 3.57
+diamond = crystal('C', [(0,0,0)], spacegroup=227, cellpar=[a, a, a, 90, 90, 90])
diff --git a/doc/tutorials/spacegroup/spacegroup-fe.py b/doc/tutorials/spacegroup/spacegroup-fe.py
new file mode 100644
index 0000000..3454e83
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-fe.py
@@ -0,0 +1,4 @@
+from ase.lattice.spacegroup import crystal
+
+a = 2.87
+fe = crystal('Fe', [(0,0,0)], spacegroup=229, cellpar=[a, a, a, 90, 90, 90])
diff --git a/doc/tutorials/spacegroup/spacegroup-mg.py b/doc/tutorials/spacegroup/spacegroup-mg.py
new file mode 100644
index 0000000..366f737
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-mg.py
@@ -0,0 +1,6 @@
+from ase.lattice.spacegroup import crystal
+
+a = 3.21
+c = 5.21
+mg = crystal('Mg', [(1./3., 2./3., 3./4.)], spacegroup=194,
+             cellpar=[a, a, c, 90, 90, 120])
diff --git a/doc/tutorials/spacegroup/spacegroup-nacl.py b/doc/tutorials/spacegroup/spacegroup-nacl.py
new file mode 100644
index 0000000..e66527a
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-nacl.py
@@ -0,0 +1,5 @@
+from ase.lattice.spacegroup import crystal
+
+a = 5.64
+nacl = crystal(['Na', 'Cl'], [(0, 0, 0), (0.5, 0.5, 0.5)], spacegroup=225,
+               cellpar=[a, a, a, 90, 90, 90])
diff --git a/doc/tutorials/spacegroup/spacegroup-rutile.py b/doc/tutorials/spacegroup/spacegroup-rutile.py
new file mode 100644
index 0000000..80dd4d7
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-rutile.py
@@ -0,0 +1,6 @@
+from ase.lattice.spacegroup import crystal
+
+a = 4.6
+c = 2.95
+rutile =crystal(['Ti', 'O'], basis=[(0, 0, 0), (0.3, 0.3, 0.0)],
+                spacegroup=136, cellpar=[a, a, c, 90, 90, 90])
diff --git a/doc/tutorials/spacegroup/spacegroup-sg.py b/doc/tutorials/spacegroup/spacegroup-sg.py
new file mode 100644
index 0000000..6d86780
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-sg.py
@@ -0,0 +1,12 @@
+from ase.lattice.spacegroup import Spacegroup
+
+sg = Spacegroup(225)
+
+print 'Space group:', sg.no, sg.symbol
+print 'Primitive cell:\n', sg.scaled_primitive_cell
+print 'Reciprocal cell:\n', sg.scaled_reciprocal_cell
+print 'Lattice type:', sg.lattice
+print 'Lattice sub-translations', sg.subtrans
+print 'Centerosymmetric', sg.centerosymmetric
+print 'Rotation matrices (not including inversions)\n', sg.rotations
+print 'Translation vectors (not including inversions)\n', sg.translations
diff --git a/doc/tutorials/spacegroup/spacegroup-sg2.py b/doc/tutorials/spacegroup/spacegroup-sg2.py
new file mode 100644
index 0000000..a18678e
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-sg2.py
@@ -0,0 +1 @@
+print sg
diff --git a/doc/tutorials/spacegroup/spacegroup-sg3.py b/doc/tutorials/spacegroup/spacegroup-sg3.py
new file mode 100644
index 0000000..e9192af
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-sg3.py
@@ -0,0 +1 @@
+sites,kinds = sg.equivalent_sites([(0, 0, 0.5)])
diff --git a/doc/tutorials/spacegroup/spacegroup-skutterudite.py b/doc/tutorials/spacegroup/spacegroup-skutterudite.py
new file mode 100644
index 0000000..40b5b9a
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup-skutterudite.py
@@ -0,0 +1,9 @@
+from ase.lattice.spacegroup import crystal
+
+a = 9.04
+skutterudite = crystal(('Co', 'Sb'),
+                       basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)],
+                       spacegroup=204, 
+                       cellpar=[a, a, a, 90, 90, 90])
+
+#ase.view(skutterudite)
diff --git a/doc/tutorials/spacegroup/spacegroup.py b/doc/tutorials/spacegroup/spacegroup.py
new file mode 100644
index 0000000..281f824
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup.py
@@ -0,0 +1,28 @@
+# creates: spacegroup-al.png spacegroup-fe.png spacegroup-rutile.png spacegroup-cosb3.png spacegroup-mg.png spacegroup-skutterudite.png spacegroup-diamond.png spacegroup-nacl.png
+
+import ase.io
+
+execfile('spacegroup-al.py')
+execfile('spacegroup-mg.py')
+execfile('spacegroup-fe.py')
+execfile('spacegroup-diamond.py')
+execfile('spacegroup-nacl.py')
+execfile('spacegroup-rutile.py')
+execfile('spacegroup-skutterudite.py')
+
+for name in ['al', 'mg', 'fe', 'diamond', 'nacl', 'rutile', 'skutterudite']:
+    atoms = globals()[name]
+    ase.io.write('spacegroup-%s.pov'%name, 
+                 atoms, 
+                 transparent=False, 
+                 display=False, 
+                 run_povray=True,
+                 #canvas_width=128,
+                 show_unit_cell=2,
+                 rotation='10x,-10y', 
+                 #celllinewidth=0.02,
+                 celllinewidth=0.05,
+                 )
+
+execfile('spacegroup-cosb3.py')
+    
diff --git a/doc/tutorials/spacegroup/spacegroup.rst b/doc/tutorials/spacegroup/spacegroup.rst
new file mode 100644
index 0000000..5fa1e65
--- /dev/null
+++ b/doc/tutorials/spacegroup/spacegroup.rst
@@ -0,0 +1,128 @@
+.. module:: lattice.spacegroup
+
+===============================
+Using the spacegroup subpackage
+===============================
+
+The most evident usage of the spacegroup subpackage is to set up an
+initial unit of a bulk structure. For this you only need to supply the
+unique atoms and their scaled positions, space group and lattice
+parameters.
+
+Examples of setting up bulk structures
+======================================
+
+We start by showing some examples of how to set up some common or
+interesting bulk structures using
+**ase.lattice.spacegroup.crystal()**.  This function takes a lot of
+arguments, of which the most important are:
+
+    symbols : string | sequence of strings
+        Either a string formula or sequence of element
+        symbols. E.g. 'NaCl' and ('Na', 'Cl') are equivalent.
+    basis : list of scaled coordinates
+        Coordinates of the non-equivalent sites in units of the
+        lattice vectors.
+    spacegroup : int | string | Spacegroup instance
+        Space group given either as its number in International Tables
+        or as its Hermann-Mauguin (or international) symbol.
+    setting : 1 | 2
+        Space group setting.
+    cellpar : [a, b, c, alpha, beta, gamma]
+        Cell parameters with angles in degree. Is not used when `cell`
+        is given.
+
+Aluminium (fcc)
+---------------
+
+.. image:: spacegroup-al.png
+
+.. literalinclude:: spacegroup-al.py
+
+The *spacegroup* argument can also be entered with its Hermann-Mauguin
+symbol, e.g. *spacegroup=225* is equivalent to *spacegroup='F m -3 m'*.
+
+Iron (bcc)
+----------
+
+.. image:: spacegroup-fe.png
+
+.. literalinclude:: spacegroup-fe.py
+
+Magnesium (hcp)
+---------------
+
+.. image:: spacegroup-mg.png
+
+.. literalinclude:: spacegroup-mg.py
+
+Diamond
+-------
+
+.. image:: spacegroup-diamond.png
+
+.. literalinclude:: spacegroup-diamond.py
+
+Sodium chloride
+---------------
+
+.. image:: spacegroup-nacl.png
+
+.. literalinclude:: spacegroup-nacl.py
+
+Rutile
+------
+
+.. image:: spacegroup-rutile.png
+
+.. literalinclude:: spacegroup-rutile.py
+
+CoSb3 skutterudite
+------------------
+
+.. image:: spacegroup-skutterudite.png
+
+Skutterudites_ are quite interesting structures with 32 atoms
+in the unit cell.
+
+.. _Skutterudites: http://en.wikipedia.org/wiki/Skutterudite
+
+.. literalinclude:: spacegroup-skutterudite.py
+
+Often this structure is visualised with the Cobalt atoms on the
+corners. This can easily be accomplished with ASE using
+**ase.utils.geomegry.cut()**. Below is the *origo* argument used to
+put the Cobalt atom on the corners and *extend* to include all corner
+and edge atoms, even those belonging to neighbouring unit cells.
+
+.. image:: spacegroup-cosb3.png
+
+.. literalinclude:: spacegroup-cosb3.py
+
+
+The Spacegroup class
+====================
+
+The :epydoc:`ase.lattice.spacegroup.Spacegroup` class is used
+internally by the **ase.lattice.spacegroup.crystal()** function, but
+might sometimes also be useful if you want to know e.g. the symmetry
+operations of a given space group. Instances of the
+:epydoc:`ase.lattice.spacegroup.Spacegroup` class are immutable
+objects holding space group information, such as symmetry operations.
+
+Let us e.g. consider the fcc structure. To print information about the
+space group, do
+
+.. literalinclude:: spacegroup-sg.py
+
+or simply
+
+.. literalinclude:: spacegroup-sg2.py
+
+Or, if you want to figure out what sites in the unit cell are
+equivalent to (0, 0, 0.5), simply do
+
+.. literalinclude:: spacegroup-sg3.py
+
+where *sites* will be an array containing the scaled positions of the
+four symmetry-equivalent sites.
diff --git a/doc/tutorials/stm-tutorial.rst b/doc/tutorials/stm-tutorial.rst
new file mode 100644
index 0000000..912b145
--- /dev/null
+++ b/doc/tutorials/stm-tutorial.rst
@@ -0,0 +1,60 @@
+.. _stm-tutorial:
+
+==============================
+Tutorial: STM images - Al(100)
+==============================
+
+The STM is a revolutionary experimental surface probe that has
+provided direct local insight into the surface electronic
+structure. Sometimes the interpretation of STM topographs are not
+straightforward and therefore theoretically modeled STM images may
+resolve conflicting possibilities and point to an underlying atomistic
+model. The CAMPOS code includes python modules for generating
+Tersoff-Hamann STM topographs. The STM code is illustrated here for a
+Al(100) in a 2x2 unit cell.
+
+Let's make the Al(100) fcc surface by using the :mod:`lattice` module::
+
+  from ase.lattice.surface import *
+  atoms = fcc100('Al', size=(2,2,2))
+
+Now a calculator must be defined, in this tutorial we will make a STM
+image from the GPAW calculator.
+
+For the GPAW code the calculator for the Al(100) surface can be
+defined like this::
+
+  from gpaw import GPAW
+  calc = GPAW(gpts=(28,28,20),nbands=28,
+  	kpts=(4,4,1),txt='Al100.out')
+  atoms.set_calculator(calc)
+  energy = atoms.get_potential_energy() 
+  calc.write('Al100.gpw', 'all')
+
+
+Linescans
+=========
+
+In this section we will make simulated STM linescans and contour plot
+using matplotlib. First initialize the :class:`STM` object and get the
+averaged current along the z-direction::
+
+  from ase import STM
+  stm = STM(atoms, symmetries=[0, 1, 2])
+  z = 2.5
+  c = stm.get_averaged_current(z)
+
+From the current we make a scan to get a 2D array of constant current
+heights::
+
+  h = stm.scan(c)
+
+Finally we make a contour plot::
+
+  import pylab as p
+  p.contourf(h, 40)
+  p.hot()
+  p.colorbar()
+  p.show()	
+  
+
diff --git a/doc/tutorials/surface.py b/doc/tutorials/surface.py
new file mode 100644
index 0000000..30dcea2
--- /dev/null
+++ b/doc/tutorials/surface.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+# creates:  surface.png
+import os
+from ase.io import read, write
+execfile('N2Cu.py')
+image = read('N2Cu.traj at -1')
+write('surface.pov', image, transparent=False, display=False, run_povray=True)
diff --git a/doc/tutorials/surface.rst b/doc/tutorials/surface.rst
new file mode 100644
index 0000000..975be96
--- /dev/null
+++ b/doc/tutorials/surface.rst
@@ -0,0 +1,202 @@
+.. _surface:
+
+================================
+Introduction: Nitrogen on copper
+================================
+
+This section gives a quick (and incomplete) overview of what ASE can do.
+
+We will calculate the adsorption energy of a nitrogen
+molecule on a copper surface.
+This is done by calculating the total
+energy for the isolated slab and for the isolated molecule. The
+adsorbate is then added to the slab and relaxed, and the total energy
+for this composite system is calculated. The adsorption energy is
+obtained as the sum of the isolated energies minus the energy of the
+composite system.
+
+Here is a picture of the system after the relaxation:
+
+.. image:: surface.png
+
+Please have a look at the following script :svn:`doc/tutorials/N2Cu.py`:
+
+.. literalinclude:: N2Cu.py
+
+Assuming you have ASE setup correctly (:ref:`download_and_install`)
+run the script::
+
+  python N2Cu.py
+
+Please read below what the script does.
+
+-----
+Atoms
+-----
+
+The :class:`~ase.atoms.Atoms` object is a collection of atoms.  Here
+is how to define a N2 molecule by directly specifying the position of
+two nitrogen atoms::
+
+  from ase import Atoms
+  d = 1.10
+  molecule = Atoms('2N', positions=[(0., 0., 0.), (0., 0., d)])
+
+You can also build crystals using, for example, the lattice module
+which returns :class:`~ase.atoms.Atoms` objects corresponding to
+common crystal structures. Let us make a Cu (111) surface::
+
+  from ase.lattice.surface import fcc111
+  slab = fcc111('Cu', size=(4,4,2), vacuum=10.0)
+
+
+
+-----------
+Calculators
+----------- 
+
+The following :mod:`calculators` can be used with ASE:
+:mod:`~calculators.emt`, Asap_, Dacapo_, GPAW_, Siesta_
+(Abinit_, Vasp_, and MMTK - work in progress).
+  
+.. _Asap: http://wiki.fysik.dtu.dk/asap
+.. _Dacapo: http://wiki.fysik.dtu.dk/dacapo
+.. _GPAW: http://wiki.fysik.dtu.dk/gpaw
+.. _Siesta: http://www.icmab.es/siesta
+.. _Abinit: http://www.abinit.org
+.. _Vasp: http://cms.mpi.univie.ac.at/vasp
+
+In this overview we use the effective medium theory (EMT) calculator,
+as it is very fast and hence useful for getting started.
+
+We can attach a calculator to the previously created
+:class:`~ase.atoms.Atoms` objects::
+
+  from ase import EMT
+  slab.set_calculator(EMT())
+  molecule.set_calculator(EMT()) 
+
+and use it to calculate the total energies for the systems by using
+the :meth:`~ase.atoms.Atoms.get_potential_energy` method from the
+:class:`~ase.atoms.Atoms` class::
+
+  e_slab = slab.get_potential_energy()
+  e_N2 = molecule.get_potential_energy()
+
+
+--------------------
+Structure relaxation
+--------------------
+
+Let's use the :mod:`QuasiNewton <optimize.qn>` minimizer to optimize the
+structure of the N2 molecule adsorbed on the Cu surface. First add the
+adsorbate to the Cu slab, for example in the on-top position::
+  
+  h = 1.85
+  add_adsorbate(slab, molecule, h, 'ontop')
+
+In order to speed up the relaxation, let us keep the Cu atoms fixed in
+the slab by using :class:`~constraints.FixAtoms` from the
+:mod:`~ase.constraints` module. Only the N2 molecule is then allowed
+to relax to the equilibrium structure::
+
+  from ase.constraints import FixAtoms
+  constraint = FixAtoms(mask=[a.symbol != 'N' for a in slab])
+  slab.set_constraint(constraint)
+
+Now attach the :mod:`QuasiNewton <optimize.qn>` minimizer to the
+system and save the trajectory file. Run the minimizer with the
+convergence criteria that the force on all atoms should be less than
+some ``fmax``::
+
+  from ase.optimize import QuasiNewton
+  dyn = QuasiNewton(slab, trajectory='N2Cu.traj')
+  dyn.run(fmax=0.05)
+
+.. note::
+
+  The general documentation on
+  :ref:`structure optimizations <structure_optimizations>` contains
+  information about different algorithms, saving the state of an optimizer
+  and other functionality which should be considered when performing
+  expensive relaxations.
+
+------------
+Input-output
+------------
+
+Writing the atomic positions to a file is done with the
+:func:`~ase.io.write` function::
+
+  from ase.io import write
+  write('slab.xyz', slab)
+
+This will write a file in the xyz-format.  Possible formats are:
+
+========  ===========================
+format    description
+========  ===========================
+``xyz``   Simple xyz-format
+``cube``  Gaussian cube file
+``pdb``   Protein data bank file
+``traj``  ASE's own trajectory format
+``py``    Python script
+========  ===========================
+
+Reading from a file is done like this::
+
+  from ase.io import read
+  slab_from_file = read('slab.xyz')
+
+If the file contains several configurations, the default behavior of
+the :func:`~ase.io.write` function is to return the last
+configuration. However, we can load a specific configuration by
+doing::
+
+  read('slab.traj')      # last configuration
+  read('slab.traj', -1)  # same as above
+  read('slab.traj', 0)   # first configuration
+
+
+-------------
+Visualization
+-------------
+
+The simplest way to visualize the atoms is the :func:`~visualize.view`
+function::
+
+  from ase.visualize import view
+  view(slab)
+
+This will pop up a :mod:`gui` window.  Alternative viewers can be used
+by specifying the optional keyword ``viewer=...`` - use one of
+'ase.gui', 'gopenmol', 'vmd', or 'rasmol'. (Note that these alternative
+viewers are not a part of ASE and will need to be installed by the user
+separately.) The VMD viewer can take an optional ``data`` argument to
+show 3D data::
+
+  view(slab, viewer='VMD', data=array)
+
+
+------------------
+Molecular dynamics
+------------------
+
+Let us look at the nitrogen molecule as an example of molecular
+dynamics with the :class:`VelocityVerlet <md.verlet.VelocityVerlet>`
+algorithm. We first create the :class:`VelocityVerlet
+<md.verlet.VelocityVerlet>` object giving it the molecule and the time
+step for the integration of Newton's law. We then perform the dynamics
+by calling its :meth:`run` method and giving it the number of steps to
+take::
+
+  from ase.md.verlet import VelocityVerlet
+  from ase import units
+  dyn = VelocityVerlet(molecule, dt=1.0 * units.fs)
+  for i in range(10):
+     pot = molecule.get_potential_energy()
+     kin = molecule.get_kinetic_energy()
+     print '%2d: %.5f eV, %.5f eV, %.5f eV' % (i, pot + kin, pot, kin)
+     dyn.run(steps=20)
+
+
diff --git a/doc/tutorials/tutorials.rst b/doc/tutorials/tutorials.rst
new file mode 100644
index 0000000..da2999d
--- /dev/null
+++ b/doc/tutorials/tutorials.rst
@@ -0,0 +1,80 @@
+.. _tutorials:
+
+Tutorials
+=========
+
+Python
+------
+
+If you are not familiar with Python please read :ref:`python_info`.
+
+.. toctree::
+   :maxdepth: 3
+
+   ../python
+
+ASE
+---
+
+Most of the tutorials will use the :mod:`EMT <emt>` potential, but any
+other :mod:`Calculator <calculators>` could be plugged in instead.
+
+.. toctree::
+   :maxdepth: 3
+
+   surface
+
+   manipulating_atoms
+   spacegroup/spacegroup
+   atomization
+   lattice_constant
+   eos/eos
+   dissociation
+   aneb
+   association
+   neb/diffusion
+   constraints/diffusion
+   md/md
+   stm-tutorial
+   wannier/wannier
+ 
+NumPy
+-----
+
+If your ASE scripts make extensive use of matrices you may want to familiarize yourself with :ref:`numpy`.
+
+.. toctree::
+   :maxdepth: 3
+
+   ../numpy
+
+Further reading
+---------------
+
+For more details:
+
+* Look at the documentation for the individual :mod:`modules <ase>`.
+* See the automatically generated documentation ``Epydoc``: :epydoc:`ase`.
+* Browse the `source code`_ online.
+* Have a look at :mod:`~calculators.siesta` exercises:
+
+.. toctree::
+   :maxdepth: 3
+
+   ../exercises/exercises
+
+.. _source code: http://trac.fysik.dtu.dk/projects/ase/browser/trunk
+
+Videos
+------
+
+The following video tutorials are available:
+
+ - **Overview and installation of ASE** (duration: ~5min 30sec; size: 26 MB) - en: |oi_en|
+
+.. |oi_en| image:: ../_static/United_States_of_America.png
+   :target: https://wiki.fysik.dtu.dk/ase-files/oi_en.avi
+
+.. |oi_cn| image:: ../_static/China.png
+   :target: https://wiki.fysik.dtu.dk/ase-files/oi_ch.avi
+
diff --git a/doc/tutorials/wannier/benzene.py b/doc/tutorials/wannier/benzene.py
new file mode 100644
index 0000000..94023f9
--- /dev/null
+++ b/doc/tutorials/wannier/benzene.py
@@ -0,0 +1,15 @@
+from ase.data.molecules import molecule
+from gpaw import GPAW
+
+atoms = molecule('C6H6')
+atoms.center(vacuum=3.5)
+
+calc = GPAW(h=.21, xc='PBE', txt='benzene.txt', nbands=18)
+atoms.set_calculator(calc)
+atoms.get_potential_energy()
+
+calc.set(fixdensity=True, txt='benzene-harris.txt',
+         nbands=40, eigensolver='cg', convergence={'bands': 35})
+atoms.get_potential_energy()
+
+calc.write('benzene.gpw', mode='all')
diff --git a/doc/tutorials/wannier/plot_band_structure.py b/doc/tutorials/wannier/plot_band_structure.py
new file mode 100644
index 0000000..ebea3c3
--- /dev/null
+++ b/doc/tutorials/wannier/plot_band_structure.py
@@ -0,0 +1,22 @@
+import pylab as pl
+
+fig = pl.figure(1, dpi=80, figsize=(4.2, 6))
+fig.subplots_adjust(left=.16, right=.97, top=.97, bottom=.05)
+
+# Plot KS bands
+k, eps = pl.load('KSbands.txt', unpack=True)
+pl.plot(k, eps, 'ro', label='DFT', ms=9)
+
+# Plot Wannier bands
+k, eps = pl.load('WANbands.txt', unpack=True)
+pl.plot(k, eps, 'k.', label='Wannier')
+
+pl.plot([-.5, .5], [1, 1], 'k:', label='_nolegend_')
+pl.text(-.5, 1, 'fixedenergy', ha='left', va='bottom')
+pl.axis('tight')
+pl.xticks([-.5, -.25, 0, .25, .5],
+          [ r'$X$', r'$\Delta$', r'$\Gamma$', r'$\Delta$', r'$X$'], size=16)
+pl.ylabel(r'$E - E_F\  \rm{(eV)}$', size=16)
+pl.legend()
+pl.savefig('bands.png', dpi=80)
+pl.show()
diff --git a/doc/tutorials/wannier/plot_spectral_weight.py b/doc/tutorials/wannier/plot_spectral_weight.py
new file mode 100644
index 0000000..84ad10e
--- /dev/null
+++ b/doc/tutorials/wannier/plot_spectral_weight.py
@@ -0,0 +1,20 @@
+from ase.dft import Wannier
+from gpaw import restart
+
+atoms, calc = restart('benzene.gpw', txt=None)
+wan = Wannier(nwannier=18, calc=calc, fixedstates=15, file='wan18.pickle')
+
+import pylab as pl
+weight_n = pl.sum(abs(wan.V_knw[0])**2, 1)
+N = len(weight_n)
+F = wan.fixedstates_k[0]
+pl.figure(1, figsize=(12, 4))
+pl.bar(range(1, N+1), weight_n, width=0.65, bottom=0,
+        color='k', edgecolor='k', linewidth=None,
+       align='center', orientation='vertical')
+pl.plot([F+.5, F+.5], [0, 1], 'k--')
+pl.axis(xmin=.32, xmax=N+1.33, ymin=0, ymax=1)
+pl.xlabel('Eigenstate')
+pl.ylabel('Projection of wannier functions')
+pl.savefig('spectral_weight.png')
+pl.show()
diff --git a/doc/tutorials/wannier/polyacetylene.py b/doc/tutorials/wannier/polyacetylene.py
new file mode 100644
index 0000000..de52ed1
--- /dev/null
+++ b/doc/tutorials/wannier/polyacetylene.py
@@ -0,0 +1,23 @@
+import numpy as np
+
+from ase import Atoms
+from ase.dft.kpoints import monkhorst_pack
+from gpaw import GPAW
+
+kpts = monkhorst_pack((13, 1, 1)) + [1e-5, 0, 0]
+calc = GPAW(h=.21, xc='PBE', kpts=kpts, nbands=12, txt='poly.txt',
+            eigensolver='cg', convergence={'bands': 9})
+
+CC = 1.38
+CH = 1.094
+a  = 2.45
+x = a / 2.
+y = np.sqrt(CC**2 - x**2)
+atoms = Atoms('C2H2', pbc=(True, False, False), cell=(a, 8., 6.),
+              calculator=calc, positions=[[0,    0, 0],
+                                          [x,    y, 0],
+                                          [x, y+CH, 0],
+                                          [0,  -CH, 0]])
+atoms.center()
+atoms.get_potential_energy()
+calc.write('poly.gpw', mode='all')
diff --git a/doc/tutorials/wannier/wannier.rst b/doc/tutorials/wannier/wannier.rst
new file mode 100644
index 0000000..2eaf2cb
--- /dev/null
+++ b/doc/tutorials/wannier/wannier.rst
@@ -0,0 +1,10 @@
+=================================
+Partly occupied Wannier Functions
+=================================
+
+.. literalinclude:: benzene.py
+.. literalinclude:: wannier_benzene.py
+.. literalinclude:: plot_spectral_weight.py
+.. literalinclude:: polyacetylene.py
+.. literalinclude:: wannier_polyacetylene.py
+.. literalinclude:: plot_band_structure.py
diff --git a/doc/tutorials/wannier/wannier_benzene.py b/doc/tutorials/wannier/wannier_benzene.py
new file mode 100644
index 0000000..cd68aa9
--- /dev/null
+++ b/doc/tutorials/wannier/wannier_benzene.py
@@ -0,0 +1,17 @@
+from gpaw import restart
+from ase.dft import Wannier
+
+atoms, calc = restart('benzene.gpw', txt=None)
+
+# Make wannier functions of occupied space only
+wan = Wannier(nwannier=15, calc=calc)
+wan.localize()
+for i in range(wan.nwannier):
+    wan.write_cube(i, 'benzene15_%i.cube' % i)
+
+# Make wannier functions using (three) extra degrees of freedom.
+wan = Wannier(nwannier=18, calc=calc, fixedstates=15)
+wan.localize()
+wan.save('wan18.pickle')
+for i in range(wan.nwannier):
+    wan.write_cube(i, 'benzene18_%i.cube' % i)
diff --git a/doc/tutorials/wannier/wannier_polyacetylene.py b/doc/tutorials/wannier/wannier_polyacetylene.py
new file mode 100644
index 0000000..36a721f
--- /dev/null
+++ b/doc/tutorials/wannier/wannier_polyacetylene.py
@@ -0,0 +1,27 @@
+import numpy as np
+
+from ase.dft import Wannier
+from gpaw import restart
+
+atoms, calc = restart('poly.gpw', txt=None)
+
+# Make wannier functions using (one) extra degree of freedom
+wan = Wannier(nwannier=6, calc=calc, fixedenergy=1.5)
+wan.localize()
+wan.save('poly.pickle')
+wan.translate_all_to_cell((2, 0, 0))
+for i in range(wan.nwannier):
+    wan.write_cube(i, 'polyacetylene_%i.cube' % i)
+
+# Print Kohn-Sham bandstructure
+ef = calc.get_fermi_level()
+f = open('KSbands.txt', 'w')
+for k, kpt_c in enumerate(calc.get_ibz_k_points()):
+    for eps in calc.get_eigenvalues(kpt=k):
+        print >> f, kpt_c[0], eps - ef
+
+# Print Wannier bandstructure
+f = open('WANbands.txt', 'w')
+for k in np.linspace(-.5, .5, 100):
+    for eps in np.linalg.eigvalsh(wan.get_hamiltonian_kpoint([k, 0, 0])).real:
+        print >> f, k, eps - ef
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..5b31ccb
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2007  CAMP
+# Please see the accompanying LICENSE file for further information.
+
+from distutils.core import setup, Command
+from distutils.command.build_py import build_py as _build_py
+from glob import glob
+from os.path import join
+
+import os
+import sys
+
+long_description = """\
+ASE is a python package providing an open source Atomic Simulation
+Environment in the python scripting language."""
+
+
+if sys.version_info < (2, 4, 0, 'final', 0):
+    raise SystemExit, 'Python 2.4 or later is required!'
+
+packages = ['ase',
+            'ase.cluster',
+            'ase.cluster.data',
+            'ase.io',
+            'ase.md',
+            'ase.dft',
+            'ase.gui',
+            'ase.gui.languages',
+            'ase.data',
+            'ase.test',
+            'ase.test.abinit',
+            'ase.test.castep',
+            'ase.test.cmr',
+            'ase.test.elk',
+            'ase.test.fio',
+            'ase.test.fleur',
+            'ase.test.jacapo',
+            'ase.test.nwchem',
+            'ase.test.vasp',
+            'ase.tasks',
+            'ase.utils',
+            'ase.lattice',
+            'ase.lattice.spacegroup',
+            'ase.examples',
+            'ase.optimize',
+            'ase.optimize.test',
+            'ase.visualize',
+            'ase.visualize.vtk',
+            'ase.transport',
+            'ase.calculators',
+            'ase.calculators.jacapo']
+
+package_dir={'ase': 'ase'}
+
+package_data={'ase': ['lattice/spacegroup/spacegroup.dat']}
+
+class test(Command):
+    description = 'build and run test suite; exit code is number of failures'
+    user_options = []
+    
+    def __init__(self, dist):
+        Command.__init__(self, dist)
+        self.sub_commands = ['build']
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        self.run_command('build')
+        buildcmd = self.get_finalized_command('build')
+        sys.path.insert(0, buildcmd.build_lib)
+
+        from ase.test import test as _test
+        testdir = '%s/testase-tempfiles' % buildcmd.build_base
+        origcwd = os.getcwd()
+        if not os.path.exists(testdir):
+            os.mkdir(testdir)
+        os.chdir(testdir)
+        try:
+            results = _test(2, display=False)
+            if results.failures or results.errors:
+                print >> sys.stderr, 'Test suite failed'
+                raise SystemExit(len(results.failures) + len(results.errors))
+        finally:
+            os.chdir(origcwd)
+
+class build_py(_build_py):
+    """Custom distutils command to build translations."""
+    def __init__(self, *args, **kwargs):
+        _build_py.__init__(self, *args, **kwargs)
+        # Keep list of files to appease bdist_rpm.  We have to keep track of
+        # all the installed files for no particular reason.
+        self.mofiles = []
+
+    def run(self):
+        """Compile translation files (requires gettext)."""
+        _build_py.run(self)
+        msgfmt = 'msgfmt'
+        status = os.system(msgfmt + ' -V')
+        if status == 0:
+            for pofile in glob('ase/gui/po/??_??/LC_MESSAGES/ag.po'):
+                dirname = join(self.build_lib, os.path.dirname(pofile))
+                if not os.path.isdir(dirname):
+                    os.makedirs(dirname)
+                mofile = join(dirname, 'ag.mo')
+                status = os.system('%s -cv %s --output-file=%s 2>&1' %
+                                   (msgfmt, pofile, mofile))
+                assert status == 0, 'msgfmt failed!'
+                self.mofiles.append(mofile)
+
+    def get_outputs(self, *args, **kwargs):
+        return _build_py.get_outputs(self, *args, **kwargs) + self.mofiles
+
+# Get the current version number:
+execfile('ase/svnversion_io.py')  # write ase/svnversion.py and get svnversion
+execfile('ase/version.py')        # get version_base
+if svnversion and os.name not in ['ce', 'nt']: # MSI accepts only version X.X.X
+    version = version_base + '.' + svnversion
+else:
+    version = version_base
+
+scripts = ['tools/ag', 'tools/ase', 'tools/ASE2ase', 'tools/testase']
+
+setup(name='python-ase',
+      version=version,
+      description='Atomic Simulation Environment',
+      url='https://wiki.fysik.dtu.dk/ase',
+      maintainer='CAMd',
+      maintainer_email='camd at fysik.dtu.dk',
+      license='LGPLv2.1+',
+      platforms=['linux'],
+      packages=packages,
+      package_dir=package_dir,
+      package_data=package_data,
+      scripts=scripts,
+      long_description=long_description,
+      cmdclass={'build_py': build_py,
+                'test': test})
diff --git a/tools/ASE2ase b/tools/ASE2ase
new file mode 100644
index 0000000..e2534fb
--- /dev/null
+++ b/tools/ASE2ase
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+from optparse import OptionParser
+
+description = """Convert ASE2 script FILEs to ase3.  FILEs will be
+modified in-place to be compatible with ase3.  Original files are
+backed up."""
+
+p = OptionParser(usage='%prog FILE...', description=description)
+
+opts, args = p.parse_args()
+
+def convert(filename):
+    lines = open(filename).readlines()
+    t1 = ''.join(lines)
+
+    first = True
+    for i in range(len(lines)):
+        line = lines[i]
+        if line.startswith('from ASE'):
+            if first:
+                lines[i] = 'from ase.all import *\n'
+                first = False
+            else:
+                lines[i] = ''
+
+    t = ''.join(lines)
+
+    for old, new in [('GetCartesianPositions', 'get_positions'),
+                     ('SetCartesianPositions', 'set_positions'),
+                     ('GetPotentialEnergy', 'get_potential_energy'),
+                     ('SetCalculator', 'set_calculator'),
+                     ('GetScaledPositions', 'get_scaled_positions'),
+                     ('SetScaledPositions', 'set_scaled_positions'),
+                     ('SetUnitCell', 'set_cell'),
+                     ('GetUnitCell', 'get_cell'),
+                     ('GetBoundaryConditions', 'get_pbc'),
+                     ('GetCartesianForces', 'get_forces'),
+                     ('GetCartesianVelocities', 'get_velocities'),
+                     ('SetCartesianVelocities', 'set_velocities'),
+                     ('GetCartesianMomenta', 'get_momenta'),
+                     ('SetCartesianMomenta', 'set_momenta'),
+                     ('ListOfAtoms', 'Atoms'),
+                     ('periodic', 'pbc'),
+                     ('pbcity', 'periodicity'),
+                     ('.Converge(', '.run('),
+                     ('Repeat', 'repeat'),
+                     ('Numeric', 'numpy'),
+                     ('numpyal', 'Numerical'),
+                     ('GetAtomicNumber()', 'number'),
+                     ('GetChemicalSymbol()', 'symbol'),
+                     ('GetCartesianPosition()', 'position'),
+                     ('GetTag()', 'tag'),
+                     ('GetCharge()', 'charge'),
+                     ('GetMass()', 'mass'),
+                     ('GetCartesianMomentum()', 'momentum'),
+                     ('GetMagneticMoment()', 'magmom'),
+                     ]:
+        t = t.replace(old, new)
+
+    t2 = ''
+    while 1:
+        i = t.find('.')
+        i2 = t.find('def ')
+        if 0 <= i < i2:
+            n = 1
+        elif i2 != -1:
+            n = 4
+            i = i2
+        else:
+            break
+        t2 += t[:i + n]
+        t = t[i + n:]
+        if t[0].isupper() and t[1].islower():
+            j = t.find('(')
+            if j != -1 and t[2: j].isalpha():
+                for k in range(j):
+                    if t[k].isupper() and k > 0:
+                        t2 += '_'
+                    t2 += t[k].lower()
+                t = t[j:]
+
+    t2 += t
+
+    if t2 != t1:
+        print filename, len(t1) - len(t2)
+        open(filename + '.bak', 'w').write(t1)
+        open(filename, 'w').write(t2)
+
+for filename in args:
+    convert(filename)
diff --git a/tools/ASE2ase.py b/tools/ASE2ase.py
new file mode 100755
index 0000000..d7cb5d4
--- /dev/null
+++ b/tools/ASE2ase.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+import sys
+
+def convert(filename):
+    lines = open(filename).readlines()
+    t1 = ''.join(lines)
+
+    first = True
+    for i in range(len(lines)):
+        line = lines[i]
+        if line.startswith('from ASE'):
+            if first:
+                lines[i] = 'from ase.all import *\n'
+                first = False
+            else:
+                lines[i] = ''
+
+    t = ''.join(lines)
+
+    for old, new in [('GetCartesianPositions', 'get_positions'),
+                     ('SetCartesianPositions', 'set_positions'),
+                     ('GetPotentialEnergy', 'get_potential_energy'),
+                     ('SetCalculator', 'set_calculator'),
+                     ('GetScaledPositions', 'get_scaled_positions'),
+                     ('SetScaledPositions', 'set_scaled_positions'),
+                     ('SetUnitCell', 'set_cell'),
+                     ('GetUnitCell', 'get_cell'),
+                     ('GetBoundaryConditions', 'get_pbc'),
+                     ('GetCartesianForces', 'get_forces'),
+                     ('GetCartesianVelocities', 'get_velocities'),
+                     ('SetCartesianVelocities', 'set_velocities'),
+                     ('GetCartesianMomenta', 'get_momenta'),
+                     ('SetCartesianMomenta', 'set_momenta'),
+                     ('ListOfAtoms', 'Atoms'),
+                     ('periodic', 'pbc'),
+                     ('pbcity', 'periodicity'),
+                     ('.Converge(', '.run('),
+                     ('Repeat', 'repeat'),
+                     ('Numeric', 'numpy'),
+                     ('numpyal', 'Numerical'),
+                     ('GetAtomicNumber()', 'number'),
+                     ('GetChemicalSymbol()', 'symbol'),
+                     ('GetCartesianPosition()', 'position'),
+                     ('GetTag()', 'tag'),
+                     ('GetCharge()', 'charge'),
+                     ('GetMass()', 'mass'),
+                     ('GetCartesianMomentum()', 'momentum'),
+                     ('GetMagneticMoment()', 'magmom'),
+                     ]:
+        t = t.replace(old, new)
+
+    t2 = ''
+    while 1:
+        i = t.find('.')
+        i2 = t.find('def ')
+        if 0 <= i < i2:
+            n = 1
+        elif i2 != -1:
+            n = 4
+            i = i2
+        else:
+            break
+        t2 += t[:i + n]
+        t = t[i + n:]
+        if t[0].isupper() and t[1].islower():
+            j = t.find('(')
+            if j != -1 and t[2: j].isalpha():
+                for k in range(j):
+                    if t[k].isupper() and k > 0:
+                        t2 += '_'
+                    t2 += t[k].lower()
+                t = t[j:]
+
+    t2 += t
+
+    if t2 != t1:
+        print filename, len(t1) - len(t2)
+        open(filename + '.bak', 'w').write(t1)
+        open(filename, 'w').write(t2)
+
+for filename in sys.argv[1:]:
+    convert(filename)
diff --git a/tools/ag b/tools/ag
new file mode 100755
index 0000000..cff8ab7
--- /dev/null
+++ b/tools/ag
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+from ase.gui.ag import main
+
+main()
diff --git a/tools/ase b/tools/ase
new file mode 100755
index 0000000..bd1fe73
--- /dev/null
+++ b/tools/ase
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+from ase.tasks.main import main
+
+main()
diff --git a/tools/pep8.py b/tools/pep8.py
new file mode 100755
index 0000000..7040f5a
--- /dev/null
+++ b/tools/pep8.py
@@ -0,0 +1,1399 @@
+#!/usr/bin/python
+# pep8.py - Check Python source code formatting, according to PEP 8
+# Copyright (C) 2006 Johann C. Rocholl <johann at rocholl.net>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Copied from:
+#
+# https://github.com/jcrocholl/pep8/blob/
+# 8d2d68790b6931833277cd671dfb8158962fac0c/pep8.py
+#
+# on May 10, 2011.
+
+
+"""
+Check Python source code formatting, according to PEP 8:
+http://www.python.org/dev/peps/pep-0008/
+
+For usage and a list of options, try this:
+$ python pep8.py -h
+
+This program and its regression test suite live here:
+http://github.com/jcrocholl/pep8
+
+Groups of errors and warnings:
+E errors
+W warnings
+100 indentation
+200 whitespace
+300 blank lines
+400 imports
+500 line length
+600 deprecation
+700 statements
+
+You can add checks to this program by writing plugins. Each plugin is
+a simple function that is called for each line of source code, either
+physical or logical.
+
+Physical line:
+- Raw line of text from the input file.
+
+Logical line:
+- Multi-line statements converted to a single line.
+- Stripped left and right.
+- Contents of strings replaced with 'xxx' of same length.
+- Comments removed.
+
+The check function requests physical or logical lines by the name of
+the first argument:
+
+def maximum_line_length(physical_line)
+def extraneous_whitespace(logical_line)
+def blank_lines(logical_line, blank_lines, indent_level, line_number)
+
+The last example above demonstrates how check plugins can request
+additional information with extra arguments. All attributes of the
+Checker object are available. Some examples:
+
+lines: a list of the raw lines from the input file
+tokens: the tokens that contribute to this logical line
+line_number: line number in the input file
+blank_lines: blank lines before this one
+indent_char: first indentation character in this file (' ' or '\t')
+indent_level: indentation (with tabs expanded to multiples of 8)
+previous_indent_level: indentation on previous line
+previous_logical: previous logical line
+
+The docstring of each check function shall be the relevant part of
+text from PEP 8. It is printed if the user enables --show-pep8.
+Several docstrings contain examples directly from the PEP 8 document.
+
+Okay: spam(ham[1], {eggs: 2})
+E201: spam( ham[1], {eggs: 2})
+
+These examples are verified automatically when pep8.py is run with the
+--doctest option. You can add examples for your own check functions.
+The format is simple: "Okay" or error/warning code followed by colon
+and space, the rest of the line is example source code. If you put 'r'
+before the docstring, you can use \n for newline, \t for tab and \s
+for space.
+
+"""
+
+__version__ = '0.5.1dev'
+
+import os
+import sys
+import re
+import time
+import inspect
+import keyword
+import tokenize
+from optparse import OptionParser
+from fnmatch import fnmatch
+try:
+    frozenset
+except NameError:
+    from sets import ImmutableSet as frozenset
+
+
+DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git'
+DEFAULT_IGNORE = 'E24'
+MAX_LINE_LENGTH = 79
+
+INDENT_REGEX = re.compile(r'([ \t]*)')
+RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)')
+SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)')
+ERRORCODE_REGEX = re.compile(r'[EW]\d{3}')
+DOCSTRING_REGEX = re.compile(r'u?r?["\']')
+WHITESPACE_AROUND_OPERATOR_REGEX = \
+    re.compile('([^\w\s]*)\s*(\t|  )\s*([^\w\s]*)')
+EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
+WHITESPACE_AROUND_NAMED_PARAMETER_REGEX = \
+    re.compile(r'[()]|\s=[^=]|[^=!<>]=\s')
+
+
+WHITESPACE = ' \t'
+
+BINARY_OPERATORS = frozenset(['**=', '*=', '+=', '-=', '!=', '<>',
+    '%=', '^=', '&=', '|=', '==', '/=', '//=', '<=', '>=', '<<=', '>>=',
+    '%',  '^',  '&',  '|',  '=',  '/',  '//',  '<',  '>',  '<<'])
+UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
+OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS
+SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.INDENT,
+                         tokenize.DEDENT, tokenize.NEWLINE])
+E225NOT_KEYWORDS = (frozenset(keyword.kwlist + ['print']) -
+                    frozenset(['False', 'None', 'True']))
+BENCHMARK_KEYS = ('directories', 'files', 'logical lines', 'physical lines')
+
+options = None
+args = None
+
+
+##############################################################################
+# Plugins (check functions) for physical lines
+##############################################################################
+
+
+def tabs_or_spaces(physical_line, indent_char):
+    r"""
+    Never mix tabs and spaces.
+
+    The most popular way of indenting Python is with spaces only.  The
+    second-most popular way is with tabs only.  Code indented with a mixture
+    of tabs and spaces should be converted to using spaces exclusively.  When
+    invoking the Python command line interpreter with the -t option, it issues
+    warnings about code that illegally mixes tabs and spaces.  When using -tt
+    these warnings become errors.  These options are highly recommended!
+
+    Okay: if a == 0:\n        a = 1\n        b = 1
+    E101: if a == 0:\n        a = 1\n\tb = 1
+    """
+    indent = INDENT_REGEX.match(physical_line).group(1)
+    for offset, char in enumerate(indent):
+        if char != indent_char:
+            return offset, "E101 indentation contains mixed spaces and tabs"
+
+
+def tabs_obsolete(physical_line):
+    r"""
+    For new projects, spaces-only are strongly recommended over tabs.  Most
+    editors have features that make this easy to do.
+
+    Okay: if True:\n    return
+    W191: if True:\n\treturn
+    """
+    indent = INDENT_REGEX.match(physical_line).group(1)
+    if indent.count('\t'):
+        return indent.index('\t'), "W191 indentation contains tabs"
+
+
+def trailing_whitespace(physical_line):
+    r"""
+    JCR: Trailing whitespace is superfluous.
+    FBM: Except when it occurs as part of a blank line (i.e. the line is
+         nothing but whitespace). According to Python docs[1] a line with only
+         whitespace is considered a blank line, and is to be ignored. However,
+         matching a blank line to its indentation level avoids mistakenly
+         terminating a multi-line statement (e.g. class declaration) when
+         pasting code into the standard Python interpreter.
+
+         [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines
+
+    The warning returned varies on whether the line itself is blank, for easier
+    filtering for those who want to indent their blank lines.
+
+    Okay: spam(1)
+    W291: spam(1)\s
+    W293: class Foo(object):\n    \n    bang = 12
+    """
+    physical_line = physical_line.rstrip('\n')    # chr(10), newline
+    physical_line = physical_line.rstrip('\r')    # chr(13), carriage return
+    physical_line = physical_line.rstrip('\x0c')  # chr(12), form feed, ^L
+    stripped = physical_line.rstrip()
+    if physical_line != stripped:
+        if stripped:
+            return len(stripped), "W291 trailing whitespace"
+        else:
+            return 0, "W293 blank line contains whitespace"
+
+
+def trailing_blank_lines(physical_line, lines, line_number):
+    r"""
+    JCR: Trailing blank lines are superfluous.
+
+    Okay: spam(1)
+    W391: spam(1)\n
+    """
+    if physical_line.strip() == '' and line_number == len(lines):
+        return 0, "W391 blank line at end of file"
+
+
+def missing_newline(physical_line):
+    """
+    JCR: The last line should have a newline.
+    """
+    if physical_line.rstrip() == physical_line:
+        return len(physical_line), "W292 no newline at end of file"
+
+
+def maximum_line_length(physical_line):
+    """
+    Limit all lines to a maximum of 79 characters.
+
+    There are still many devices around that are limited to 80 character
+    lines; plus, limiting windows to 80 characters makes it possible to have
+    several windows side-by-side.  The default wrapping on such devices looks
+    ugly.  Therefore, please limit all lines to a maximum of 79 characters.
+    For flowing long blocks of text (docstrings or comments), limiting the
+    length to 72 characters is recommended.
+    """
+    line = physical_line.rstrip()
+    length = len(line)
+    if length > MAX_LINE_LENGTH:
+        try:
+            # The line could contain multi-byte characters
+            if not hasattr(line, 'decode'):   # Python 3
+                line = line.encode('latin-1')
+            length = len(line.decode('utf-8'))
+        except UnicodeDecodeError:
+            pass
+    if length > MAX_LINE_LENGTH:
+        return MAX_LINE_LENGTH, "E501 line too long (%d characters)" % length
+
+
+##############################################################################
+# Plugins (check functions) for logical lines
+##############################################################################
+
+
+def blank_lines(logical_line, blank_lines, indent_level, line_number,
+                previous_logical, previous_indent_level,
+                blank_lines_before_comment):
+    r"""
+    Separate top-level function and class definitions with two blank lines.
+
+    Method definitions inside a class are separated by a single blank line.
+
+    Extra blank lines may be used (sparingly) to separate groups of related
+    functions.  Blank lines may be omitted between a bunch of related
+    one-liners (e.g. a set of dummy implementations).
+
+    Use blank lines in functions, sparingly, to indicate logical sections.
+
+    Okay: def a():\n    pass\n\n\ndef b():\n    pass
+    Okay: def a():\n    pass\n\n\n# Foo\n# Bar\n\ndef b():\n    pass
+
+    E301: class Foo:\n    b = 0\n    def bar():\n        pass
+    E302: def a():\n    pass\n\ndef b(n):\n    pass
+    E303: def a():\n    pass\n\n\n\ndef b(n):\n    pass
+    E303: def a():\n\n\n\n    pass
+    E304: @decorator\n\ndef a():\n    pass
+    """
+    if line_number == 1:
+        return  # Don't expect blank lines before the first line
+    max_blank_lines = max(blank_lines, blank_lines_before_comment)
+    if previous_logical.startswith('@'):
+        if max_blank_lines:
+            return 0, "E304 blank lines found after function decorator"
+    elif max_blank_lines > 2 or (indent_level and max_blank_lines == 2):
+        return 0, "E303 too many blank lines (%d)" % max_blank_lines
+    elif (logical_line.startswith('def ') or
+          logical_line.startswith('class ') or
+          logical_line.startswith('@')):
+        if indent_level:
+            if not (max_blank_lines or previous_indent_level < indent_level or
+                    DOCSTRING_REGEX.match(previous_logical)):
+                return 0, "E301 expected 1 blank line, found 0"
+        elif max_blank_lines != 2:
+            return 0, "E302 expected 2 blank lines, found %d" % max_blank_lines
+
+
+def extraneous_whitespace(logical_line):
+    """
+    Avoid extraneous whitespace in the following situations:
+
+    - Immediately inside parentheses, brackets or braces.
+
+    - Immediately before a comma, semicolon, or colon.
+
+    Okay: spam(ham[1], {eggs: 2})
+    E201: spam( ham[1], {eggs: 2})
+    E201: spam(ham[ 1], {eggs: 2})
+    E201: spam(ham[1], { eggs: 2})
+    E202: spam(ham[1], {eggs: 2} )
+    E202: spam(ham[1 ], {eggs: 2})
+    E202: spam(ham[1], {eggs: 2 })
+
+    E203: if x == 4: print x, y; x, y = y , x
+    E203: if x == 4: print x, y ; x, y = y, x
+    E203: if x == 4 : print x, y; x, y = y, x
+    """
+    line = logical_line
+    for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
+        text = match.group()
+        char = text.strip()
+        found = match.start()
+        if text == char + ' ' and char in '([{':
+            return found + 1, "E201 whitespace after '%s'" % char
+        if text == ' ' + char and line[found - 1] != ',':
+            if char in '}])':
+                return found, "E202 whitespace before '%s'" % char
+            if char in ',;:':
+                return found, "E203 whitespace before '%s'" % char
+
+
+def missing_whitespace(logical_line):
+    """
+    JCR: Each comma, semicolon or colon should be followed by whitespace.
+
+    Okay: [a, b]
+    Okay: (3,)
+    Okay: a[1:4]
+    Okay: a[:4]
+    Okay: a[1:]
+    Okay: a[1:4:2]
+    E231: ['a','b']
+    E231: foo(bar,baz)
+    """
+    line = logical_line
+    for index in range(len(line) - 1):
+        char = line[index]
+        if char in ',;:' and line[index + 1] not in WHITESPACE:
+            before = line[:index]
+            if char == ':' and before.count('[') > before.count(']'):
+                continue  # Slice syntax, no space required
+            if char == ',' and line[index + 1] == ')':
+                continue  # Allow tuple with only one element: (3,)
+            return index, "E231 missing whitespace after '%s'" % char
+
+
+def indentation(logical_line, previous_logical, indent_char,
+                indent_level, previous_indent_level):
+    r"""
+    Use 4 spaces per indentation level.
+
+    For really old code that you don't want to mess up, you can continue to
+    use 8-space tabs.
+
+    Okay: a = 1
+    Okay: if a == 0:\n    a = 1
+    E111:   a = 1
+
+    Okay: for item in items:\n    pass
+    E112: for item in items:\npass
+
+    Okay: a = 1\nb = 2
+    E113: a = 1\n    b = 2
+    """
+    if indent_char == ' ' and indent_level % 4:
+        return 0, "E111 indentation is not a multiple of four"
+    indent_expect = previous_logical.endswith(':')
+    if indent_expect and indent_level <= previous_indent_level:
+        return 0, "E112 expected an indented block"
+    if indent_level > previous_indent_level and not indent_expect:
+        return 0, "E113 unexpected indentation"
+
+
+def whitespace_before_parameters(logical_line, tokens):
+    """
+    Avoid extraneous whitespace in the following situations:
+
+    - Immediately before the open parenthesis that starts the argument
+      list of a function call.
+
+    - Immediately before the open parenthesis that starts an indexing or
+      slicing.
+
+    Okay: spam(1)
+    E211: spam (1)
+
+    Okay: dict['key'] = list[index]
+    E211: dict ['key'] = list[index]
+    E211: dict['key'] = list [index]
+    """
+    prev_type = tokens[0][0]
+    prev_text = tokens[0][1]
+    prev_end = tokens[0][3]
+    for index in range(1, len(tokens)):
+        token_type, text, start, end, line = tokens[index]
+        if (token_type == tokenize.OP and
+            text in '([' and
+            start != prev_end and
+            (prev_type == tokenize.NAME or prev_text in '}])') and
+            # Syntax "class A (B):" is allowed, but avoid it
+            (index < 2 or tokens[index - 2][1] != 'class') and
+            # Allow "return (a.foo for a in range(5))"
+            (not keyword.iskeyword(prev_text))):
+            return prev_end, "E211 whitespace before '%s'" % text
+        prev_type = token_type
+        prev_text = text
+        prev_end = end
+
+
+def whitespace_around_operator(logical_line):
+    """
+    Avoid extraneous whitespace in the following situations:
+
+    - More than one space around an assignment (or other) operator to
+      align it with another.
+
+    Okay: a = 12 + 3
+    E221: a = 4  + 5
+    E222: a = 4 +  5
+    E223: a = 4\t+ 5
+    E224: a = 4 +\t5
+    """
+    for match in WHITESPACE_AROUND_OPERATOR_REGEX.finditer(logical_line):
+        before, whitespace, after = match.groups()
+        tab = whitespace == '\t'
+        offset = match.start(2)
+        if before in OPERATORS:
+            return offset, (tab and "E224 tab after operator" or
+                            "E222 multiple spaces after operator")
+        elif after in OPERATORS:
+            return offset, (tab and "E223 tab before operator" or
+                            "E221 multiple spaces before operator")
+
+
+def missing_whitespace_around_operator(logical_line, tokens):
+    r"""
+    - Always surround these binary operators with a single space on
+      either side: assignment (=), augmented assignment (+=, -= etc.),
+      comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not),
+      Booleans (and, or, not).
+
+    - Use spaces around arithmetic operators.
+
+    Okay: i = i + 1
+    Okay: submitted += 1
+    Okay: x = x * 2 - 1
+    Okay: hypot2 = x * x + y * y
+    Okay: c = (a + b) * (a - b)
+    Okay: foo(bar, key='word', *args, **kwargs)
+    Okay: baz(**kwargs)
+    Okay: negative = -1
+    Okay: spam(-1)
+    Okay: alpha[:-i]
+    Okay: if not -5 < x < +5:\n    pass
+    Okay: lambda *args, **kw: (args, kw)
+
+    E225: i=i+1
+    E225: submitted +=1
+    E225: x = x*2 - 1
+    E225: hypot2 = x*x + y*y
+    E225: c = (a+b) * (a-b)
+    E225: c = alpha -4
+    E225: z = x **y
+    """
+    parens = 0
+    need_space = False
+    prev_type = tokenize.OP
+    prev_text = prev_end = None
+    for token_type, text, start, end, line in tokens:
+        if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN):
+            # ERRORTOKEN is triggered by backticks in Python 3000
+            continue
+        if text in ('(', 'lambda'):
+            parens += 1
+        elif text == ')':
+            parens -= 1
+        if need_space:
+            if start != prev_end:
+                need_space = False
+            elif text == '>' and prev_text == '<':
+                # Tolerate the "<>" operator, even if running Python 3
+                pass
+            else:
+                return prev_end, "E225 missing whitespace around operator"
+        elif token_type == tokenize.OP and prev_end is not None:
+            if text == '=' and parens:
+                # Allow keyword args or defaults: foo(bar=None).
+                pass
+            elif text in BINARY_OPERATORS:
+                need_space = True
+            elif text in UNARY_OPERATORS:
+                # Allow unary operators: -123, -x, +1.
+                # Allow argument unpacking: foo(*args, **kwargs).
+                if prev_type == tokenize.OP:
+                    if prev_text in '}])':
+                        need_space = True
+                elif prev_type == tokenize.NAME:
+                    if prev_text not in E225NOT_KEYWORDS:
+                        need_space = True
+                else:
+                    need_space = True
+            if need_space and start == prev_end:
+                return prev_end, "E225 missing whitespace around operator"
+        prev_type = token_type
+        prev_text = text
+        prev_end = end
+
+
+def whitespace_around_comma(logical_line):
+    """
+    Avoid extraneous whitespace in the following situations:
+
+    - More than one space around an assignment (or other) operator to
+      align it with another.
+
+    JCR: This should also be applied around comma etc.
+    Note: these checks are disabled by default
+
+    Okay: a = (1, 2)
+    E241: a = (1,  2)
+    E242: a = (1,\t2)
+    """
+    line = logical_line
+    for separator in ',;:':
+        found = line.find(separator + '  ')
+        if found > -1:
+            return found + 1, "E241 multiple spaces after '%s'" % separator
+        found = line.find(separator + '\t')
+        if found > -1:
+            return found + 1, "E242 tab after '%s'" % separator
+
+
+def whitespace_around_named_parameter_equals(logical_line):
+    """
+    Don't use spaces around the '=' sign when used to indicate a
+    keyword argument or a default parameter value.
+
+    Okay: def complex(real, imag=0.0):
+    Okay: return magic(r=real, i=imag)
+    Okay: boolean(a == b)
+    Okay: boolean(a != b)
+    Okay: boolean(a <= b)
+    Okay: boolean(a >= b)
+
+    E251: def complex(real, imag = 0.0):
+    E251: return magic(r = real, i = imag)
+    """
+    parens = 0
+    for match in WHITESPACE_AROUND_NAMED_PARAMETER_REGEX.finditer(
+            logical_line):
+        text = match.group()
+        if parens and len(text) == 3:
+            issue = "E251 no spaces around keyword / parameter equals"
+            return match.start(), issue
+        if text == '(':
+            parens += 1
+        elif text == ')':
+            parens -= 1
+
+
+def whitespace_before_inline_comment(logical_line, tokens):
+    """
+    Separate inline comments by at least two spaces.
+
+    An inline comment is a comment on the same line as a statement.  Inline
+    comments should be separated by at least two spaces from the statement.
+    They should start with a # and a single space.
+
+    Okay: x = x + 1  # Increment x
+    Okay: x = x + 1    # Increment x
+    E261: x = x + 1 # Increment x
+    E262: x = x + 1  #Increment x
+    E262: x = x + 1  #  Increment x
+    """
+    prev_end = (0, 0)
+    for token_type, text, start, end, line in tokens:
+        if token_type == tokenize.NL:
+            continue
+        if token_type == tokenize.COMMENT:
+            if not line[:start[1]].strip():
+                continue
+            if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
+                return (prev_end,
+                        "E261 at least two spaces before inline comment")
+            if (len(text) > 1 and text.startswith('#  ')
+                           or not text.startswith('# ')):
+                return start, "E262 inline comment should start with '# '"
+        else:
+            prev_end = end
+
+
+def imports_on_separate_lines(logical_line):
+    r"""
+    Imports should usually be on separate lines.
+
+    Okay: import os\nimport sys
+    E401: import sys, os
+
+    Okay: from subprocess import Popen, PIPE
+    Okay: from myclas import MyClass
+    Okay: from foo.bar.yourclass import YourClass
+    Okay: import myclass
+    Okay: import foo.bar.yourclass
+    """
+    line = logical_line
+    if line.startswith('import '):
+        found = line.find(',')
+        if found > -1:
+            return found, "E401 multiple imports on one line"
+
+
+def compound_statements(logical_line):
+    r"""
+    Compound statements (multiple statements on the same line) are
+    generally discouraged.
+
+    While sometimes it's okay to put an if/for/while with a small body
+    on the same line, never do this for multi-clause statements. Also
+    avoid folding such long lines!
+
+    Okay: if foo == 'blah':\n    do_blah_thing()
+    Okay: do_one()
+    Okay: do_two()
+    Okay: do_three()
+
+    E701: if foo == 'blah': do_blah_thing()
+    E701: for x in lst: total += x
+    E701: while t < 10: t = delay()
+    E701: if foo == 'blah': do_blah_thing()
+    E701: else: do_non_blah_thing()
+    E701: try: something()
+    E701: finally: cleanup()
+    E701: if foo == 'blah': one(); two(); three()
+
+    E702: do_one(); do_two(); do_three()
+    """
+    line = logical_line
+    found = line.find(':')
+    if -1 < found < len(line) - 1:
+        before = line[:found]
+        if (before.count('{') <= before.count('}') and  # {'a': 1} (dict)
+            before.count('[') <= before.count(']') and  # [1:2] (slice)
+            not re.search(r'\blambda\b', before)):      # lambda x: x
+            return found, "E701 multiple statements on one line (colon)"
+    found = line.find(';')
+    if -1 < found:
+        return found, "E702 multiple statements on one line (semicolon)"
+
+
+def python_3000_has_key(logical_line):
+    """
+    The {}.has_key() method will be removed in the future version of
+    Python. Use the 'in' operation instead, like:
+    d = {"a": 1, "b": 2}
+    if "b" in d:
+        print d["b"]
+    """
+    pos = logical_line.find('.has_key(')
+    if pos > -1:
+        return pos, "W601 .has_key() is deprecated, use 'in'"
+
+
+def python_3000_raise_comma(logical_line):
+    """
+    When raising an exception, use "raise ValueError('message')"
+    instead of the older form "raise ValueError, 'message'".
+
+    The paren-using form is preferred because when the exception arguments
+    are long or include string formatting, you don't need to use line
+    continuation characters thanks to the containing parentheses.  The older
+    form will be removed in Python 3000.
+    """
+    match = RAISE_COMMA_REGEX.match(logical_line)
+    if match:
+        return match.start(1), "W602 deprecated form of raising exception"
+
+
+def python_3000_not_equal(logical_line):
+    """
+    != can also be written <>, but this is an obsolete usage kept for
+    backwards compatibility only. New code should always use !=.
+    The older syntax is removed in Python 3000.
+    """
+    pos = logical_line.find('<>')
+    if pos > -1:
+        return pos, "W603 '<>' is deprecated, use '!='"
+
+
+def python_3000_backticks(logical_line):
+    """
+    Backticks are removed in Python 3000.
+    Use repr() instead.
+    """
+    pos = logical_line.find('`')
+    if pos > -1:
+        return pos, "W604 backticks are deprecated, use 'repr()'"
+
+
+def python_3000_print(logical_line):
+    """
+    Use Python 3 compatible print statement.
+
+    Okay: print('hello %s' % you)
+    E802: print 'hello', you
+    """
+    if logical_line.lstrip().startswith('print '):
+        start = logical_line.find('print')
+        return start, 'E802 not Python 3 compatible, use print(...)'
+
+
+def single_quoted_strings(logical_line, tokens):
+    """
+    Use single quoted strings instead of double quoted, unless the
+    string contains a single quote.
+
+    Okay: s = 'abc'
+    E801: s = "abc"
+    Okay: s = "don't"
+    """
+    for type, text, start, stop, line in tokens:
+        if (type == tokenize.STRING and
+            not text.startswith('"""') and
+            text[0] == '"' and
+            "'" not in text):
+            if len(text) > 30:
+                text = text[:28] + ' ..."'
+            return start, 'E801 double quoted string: %s' % text
+
+
+##############################################################################
+# Helper functions
+##############################################################################
+
+
+if '' == ''.encode():
+    # Python 2: implicit encoding.
+    def readlines(filename):
+        return open(filename).readlines()
+else:
+    # Python 3: decode to latin-1.
+    # This function is lazy, it does not read the encoding declaration.
+    # XXX: use tokenize.detect_encoding()
+    def readlines(filename):
+        return open(filename, encoding='latin-1').readlines()
+
+
+def expand_indent(line):
+    """
+    Return the amount of indentation.
+    Tabs are expanded to the next multiple of 8.
+
+    >>> expand_indent('    ')
+    4
+    >>> expand_indent('\\t')
+    8
+    >>> expand_indent('    \\t')
+    8
+    >>> expand_indent('       \\t')
+    8
+    >>> expand_indent('        \\t')
+    16
+    """
+    result = 0
+    for char in line:
+        if char == '\t':
+            result = result // 8 * 8 + 8
+        elif char == ' ':
+            result += 1
+        else:
+            break
+    return result
+
+
+def mute_string(text):
+    """
+    Replace contents with 'xxx' to prevent syntax matching.
+
+    >>> mute_string('"abc"')
+    '"xxx"'
+    >>> mute_string("'''abc'''")
+    "'''xxx'''"
+    >>> mute_string("r'abc'")
+    "r'xxx'"
+    """
+    start = 1
+    end = len(text) - 1
+    # String modifiers (e.g. u or r)
+    if text.endswith('"'):
+        start += text.index('"')
+    elif text.endswith("'"):
+        start += text.index("'")
+    # Triple quotes
+    if text.endswith('"""') or text.endswith("'''"):
+        start += 2
+        end -= 2
+    return text[:start] + 'x' * (end - start) + text[end:]
+
+
+def message(text):
+    """Print a message."""
+    # print >> sys.stderr, options.prog + ': ' + text
+    # print >> sys.stderr, text
+    print(text)
+
+
+##############################################################################
+# Framework to run all checks
+##############################################################################
+
+
+def find_checks(argument_name):
+    """
+    Find all globally visible functions where the first argument name
+    starts with argument_name.
+    """
+    checks = []
+    for name, function in globals().items():
+        if not inspect.isfunction(function):
+            continue
+        args = inspect.getargspec(function)[0]
+        if args and args[0].startswith(argument_name):
+            codes = ERRORCODE_REGEX.findall(inspect.getdoc(function) or '')
+            for code in codes or ['']:
+                if not code or not ignore_code(code):
+                    checks.append((name, function, args))
+                    break
+    checks.sort()
+    return checks
+
+
+class Checker(object):
+    """
+    Load a Python source file, tokenize it, check coding style.
+    """
+
+    def __init__(self, filename, lines=None):
+        self.filename = filename
+        if filename is None:
+            self.filename = 'stdin'
+            self.lines = lines or []
+        elif lines is None:
+            self.lines = readlines(filename)
+        else:
+            self.lines = lines
+        options.counters['physical lines'] += len(self.lines)
+
+    def readline(self):
+        """
+        Get the next line from the input buffer.
+        """
+        self.line_number += 1
+        if self.line_number > len(self.lines):
+            return ''
+        return self.lines[self.line_number - 1]
+
+    def readline_check_physical(self):
+        """
+        Check and return the next physical line. This method can be
+        used to feed tokenize.generate_tokens.
+        """
+        line = self.readline()
+        if line:
+            self.check_physical(line)
+        return line
+
+    def run_check(self, check, argument_names):
+        """
+        Run a check plugin.
+        """
+        arguments = []
+        for name in argument_names:
+            arguments.append(getattr(self, name))
+        return check(*arguments)
+
+    def check_physical(self, line):
+        """
+        Run all physical checks on a raw input line.
+        """
+        self.physical_line = line
+        if self.indent_char is None and len(line) and line[0] in ' \t':
+            self.indent_char = line[0]
+        for name, check, argument_names in options.physical_checks:
+            result = self.run_check(check, argument_names)
+            if result is not None:
+                offset, text = result
+                self.report_error(self.line_number, offset, text, check)
+
+    def build_tokens_line(self):
+        """
+        Build a logical line from tokens.
+        """
+        self.mapping = []
+        logical = []
+        length = 0
+        previous = None
+        for token in self.tokens:
+            token_type, text = token[0:2]
+            if token_type in SKIP_TOKENS:
+                continue
+            if token_type == tokenize.STRING:
+                text = mute_string(text)
+            if previous:
+                end_line, end = previous[3]
+                start_line, start = token[2]
+                if end_line != start_line:  # different row
+                    prev_text = self.lines[end_line - 1][end - 1]
+                    if prev_text == ',' or (prev_text not in '{[('
+                                            and text not in '}])'):
+                        logical.append(' ')
+                        length += 1
+                elif end != start:  # different column
+                    fill = self.lines[end_line - 1][end:start]
+                    logical.append(fill)
+                    length += len(fill)
+            self.mapping.append((length, token))
+            logical.append(text)
+            length += len(text)
+            previous = token
+        self.logical_line = ''.join(logical)
+        assert self.logical_line.lstrip() == self.logical_line
+        assert self.logical_line.rstrip() == self.logical_line
+
+    def check_logical(self):
+        """
+        Build a line from tokens and run all logical checks on it.
+        """
+        options.counters['logical lines'] += 1
+        self.build_tokens_line()
+        first_line = self.lines[self.mapping[0][1][2][0] - 1]
+        indent = first_line[:self.mapping[0][1][2][1]]
+        self.previous_indent_level = self.indent_level
+        self.indent_level = expand_indent(indent)
+        if options.verbose >= 2:
+            print(self.logical_line[:80].rstrip())
+        for name, check, argument_names in options.logical_checks:
+            if options.verbose >= 4:
+                print('   ' + name)
+            result = self.run_check(check, argument_names)
+            if result is not None:
+                offset, text = result
+                if isinstance(offset, tuple):
+                    original_number, original_offset = offset
+                else:
+                    for token_offset, token in self.mapping:
+                        if offset >= token_offset:
+                            original_number = token[2][0]
+                            original_offset = (token[2][1]
+                                               + offset - token_offset)
+                self.report_error(original_number, original_offset,
+                                  text, check)
+        self.previous_logical = self.logical_line
+
+    def check_all(self, expected=None, line_offset=0):
+        """
+        Run all checks on the input file.
+        """
+        self.expected = expected or ()
+        self.line_offset = line_offset
+        self.line_number = 0
+        self.file_errors = 0
+        self.indent_char = None
+        self.indent_level = 0
+        self.previous_logical = ''
+        self.blank_lines = 0
+        self.blank_lines_before_comment = 0
+        self.tokens = []
+        parens = 0
+        for token in tokenize.generate_tokens(self.readline_check_physical):
+            if options.verbose >= 3:
+                if token[2][0] == token[3][0]:
+                    pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
+                else:
+                    pos = 'l.%s' % token[3][0]
+                print('l.%s\t%s\t%s\t%r' %
+                    (token[2][0], pos, tokenize.tok_name[token[0]], token[1]))
+            self.tokens.append(token)
+            token_type, text = token[0:2]
+            if token_type == tokenize.OP and text in '([{':
+                parens += 1
+            if token_type == tokenize.OP and text in '}])':
+                parens -= 1
+            if token_type == tokenize.NEWLINE and not parens:
+                self.check_logical()
+                self.blank_lines = 0
+                self.blank_lines_before_comment = 0
+                self.tokens = []
+            if token_type == tokenize.NL and not parens:
+                if len(self.tokens) <= 1:
+                    # The physical line contains only this token.
+                    self.blank_lines += 1
+                self.tokens = []
+            if token_type == tokenize.COMMENT:
+                source_line = token[4]
+                token_start = token[2][1]
+                if source_line[:token_start].strip() == '':
+                    self.blank_lines_before_comment = max(self.blank_lines,
+                        self.blank_lines_before_comment)
+                    self.blank_lines = 0
+                if text.endswith('\n') and not parens:
+                    # The comment also ends a physical line.  This works around
+                    # Python < 2.6 behaviour, which does not generate NL after
+                    # a comment which is on a line by itself.
+                    self.tokens = []
+        return self.file_errors
+
+    def report_error(self, line_number, offset, text, check):
+        """
+        Report an error, according to options.
+        """
+        code = text[:4]
+        if ignore_code(code):
+            return
+        if options.quiet == 1 and not self.file_errors:
+            message(self.filename)
+        if code in options.counters:
+            options.counters[code] += 1
+        else:
+            options.counters[code] = 1
+            options.messages[code] = text[5:]
+        if options.quiet or code in self.expected:
+            # Don't care about expected errors or warnings
+            return
+        self.file_errors += 1
+        if options.counters[code] == 1 or options.repeat:
+            message("%s:%s:%d: %s" %
+                    (self.filename, self.line_offset + line_number,
+                     offset + 1, text))
+            if options.show_source:
+                line = self.lines[line_number - 1]
+                message(line.rstrip())
+                message(' ' * offset + '^')
+            if options.show_pep8:
+                message(check.__doc__.lstrip('\n').rstrip())
+
+
+def input_file(filename):
+    """
+    Run all checks on a Python source file.
+    """
+    if options.verbose:
+        message('checking ' + filename)
+    errors = Checker(filename).check_all()
+
+
+def input_dir(dirname, runner=None):
+    """
+    Check all Python source files in this directory and all subdirectories.
+    """
+    dirname = dirname.rstrip('/')
+    if excluded(dirname):
+        return
+    if runner is None:
+        runner = input_file
+    for root, dirs, files in os.walk(dirname):
+        if options.verbose:
+            message('directory ' + root)
+        options.counters['directories'] += 1
+        dirs.sort()
+        for subdir in dirs:
+            if excluded(subdir):
+                dirs.remove(subdir)
+        files.sort()
+        for filename in files:
+            if filename_match(filename) and not excluded(filename):
+                options.counters['files'] += 1
+                runner(os.path.join(root, filename))
+
+
+def excluded(filename):
+    """
+    Check if options.exclude contains a pattern that matches filename.
+    """
+    basename = os.path.basename(filename)
+    for pattern in options.exclude:
+        if fnmatch(basename, pattern):
+            # print basename, 'excluded because it matches', pattern
+            return True
+
+
+def filename_match(filename):
+    """
+    Check if options.filename contains a pattern that matches filename.
+    If options.filename is unspecified, this always returns True.
+    """
+    if not options.filename:
+        return True
+    for pattern in options.filename:
+        if fnmatch(filename, pattern):
+            return True
+
+
+def ignore_code(code):
+    """
+    Check if options.ignore contains a prefix of the error code.
+    If options.select contains a prefix of the error code, do not ignore it.
+    """
+    for select in options.select:
+        if code.startswith(select):
+            return False
+    for ignore in options.ignore:
+        if code.startswith(ignore):
+            return True
+
+
+def reset_counters():
+    for key in list(options.counters.keys()):
+        if key not in BENCHMARK_KEYS:
+            del options.counters[key]
+    options.messages = {}
+
+
+def get_error_statistics():
+    """Get error statistics."""
+    return get_statistics("E")
+
+
+def get_warning_statistics():
+    """Get warning statistics."""
+    return get_statistics("W")
+
+
+def get_statistics(prefix=''):
+    """
+    Get statistics for message codes that start with the prefix.
+
+    prefix='' matches all errors and warnings
+    prefix='E' matches all errors
+    prefix='W' matches all warnings
+    prefix='E4' matches all errors that have to do with imports
+    """
+    stats = []
+    keys = list(options.messages.keys())
+    keys.sort()
+    for key in keys:
+        if key.startswith(prefix):
+            stats.append('%-7s %s %s' %
+                         (options.counters[key], key, options.messages[key]))
+    return stats
+
+
+def get_count(prefix=''):
+    """Return the total count of errors and warnings."""
+    keys = list(options.messages.keys())
+    count = 0
+    for key in keys:
+        if key.startswith(prefix):
+            count += options.counters[key]
+    return count
+
+
+def print_statistics(prefix=''):
+    """Print overall statistics (number of errors and warnings)."""
+    for line in get_statistics(prefix):
+        print(line)
+
+
+def print_benchmark(elapsed):
+    """
+    Print benchmark numbers.
+    """
+    print('%-7.2f %s' % (elapsed, 'seconds elapsed'))
+    for key in BENCHMARK_KEYS:
+        print('%-7d %s per second (%d total)' % (
+            options.counters[key] / elapsed, key,
+            options.counters[key]))
+
+
+def run_tests(filename):
+    """
+    Run all the tests from a file.
+
+    A test file can provide many tests.  Each test starts with a declaration.
+    This declaration is a single line starting with '#:'.
+    It declares codes of expected failures, separated by spaces or 'Okay'
+    if no failure is expected.
+    If the file does not contain such declaration, it should pass all tests.
+    If the declaration is empty, following lines are not checked, until next
+    declaration.
+
+    Examples:
+
+     * Only E224 and W701 are expected:         #: E224 W701
+     * Following example is conform:            #: Okay
+     * Don't check these lines:                 #:
+    """
+    lines = readlines(filename) + ['#:\n']
+    line_offset = 0
+    codes = ['Okay']
+    testcase = []
+    for index, line in enumerate(lines):
+        if not line.startswith('#:'):
+            if codes:
+                # Collect the lines of the test case
+                testcase.append(line)
+            continue
+        if codes and index > 0:
+            label = '%s:%s:1' % (filename, line_offset + 1)
+            codes = [c for c in codes if c != 'Okay']
+            # Run the checker
+            errors = Checker(filename, testcase).check_all(codes, line_offset)
+            # Check if the expected errors were found
+            for code in codes:
+                if not options.counters.get(code):
+                    errors += 1
+                    message('%s: error %s not found' % (label, code))
+            if options.verbose and not errors:
+                message('%s: passed (%s)' % (label, ' '.join(codes)))
+            # Keep showing errors for multiple tests
+            reset_counters()
+        # output the real line numbers
+        line_offset = index
+        # configure the expected errors
+        codes = line.split()[1:]
+        # empty the test case buffer
+        del testcase[:]
+
+
+def selftest():
+    """
+    Test all check functions with test cases in docstrings.
+    """
+    count_passed = 0
+    count_failed = 0
+    checks = options.physical_checks + options.logical_checks
+    for name, check, argument_names in checks:
+        for line in check.__doc__.splitlines():
+            line = line.lstrip()
+            match = SELFTEST_REGEX.match(line)
+            if match is None:
+                continue
+            code, source = match.groups()
+            checker = Checker(None)
+            for part in source.split(r'\n'):
+                part = part.replace(r'\t', '\t')
+                part = part.replace(r'\s', ' ')
+                checker.lines.append(part + '\n')
+            options.quiet = 2
+            checker.check_all()
+            error = None
+            if code == 'Okay':
+                if len(options.counters) > len(BENCHMARK_KEYS):
+                    codes = [key for key in options.counters.keys()
+                             if key not in BENCHMARK_KEYS]
+                    error = "incorrectly found %s" % ', '.join(codes)
+            elif not options.counters.get(code):
+                error = "failed to find %s" % code
+            # Reset the counters
+            reset_counters()
+            if not error:
+                count_passed += 1
+            else:
+                count_failed += 1
+                if len(checker.lines) == 1:
+                    print("pep8.py: %s: %s" %
+                          (error, checker.lines[0].rstrip()))
+                else:
+                    print("pep8.py: %s:" % error)
+                    for line in checker.lines:
+                        print(line.rstrip())
+    if options.verbose:
+        print("%d passed and %d failed." % (count_passed, count_failed))
+        if count_failed:
+            print("Test failed.")
+        else:
+            print("Test passed.")
+
+
+def process_options(arglist=None):
+    """
+    Process options passed either via arglist or via command line args.
+    """
+    global options, args
+    parser = OptionParser(version=__version__,
+                          usage="%prog [options] input ...")
+    parser.add_option('-v', '--verbose', default=0, action='count',
+                      help="print status messages, or debug with -vv")
+    parser.add_option('-q', '--quiet', default=0, action='count',
+                      help="report only file names, or nothing with -qq")
+    parser.add_option('-r', '--repeat', action='store_true',
+                      help="show all occurrences of the same error")
+    parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE,
+                      help="exclude files or directories which match these "
+                        "comma separated patterns (default: %s)" %
+                        DEFAULT_EXCLUDE)
+    parser.add_option('--filename', metavar='patterns', default='*.py',
+                      help="when parsing directories, only check filenames "
+                        "matching these comma separated patterns (default: "
+                        "*.py)")
+    parser.add_option('--select', metavar='errors', default='',
+                      help="select errors and warnings (e.g. E,W6)")
+    parser.add_option('--ignore', metavar='errors', default='',
+                      help="skip errors and warnings (e.g. E4,W)")
+    parser.add_option('--show-source', action='store_true',
+                      help="show source code for each error")
+    parser.add_option('--show-pep8', action='store_true',
+                      help="show text of PEP 8 for each error")
+    parser.add_option('--statistics', action='store_true',
+                      help="count errors and warnings")
+    parser.add_option('--count', action='store_true',
+                      help="print total number of errors and warnings "
+                        "to standard error and set exit code to 1 if "
+                        "total is not null")
+    parser.add_option('--benchmark', action='store_true',
+                      help="measure processing speed")
+    parser.add_option('--testsuite', metavar='dir',
+                      help="run regression tests from dir")
+    parser.add_option('--doctest', action='store_true',
+                      help="run doctest on myself")
+    options, args = parser.parse_args(arglist)
+    if options.testsuite:
+        args.append(options.testsuite)
+    if not args and not options.doctest:
+        parser.error('input not specified')
+    options.prog = os.path.basename(sys.argv[0])
+    options.exclude = options.exclude.split(',')
+    for index in range(len(options.exclude)):
+        options.exclude[index] = options.exclude[index].rstrip('/')
+    if options.filename:
+        options.filename = options.filename.split(',')
+    if options.select:
+        options.select = options.select.split(',')
+    else:
+        options.select = []
+    if options.ignore:
+        options.ignore = options.ignore.split(',')
+    elif options.select:
+        # Ignore all checks which are not explicitly selected
+        options.ignore = ['']
+    elif options.testsuite or options.doctest:
+        # For doctest and testsuite, all checks are required
+        options.ignore = []
+    else:
+        # The default choice: ignore controversial checks
+        options.ignore = DEFAULT_IGNORE.split(',')
+    options.physical_checks = find_checks('physical_line')
+    options.logical_checks = find_checks('logical_line')
+    options.counters = dict.fromkeys(BENCHMARK_KEYS, 0)
+    options.messages = {}
+    return options, args
+
+
+def _main():
+    """
+    Parse options and run checks on Python source.
+    """
+    options, args = process_options()
+    if options.doctest:
+        import doctest
+        doctest.testmod(verbose=options.verbose)
+        selftest()
+    if options.testsuite:
+        runner = run_tests
+    else:
+        runner = input_file
+    start_time = time.time()
+    for path in args:
+        if os.path.isdir(path):
+            input_dir(path, runner=runner)
+        elif not excluded(path):
+            options.counters['files'] += 1
+            runner(path)
+    elapsed = time.time() - start_time
+    if options.statistics:
+        print_statistics()
+    if options.benchmark:
+        print_benchmark(elapsed)
+    count = get_count()
+    if count:
+        if options.count:
+            sys.stderr.write(str(count) + '\n')
+        sys.exit(1)
+
+
+if __name__ == '__main__':
+    _main()
diff --git a/tools/sphinx.py b/tools/sphinx.py
new file mode 100644
index 0000000..27b9a90
--- /dev/null
+++ b/tools/sphinx.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+import os
+import sys
+import time
+import glob
+import trace
+import tempfile
+
+tmpdir = tempfile.mkdtemp(prefix='ase-')
+os.chdir(tmpdir)
+
+def build(email):
+    if os.system('svn checkout ' +
+                 'https://svn.fysik.dtu.dk/projects/ase/trunk ase') != 0:
+        raise RuntimeError('Checkout of ASE failed!')
+    os.chdir('ase')
+    if os.system('python setup.py install --home=.') != 0:
+        raise RuntimeError('Installation failed!')
+    sys.path.insert(0, 'lib/python')
+    from ase.test import test
+    from ase.version import version
+
+    # Run test-suite:
+    stream = open('test-results.txt', 'w')
+    results = test(verbosity=2, dir='ase/test', display=False, stream=stream)
+    stream.close()
+    if len(results.failures) > 0 or len(results.errors) > 0:
+        address = email
+        subject = 'ASE test-suite failed!'
+        os.system('mail -s "%s" %s < %s' %
+                  (subject, address, 'test-results.txt'))
+        raise RuntimeError('Testsuite failed!')
+
+    # Generate tar-file:
+    assert os.system('python setup.py sdist') == 0
+
+    if os.system('epydoc --docformat restructuredtext --parse-only ' +
+                 '--name ASE ' +
+                 '--url http://wiki.fysik.dtu.dk/ase ' +
+                 '--show-imports --no-frames -v ase &> epydoc.out') != 0:
+        raise RuntimeError('Epydoc failed!')
+
+    epydoc_errors = open('epydoc.out').read()
+    if ' Warning:' in epydoc_errors:
+        sys.stderr.write(epydoc_errors)
+
+    os.chdir('doc')
+    os.mkdir('_build')
+    if os.system('PYTHONPATH=%s/ase sphinx-build . _build' % tmpdir) != 0:
+        raise RuntimeError('Sphinx failed!')
+    os.system('cd _build; cp _static/searchtools.js .; ' +
+              'sed -i s/snapshot.tar/%s.tar/ download.html' % version)
+
+    if 1:
+        if os.system('PYTHONPATH=%s/ase ' % tmpdir +
+                     'sphinx-build -b latex . _build 2> error') != 0:
+            raise RuntimeError('Sphinx failed!')
+        os.system(
+            'grep -v "WARNING: unusable reference target found" error 1>&2')
+        
+        os.chdir('_build')
+        #os.system('cd ../..; ln -s doc/_static')
+        if os.system('make ase-manual.pdf 2>&1') != 0:
+            raise RuntimeError('pdflatex failed!')
+    else:
+        os.chdir('_build')
+
+    assert os.system('mv ../../html epydoc;' +
+                     'mv ../../dist/python-ase-%s.tar.gz .' % version) == 0
+    
+tarfiledir = None
+if len(sys.argv) == 3:
+    tarfiledir = sys.argv[2]
+    try:
+        os.remove(tarfiledir + '/ase-webpages.tar.gz')
+    except OSError:
+        pass
+
+build(sys.argv[1])
+    
+if tarfiledir is not None:
+    os.system('cd ..; tar czf %s/ase-webpages.tar.gz _build' % tarfiledir)
+    os.system('cd; rm -r ' + tmpdir)
diff --git a/tools/testase b/tools/testase
new file mode 100644
index 0000000..945f5f3
--- /dev/null
+++ b/tools/testase
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+from optparse import OptionParser
+
+description = """Run ASE test suite.  ***WARNING***: This will leave a
+large number of files in current working directory, so be sure to do
+it in a new directory!"""
+
+p = OptionParser(usage='%prog [OPTION]', description=description)
+p.add_option('--no-display', action='store_true',
+             help='do not open graphical windows')
+
+opts, args = p.parse_args()
+if len(args) != 0:
+    raise p.error('Unexpected arguments: %s' % args)
+
+from ase.test import test
+test(2, display=not opts.no_display)
diff --git a/tools/testase.py b/tools/testase.py
new file mode 100755
index 0000000..560c4d3
--- /dev/null
+++ b/tools/testase.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+from sys import argv
+display = (len(argv) != 2 or argv[1] != '--no-display')
+from ase.test import test
+test(2, display=display)
diff --git a/tools/trajectoryinfo b/tools/trajectoryinfo
new file mode 100755
index 0000000..ae54847
--- /dev/null
+++ b/tools/trajectoryinfo
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+import os
+from optparse import OptionParser
+from ase.io.trajectory import print_trajectory_info
+from ase.io.bundletrajectory import print_bundletrajectory_info
+
+description = 'Print summary of information from trajectory files.'
+
+p = OptionParser(usage='%prog file.traj [file2.traj ...]',
+                 description=description)
+
+opts, args = p.parse_args()
+
+if len(args) == 0:
+   p.print_help()
+   raise SystemExit(1)
+
+for f in args:
+   if os.path.isfile(f):
+      print_trajectory_info(f)
+   elif os.path.isdir(f):
+      print_bundletrajectory_info(f)
+   else:
+      print "ERROR: %s is neither a file nor a directory!" % (f,)
+      
diff --git a/tutorials/N2Ru-Association.py b/tutorials/N2Ru-Association.py
new file mode 100644
index 0000000..1de709a
--- /dev/null
+++ b/tutorials/N2Ru-Association.py
@@ -0,0 +1,44 @@
+from ase import *
+
+#from math import sqrt
+
+#from ASE.Calculators.PairPotential import PairPotential
+#from ASE.Filters.Subset import Subset
+#from ASE.Filters.FixBondLength import FixBondLength
+#from ASE.Dynamics.MDMin import MDMin
+#from ASE.Trajectories.NetCDFTrajectory import NetCDFTrajectory
+#from ASE.IO.NetCDF import ReadNetCDF
+
+slab = read('2N.traj')
+slab.set_calculator(EMT())
+
+constraint = FixBondLength(-2, -1)
+#mask=[atom.symbol != 'N' for atom in slab])
+
+#molecule = Subset(slab, indices=[-2, -1])
+
+# Create a trajectory for the dissociation path:
+path = PickleTrajectory('association.traj', slab)
+# Put the initial state in the trajectory:
+path.write()
+
+# From now on, we relax the molecule under the constraint of fixed
+# bond length:
+#fixed = FixBondLength(molecule, 0, 1)
+#relax = MDMin(fixed, dt=0.08, fmax=0.05)
+
+relax = QuasiNewton(slab)
+
+d = linalg.norm(slab[-2].position - slab[-1].position)
+#d = fixed.GetBondLength()
+delta = 0.1
+e0 = slab.get_potential_energy()
+print d, 0.0
+while d > 1.10:
+    d -= delta
+    fixed.SetBondLength(d, fix='first')
+    # XXX
+    raise NotImplementedError
+    relax.run(fmax=.05)
+    path.write()
+    print d, slab.GetPotentialEnergy() - e0
diff --git a/tutorials/N2Ru-Dissociation1.py b/tutorials/N2Ru-Dissociation1.py
new file mode 100644
index 0000000..2a90a02
--- /dev/null
+++ b/tutorials/N2Ru-Dissociation1.py
@@ -0,0 +1,47 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.constraints import FixAtoms
+from ase.optimize import QuasiNewton
+from ase.io import write
+
+# Find the initial and final states for the reaction.
+
+# Set up a (3 x 3) two layer slab of Ru:
+a = 2.70
+c = 1.59 * a
+sqrt3 = 3. ** .5
+bulk = Atoms('2Cu', [(0., 0., 0.), (1./3, 1./3, -0.5*c)],
+             tags=(1, 1),
+             pbc=(1, 1, 0))
+bulk.set_cell([(a,     0,              0),
+               (a / 2, sqrt3 * a / 2, 0),
+               (0,     0,              1)])
+slab = bulk.repeat((4, 4, 1))
+
+# Initial state.
+# Add the molecule:
+x = a / 2.
+y = a * 3. ** .5 / 6.
+z = 1.8
+d = 1.10 # N2 bond length
+
+# Molecular state parallel to the surface:
+slab += Atoms('2N', [(x, y, z), (x + sqrt3 * d / 2, y + d / 2, z)])
+
+# Use the EMT calculator for the forces and energies:
+slab.set_calculator(EMT())
+
+# We don't want to worry about the Cu degrees of freedom:
+mask = [atom.symbol == 'Cu' for atom in slab]
+slab.set_constraint(FixAtoms(mask=mask))
+relax = QuasiNewton(slab)
+relax.run(fmax=0.05)
+print 'initial state:', slab.get_potential_energy()
+write('N2.traj', slab)
+
+# Now the final state.
+# Move the second N atom to a neighboring hollow site:
+slab[-1].set_position((x + a, y, z))
+relax.run()
+print 'final state:  ', slab.get_potential_energy()
+write('2N.traj', slab)
diff --git a/tutorials/N2Ru-Dissociation2-ANEB.py b/tutorials/N2Ru-Dissociation2-ANEB.py
new file mode 100644
index 0000000..b017ee1
--- /dev/null
+++ b/tutorials/N2Ru-Dissociation2-ANEB.py
@@ -0,0 +1,57 @@
+from ase import *
+
+# XXX This cannot be converted to ase 3, since we lack ANEB
+raise NotImplementedError
+
+#from ASE.Calculators.PairPotential import PairPotential
+#from ASE.Filters.Subset import Subset
+#from ASE.Dynamics.AdaptiveNudgedElasticBand import AdaptiveNudgedElasticBand
+#from ASE.IO.NetCDF import ReadNetCDF
+import os
+
+# check that N2.nc and 2N.nc exists otherwise run
+# N2Ru-Dissociation1.py
+if not (os.path.exists('N2.nc') and os.path.exists('N2.nc')):
+    os.system('python N2Ru-Dissociation1.py')
+
+initial = read('N2.traj')
+final = read('2N.traj')
+
+configs = [initial.copy() for i in range(4)] + [final]
+
+constraint = FixAtoms(mask=[atom.symbol != 'N' for atom in initial])
+for config in configs:
+    config.set_calculator(EMT())
+    config.set_constraint(constraint)
+    
+band = NEB(configs)
+band.interpolate()
+
+# Create a quickmin object:
+relax = QuasiNewton(band)
+
+
+
+#configs0 = [initial]
+#for i in range(3):
+#    configs0.append(initial.Copy())
+#configs0.append(final)
+#
+#configs = []
+#for config in configs0:
+#    config.SetCalculator(PairPotential())
+#    configs.append(Subset(config, indices=[-2, -1]))
+
+
+# setup the Adaptive Nudged Elastic Band dynamics
+aneb = AdaptiveNudgedElasticBand(configs,prefix='aneb',linear_interpolation=True,maxlevels=1)
+
+aneb.Converge()
+
+# test the anebfit tool
+os.system('anebfit aneb')
+os.system('xmgrace aneb_-1.agr&')
+
+# clean up
+# os.system('rm aneb*conf*')
+
diff --git a/tutorials/N2Ru-Dissociation2.py b/tutorials/N2Ru-Dissociation2.py
new file mode 100644
index 0000000..a802026
--- /dev/null
+++ b/tutorials/N2Ru-Dissociation2.py
@@ -0,0 +1,30 @@
+import numpy as np
+
+from ase.io import read
+from ase.constraints import FixAtoms
+from ase.calculators.emt import EMT
+from ase.neb import NEB
+from ase.optimize import QuasiNewton
+
+initial = read('N2.traj')
+final = read('2N.traj')
+
+configs = [initial.copy() for i in range(8)] + [final]
+
+constraint = FixAtoms(mask=[atom.symbol != 'N' for atom in initial])
+for config in configs:
+    config.set_calculator(EMT())
+    config.set_constraint(constraint)
+    
+band = NEB(configs)
+band.interpolate()
+
+# Create a quickmin object:
+relax = QuasiNewton(band)
+
+relax.run(steps=20)
+
+e0 = initial.get_potential_energy()
+for config in configs:
+    d = config[-2].position - config[-1].position
+    print np.linalg.norm(d), config.get_potential_energy() - e0
diff --git a/tutorials/N2Ru-relax.py b/tutorials/N2Ru-relax.py
new file mode 100644
index 0000000..f9c3b67
--- /dev/null
+++ b/tutorials/N2Ru-relax.py
@@ -0,0 +1,37 @@
+from ase import Atoms
+from ase.calculators.emt import EMT
+from ase.optimize import QuasiNewton
+from ase.constraints import FixAtoms
+
+a = 2.70
+c = 1.59 * a
+h = 1.85
+d = 1.10
+
+slab = Atoms('2Cu', [(0., 0., 0.), (1/3., 1/3., -0.5*c)], 
+             tags=(0, 1),
+             pbc=(1, 1, 0))
+slab.set_cell([(a, 0, 0),
+               (a / 2, 3**0.5 * a / 2, 0),
+               (0, 0, 1)])
+slab = slab.repeat((4, 4, 1))
+slab.set_calculator(EMT())
+mask = [a.tag == 1 for a in slab]
+slab.set_constraint(FixAtoms(mask=mask))
+dyn = QuasiNewton(slab)
+dyn.run(fmax=0.05)
+
+e_slab = slab.get_potential_energy()
+x = slab.positions[0, 2] / (c / 2) * 100
+print 'Relaxation of the top layer: %f %%' % x
+
+molecule = Atoms('2N', positions=[(0., 0., h),
+                                  (0., 0., h + d)])
+molecule.set_calculator(EMT())
+e_N2 = molecule.get_potential_energy()
+slab.extend(molecule)
+
+dyn = QuasiNewton(slab)
+dyn.run(fmax=0.05)
+
+print 'Adsorption energy:', e_slab + e_N2 - slab.get_potential_energy()

-- 
Packaging for python-ase



More information about the debian-science-commits mailing list