pax_global_header00006660000000000000000000000064122630004660014511gustar00rootroot0000000000000052 comment=d2b262ca92fb0cea366c3e6d312dcfbb2213c36c ufl-1.3.0/000077500000000000000000000000001226300046600123005ustar00rootroot00000000000000ufl-1.3.0/.bzrignore000066400000000000000000000000001226300046600142700ustar00rootroot00000000000000ufl-1.3.0/.gitignore000066400000000000000000000000151226300046600142640ustar00rootroot00000000000000*.pyc build/ ufl-1.3.0/COPYING000066400000000000000000001045131226300046600133370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . ufl-1.3.0/COPYING.LESSER000066400000000000000000000167271226300046600143440ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. 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 that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ufl-1.3.0/ChangeLog000066400000000000000000000136411226300046600140570ustar00rootroot000000000000001.3.0 [2014-01-07] - Add cell_avg and facet_avg operators, can be applied to a Coefficient or Argument or restrictions thereof - Fix bug in cofactor: now it is transposed the correct way. - Add cell.min_facet_edge_length - Add cell.max_facet_edge_length - Simplify 0^f -> 0 if f is a non-negative scalar value - Add atan2 function - Allow form+0 -> form 1.2.0 [2013-03-24] - NB! Using shapes such as (1,) and (1,1) instead of () for 1D tensor quantities I, x, grad(f) - Add cell.facet_diameter - Add new concept Domain - Add new concept Region, which is the union of numbered subdomains - Add integration over regions (which may be overlapping by sharing subdomains) - Add integration over everywhere - Add functions cosh, sinh, tanh, Max, Min - Generalize jump(v,n) for rank(v) > 2 - Fix some minor bugs 1.1.0 [2013-01-07] - Add support for pickling of expressions (thanks to Graham Markall) - Add shorthand notation A**2 == inner(A, A), special cased for power 2. - Add support for measure sum notation f*(dx(0) + dx(3)) == f*dx(0) + f*dx(3) - Supporting code for bugfix in PyDOLFIN when comparing test/trial functions - Remove support for tuple form notation as this was ambiguous - Bugfix in quadrature degree estimation, never returning <0 now - Remove use of cmp to accomodate removal from python 3 1.1-alpha-prerelease [2012-11-18] (Not released, snapshot archived with submission of UFL journal paper) - Support adding 0 to forms, allowing sum([a]) - Major memory savings and optimizations. - Some bugfixes. - Add perp operator. - Support nested tuple syntax like MixedElement((U,V),W) - Allow outer(a, b, c, ...) by recursive application from left. - Add simplification f/f -> 1 - Add operators <,>,<=,>= in place of lt,gt,le,ge 1.0.0 [2011-12-07] - No changes since rc1. 1.0-rc1 [2011-11-22] - Added tests covering snippets from UFL chapter in FEniCS book - Added more unit tests - Added operators diag and diag_vector - Added geometric quantities cell.surface_area and cell.facet_area - Fixed rtruediv bug - Fixed bug with derivatives of elements of type Real with unspecified cell 1.0-beta3 [2011-10-26] - Added nabla_grad and nabla_div operators - Added error function erf(x) - Added bessel functions of first and second kind, normal and modified, bessel_J(nu, x), bessel_Y(nu, x), bessel_I(nu, x), bessel_K(nu, x) - Extended derivative() to allow indexed coefficient(s) as differentiation variable - Made *Constant use the 'Real' space instead of 'DG0' - Bugfix in adjoint where test and trial functions were in different spaces - Bugfix in replace where the argument to a grad was replaced with 0 - Bugfix in reconstruction of tensor elements - Some other minor bugfixes 1.0-beta2 [2011-08-11] - Support c*form where c depends on a coefficient in a Real space 1.0-beta [2011-07-08] - Add script ufl-version - Added syntax for associating an arbitrary domain data object with a measure: dss = ds[boundaries]; M = f*dss(1) + g*dss(2) - Added new operators elem_mult, elem_div, elem_pow and elem_op for elementwise application of scalar operators to tensors of equal shape - Added condition operators And(lhs,rhs) and Or(lhs,rhs) and Not(cond) - Fixed support for symmetries in subelements of a mixed element - Add support for specifying derivatives of coefficients to derivative() 0.9.1 [2011-05-16] - Remove set_foo functions in finite element classes - Change license from GPL v3 or later to LGPL v3 or later - Change behavior of preprocess(), form.compute_form_data(), form_data.preprocessed_form - Allowing grad, div, inner, dot, det, inverse on scalars - Simplify Identity(1) -> IntValue(1) automatically - Added Levi-Cevita symbol: e = PermutationSymbol(3); e[i,j,k] - Fix bug with future division behaviour (ufl does not support floor division) - Add subdomain member variables to form class - Allow action on forms of arbitrary rank 0.9.0 [2011-02-23] - Allow jump(Sigma, n) for matrix-valued expression Sigma - Bug fix in scalar curl operator - Bug fix in deviatoric operator 0.5.4 [2010-09-01] - Bug fixes in PartExtracter - Do not import x for coordinate - Add Circumradius to Cell (Cell.circumradius) - Add CellVolume to Cell (Cell.volume) 0.5.3 [2010-07-01] - Rename ElementRestriction --> RestrictedElement - Experimental import of x from tetrahedron - Make lhs/rhs work for resrictions - Redefine operator + for FiniteElements and replace + by * - Rename ElementUnion -> EnrichedElement - Add support for tan() and inverse trigonometric functions 0.5.2 [2010-02-15] - Attach form data to preprocessed form, accessible by form.form_data() 0.5.1 [2010-02-03] - Fix bug in propagate_restriction 0.5.0 [2010-02-01] - Several interface changes in FormData class - Introduce call preprocess(form) to be called at beginning of compilation - Rename BasisFunction --> Argument - Rename Function --> Coefficient 0.4.1 [2009-12-04] - Redefine grad().T --> grad() - New meaning of estimate_max_polynomial_degree - New function estimate_total_polynomial_degree - Allow degree = None and cell = None for elements 0.4.0 [2009-09-23] - Extensions for ElementRestriction (restrict FiniteElement to Cell) - Bug fix for lhs/rhs with list tensor types - Add new log function set_prefix - Add new log function log(level, message) - Added macro cell integral *dE - Added mechanism to add additional integral types - Added LiftingOperator and LiftingFunction - Added ElementRestriction 0.3.0 [2009-05-28] - Some critical bugfixes, in particular in differentiation. - Added form operators "system" and "sensitivity_rhs". - diff can take form as argument, applies to all integrands. - Rudimentary precedence handling for better use of parentheses in str(expression). - Added script ufl2py, mainly for debugging purposes. - Crude implementation of estimate_max_polynomial_degree for quadrature degree estimation. - Improved manual. 0.2.0 [2009-04-07] - Initial release of UFL. 0.1.0 [...] - Unreleased development versions of UFL. ufl-1.3.0/README000066400000000000000000000056521226300046600131700ustar00rootroot00000000000000=========================== UFL - Unified Form Language =========================== The Unified Form Language (UFL) is a domain specific language for declaration of finite element discretizations of variational forms. More precisely, it defines a flexible interface for choosing finite element spaces and defining expressions for weak forms in a notation close to mathematical notation. Authors: | Martin Sandve Alnæs | Anders Logg Contributors: | Kristian B. Ølgaard | Garth N. Wells | Marie E. Rognes | Kent-Andre Mardal | Johan Hake Installation ============ Linux:: sudo python setup.py install Directories =========== - ufl/ All source code for the UFL implementation. - scripts/ Commandline utilities like "ufl-analyse", "ufl-convert" and "form2ufl". - demo/ Several ufl form files which demonstrates the use of the form language. - doc/ The UFL manual resides here. - test/ Unit tests for the UFL implementation. Run all tests by typing "python test.py" inside the test/ directory. - sandbox/ A place for experimental scripts and other unofficial code. Utilities ========= For more information about the utilities, type:: ufl-analyse -h ufl-convert -h form2ufl -h after installation. About the Python modules ======================== The global namespace of the module ufl contains the entire UFL language:: from ufl import * Form compilers may want to import additional implementation details like:: from ufl.classes import * and:: from ufl.algorithms import * Importing a .ufl file can be done easily from Python:: from ufl.algorithms import load_ufl_file filedata = load_ufl_file("filename.ufl") forms = filedata.forms elements = filedata.elements to get lists of forms and elements from the .ufl file, or:: from ufl.algorithms import load_forms forms = load_forms("filename.ufl") to get a list of forms in the .ufl file. Contact ======= Send feature requests and questions to fenics@fenicsproject.org The Git source repository for UFL is located at https://bitbucket.org/fenics-project/ufl and bugs can be registered at https://bitbucket.org/fenics-project/ufl/issues License ======= UFL 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 3 of the License, or (at your option) any later version. UFL 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 UFL. If not, see . ufl-1.3.0/demo/000077500000000000000000000000001226300046600132245ustar00rootroot00000000000000ufl-1.3.0/demo/Constant.ufl000066400000000000000000000020001226300046600155150ustar00rootroot00000000000000# Copyright (C) 2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes, 2009 # # Test form for scalar and vector constants. cell = triangle element = FiniteElement("Lagrange", cell, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) c = Constant(cell) d = VectorConstant(cell) a = c*dot(grad(v), grad(u))*dx L = inner(d, grad(v))*dx ufl-1.3.0/demo/ConvectionJacobi.ufl000066400000000000000000000003531226300046600171540ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = VectorElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) w = Coefficient(element) a = dot(dot(u, grad(w)) + dot(w, grad(u)), v) * dx ufl-1.3.0/demo/ConvectionJacobi2.ufl000066400000000000000000000003541226300046600172370ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = VectorElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) w = Coefficient(element) a = (u[j]*w[i].dx(j) + w[j]*u[i].dx(j)) * v[i] * dx ufl-1.3.0/demo/ConvectionVector.ufl000066400000000000000000000003001226300046600172170ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) w = Coefficient(element) a = dot( dot(w, grad(w)), v ) * dx ufl-1.3.0/demo/Elasticity.ufl000066400000000000000000000004421226300046600160460ustar00rootroot00000000000000# # Author: Anders Logg # Modified by: Martin Sandve Alnes # Date: 2009-01-12 # element = VectorElement("Lagrange", tetrahedron, 1) v = TestFunction(element) u = TrialFunction(element) def epsilon(v): Dv = grad(v) return 0.5*(Dv + Dv.T) a = inner(epsilon(v), epsilon(u))*dx ufl-1.3.0/demo/EnergyNorm.ufl000066400000000000000000000016521226300046600160250ustar00rootroot00000000000000# Copyright (C) 2005-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # This example demonstrates how to define a functional, here # the energy norm (squared) for a reaction-diffusion problem. element = FiniteElement("Lagrange", tetrahedron, 1) v = Coefficient(element) a = (v*v + dot(grad(v), grad(v)))*dx ufl-1.3.0/demo/Equation.ufl000066400000000000000000000030451226300046600155230ustar00rootroot00000000000000# Copyright (C) 2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Specification of a system F(v, u) = 0 and extraction of # the bilinear and linear forms a and L for the left- and # right-hand sides: # # F(v, u) = a(v, u) - L(v) = 0 # # The example below demonstrates the specification of the # linear system for a cG(1)/Crank-Nicholson time step for # the heat equation. # # The below formulation is equivalent to writing # # a = v*u*dx + 0.5*k*dot(grad(v), grad(u))*dx # L = v*u0*dx - 0.5*k*dot(grad(v), grad(u0))*dx # # but instead of manually shuffling terms not including # the unknown u to the right-hand side, all terms may # be listed on one line and left- and right-hand sides # extracted by lhs() and rhs(). element = FiniteElement("Lagrange", triangle, 1) k = 0.1 v = TestFunction(element) u = TrialFunction(element) u0 = Coefficient(element) F = v*(u - u0)*dx + k*dot(grad(v), grad(0.5*(u0 + u)))*dx a = lhs(F) L = rhs(F) ufl-1.3.0/demo/ExplicitConvection.ufl000066400000000000000000000003331226300046600175440ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = VectorElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) w = Coefficient(element) a = dot( dot(w, grad(u)), v ) * dx ufl-1.3.0/demo/FEEC.ufl000066400000000000000000000032641226300046600144430ustar00rootroot00000000000000# Copyright (C) 2010 Marie Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . """ This demo illustrates the FEEC notation V = FiniteElement("P Lambda", cell, r, k) V = FiniteElement("P- Lambda", cell, r, k) and their aliases. """ from ufl import exterior_derivative as d set_level(INFO) cells = [interval, triangle, tetrahedron] r = 1 for cell in cells: for family in ["P Lambda", "P- Lambda"]: tdim = cell.topological_dimension() for k in range(0, tdim+1): # Testing exterior derivative V = FiniteElement(family, cell, r, form_degree=k) v = TestFunction(V) u = TrialFunction(V) a = inner(d(u), d(v))*dx # Testing mixed formulation of Hodge Laplace if k > 0 and k < tdim+1: S = FiniteElement(family, cell, r, form_degree=k-1) W = S * V (sigma, u) = TrialFunctions(W) (tau, v) = TestFunctions(W) a = (inner(sigma, tau) - inner(d(tau), u) + inner(d(sigma), v) + inner(d(u), d(v)))*dx ufl-1.3.0/demo/FunctionOperators.ufl000066400000000000000000000017071226300046600174250ustar00rootroot00000000000000# Copyright (C) 2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Test form for operators on Coefficients. element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) a = sqrt(1/abs(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx + v*u*sqrt(f*g)*g*dx ufl-1.3.0/demo/H1norm.ufl000066400000000000000000000002541226300046600151010ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) f = Coefficient(element) a = ( f*f + dot(grad(f), grad(f)) ) * dx ufl-1.3.0/demo/HarmonicMap.ufl000066400000000000000000000005371226300046600161370ustar00rootroot00000000000000# # Harmonic map demo using separate coefficients x and y. # Author: Martin Alnes # Date: 2009-04-09 # cell = triangle X = VectorElement("Lagrange", cell, 1) Y = FiniteElement("Lagrange", cell, 1) x = Coefficient(X) y = Coefficient(Y) L = inner(grad(x), grad(x))*dx + dot(x,x)*y*dx F = derivative(L,(x,y)) J = derivative(F,(x,y)) forms = [L,F,J] ufl-1.3.0/demo/HarmonicMap2.ufl000066400000000000000000000005541226300046600162200ustar00rootroot00000000000000# # Harmonic map demo using one mixed function u to represent x and y. # Author: Martin Alnes # Date: 2009-04-09 # cell = triangle X = VectorElement("Lagrange", cell, 1) Y = FiniteElement("Lagrange", cell, 1) M = X * Y u = Coefficient(M) x, y = split(u) L = inner(grad(x), grad(x))*dx + dot(x,x)*y*dx F = derivative(L, u) J = derivative(F, u) forms = [L,F,J] ufl-1.3.0/demo/Heat.ufl000066400000000000000000000023411226300046600146150ustar00rootroot00000000000000# Copyright (C) 2005-2009 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes, 2009 # # The bilinear form a(v, u1) and linear form L(v) for # one backward Euler step with the heat equation. # cell = triangle element = FiniteElement("Lagrange", cell, 1) v = TestFunction(element) # Test function u1 = TrialFunction(element) # Value at t_n u0 = Coefficient(element) # Value at t_n-1 c = Coefficient(element) # Heat conductivity f = Coefficient(element) # Heat source k = Constant(cell) # Time step a = v*u1*dx + k*c*dot(grad(v), grad(u1))*dx L = v*u0*dx + k*v*f*dx ufl-1.3.0/demo/HornSchunck.ufl000066400000000000000000000012311226300046600161560ustar00rootroot00000000000000# # Implemented by imitation of # http://code.google.com/p/debiosee/wiki/DemosOptiocFlowHornSchunck # but not tested so this could contain errors! # # Finite element spaces for scalar and vector fields cell = triangle S = FiniteElement("CG", cell, 1) V = VectorElement("CG", cell, 1) # Optical flow function u = Coefficient(V) # Previous image brightness I0 = Coefficient(S) # Current image brightness I1 = Coefficient(S) # Regularization parameter lamda = Constant(cell) # Coefficiental to minimize M = (dot(u, grad(I1)) + (I1-I0))**2 * dx\ + lamda*inner(grad(u), grad(u)) * dx # Derived linear system L = derivative(M, u) a = derivative(L, u) L = -L ufl-1.3.0/demo/HyperElasticity.ufl000066400000000000000000000034371226300046600170650ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-12-22 # # Modified by Garth N. Wells, 2009 # Cell and its properties cell = tetrahedron d = cell.d n = cell.n x = cell.x # Elements u_element = VectorElement("CG", cell, 2) p_element = FiniteElement("CG", cell, 1) A_element = TensorElement("CG", cell, 1) # Test and trial functions v = TestFunction(u_element) w = TrialFunction(u_element) # Displacement at current and two previous timesteps u = Coefficient(u_element) up = Coefficient(u_element) upp = Coefficient(u_element) # Time parameters dt = Constant(cell) # Fiber field A = Coefficient(A_element) # External forces T = Coefficient(u_element) p0 = Coefficient(p_element) N = cell.n # Material parameters FIXME rho = Constant(cell) K = Constant(cell) c00 = Constant(cell) c11 = Constant(cell) c22 = Constant(cell) # Deformation gradient I = Identity(d) F = I + grad(u) F = variable(F) Finv = inv(F) J = det(F) # Left Cauchy-Green deformation tensor B = F*F.T I1_B = tr(B) I2_B = (I1_B**2 - tr(B*B))/2 I3_B = J**2 # Right Cauchy-Green deformation tensor C = F.T*F I1_C = tr(C) I2_C = (I1_C**2 - tr(C*C))/2 I3_C = J**2 # Green strain tensor E = (C-I)/2 # Mapping of strain in fiber directions Ef = A*E*A.T # Strain energy function W(Q(Ef)) Q = c00*Ef[0,0]**2 + c11*Ef[1,1]**2 + c22*Ef[2,2]**2 # FIXME: insert some simple law here W = (K/2)*(exp(Q) - 1) # + p stuff # First Piola-Kirchoff stress tensor P = diff(W, F) # Acceleration term discretized with finite differences k = dt/rho acc = (u - 2*up + upp) # Residual equation # FIXME: Can contain errors, not tested! a_F = inner(acc, v)*dx \ + k*inner(P, grad(v))*dx \ - k*dot(J*Finv*T, v)*ds(0) \ - k*dot(J*Finv*p0*N, v)*ds(1) # Jacobi matrix of residual equation a_J = derivative(a_F, u, w) # Export forms forms = [a_F, a_J] ufl-1.3.0/demo/HyperElasticity1D.ufl000066400000000000000000000005031226300046600172410ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # cell = interval element = FiniteElement("CG", cell, 2) u = Coefficient(element) b = Constant(cell) K = Constant(cell) E = u.dx(0) + u.dx(0)**2 / 2 E = variable(E) Q = b*E**2 psi = K*(exp(Q)-1) f = psi*dx F = derivative(f, u) J = derivative(-F, u) forms = [f, F, J] ufl-1.3.0/demo/L2norm.ufl000066400000000000000000000002171226300046600151050ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) f = Coefficient(element) a = f**2*dx ufl-1.3.0/demo/Mass.ufl000066400000000000000000000016341226300046600146430ustar00rootroot00000000000000# Copyright (C) 2004-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes, 2009 # # Last changed: 2009-03-02 # # The bilinear form for a mass matrix. element = FiniteElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) a = v*u*dx ufl-1.3.0/demo/MassAD.ufl000066400000000000000000000003431226300046600150440ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-28 # element = FiniteElement("Lagrange", triangle, 1) u = Coefficient(element) # L2 norm M = u**2/2*dx # source vector L = derivative(M, u) # mass matrix a = derivative(L, u) ufl-1.3.0/demo/MixedElasticity.ufl000066400000000000000000000030011226300046600170270ustar00rootroot00000000000000# Copyright (C) 2008-2010 Marie Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-10-03 # Last changed: 2011-07-22 # Define vectorized skew operator def skw(tau): sk = 2*skew(tau) return as_vector((sk[0,1], sk[0,2], sk[1,2])) cell = tetrahedron n = 3 # Finite element exterior calculus syntax r = 1 S = VectorElement("P Lambda", cell, r, form_degree=n-1) V = VectorElement("P Lambda", cell, r-1, form_degree=n) Q = VectorElement("P Lambda", cell, r-1, form_degree=n) # Alternative syntax: # S = VectorElement("BDM", cell, r) # V = VectorElement("Discontinuous Lagrange", cell, r-1) # Q = VectorElement("Discontinuous Lagrange", cell, r-1) W = MixedElement(S, V, Q) (sigma, u, gamma) = TrialFunctions(W) (tau, v, eta) = TestFunctions(W) a = (inner(sigma, tau) - tr(sigma)*tr(tau) + dot(div(tau), u) - dot(div(sigma), v) + inner(skw(tau), gamma) + inner(skw(sigma), eta))*dx ufl-1.3.0/demo/MixedMixedElement.ufl000066400000000000000000000014471226300046600173110ustar00rootroot00000000000000# Copyright (C) 2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # A mixed element of mixed elements P3 = FiniteElement("Lagrange", triangle, 3) element = (P3 * P3) * (P3 * P3) ufl-1.3.0/demo/MixedPoisson.ufl000066400000000000000000000023301226300046600163530ustar00rootroot00000000000000# Copyright (C) 2006-2009 Anders Logg and Marie Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes, 2009 # # Last changed: 2009-01-12 # # The bilinear form a(v, u) and linear form L(v) for # a mixed formulation of Poisson's equation with BDM # (Brezzi-Douglas-Marini) elements. # cell = triangle BDM1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1) DG0 = FiniteElement("Discontinuous Lagrange", cell, 0) element = BDM1 * DG0 (tau, w) = TestFunctions(element) (sigma, u) = TrialFunctions(element) f = Coefficient(DG0) a = (dot(tau, sigma) - div(tau)*u + w*div(sigma))*dx L = w*f*dx ufl-1.3.0/demo/MixedPoisson2.ufl000066400000000000000000000005761226300046600164470ustar00rootroot00000000000000# # Author: Marie Rognes # Modified by: Martin Sandve Alnes # Date: 2009-02-12 # cell = tetrahedron RT = FiniteElement("Raviart-Thomas", cell, 1) DG = FiniteElement("DG", cell, 0) MX = RT * DG (u, p) = TrialFunctions(MX) (v, q) = TestFunctions(MX) n = cell.n a0 = (dot(u, v) + div(u)*q + div(v)*p)*dx a1 = (dot(u, v) + div(u)*q + div(v)*p)*dx - p*dot(v, n)*ds forms = [a0, a1] ufl-1.3.0/demo/NavierStokes.ufl000066400000000000000000000020421226300046600163470ustar00rootroot00000000000000# Copyright (C) 2004-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes # # Last changed: 2009-03-02 # # The bilinear form for the nonlinear term in the # Navier-Stokes equations with fixed convective velocity. cell = tetrahedron element = VectorElement("Lagrange", cell, 1) v = TestFunction(element) u = TrialFunction(element) w = Coefficient(element) Du = grad(u) a = dot( dot(w, Du), v )*dx ufl-1.3.0/demo/NeumannProblem.ufl000066400000000000000000000020051226300046600166530ustar00rootroot00000000000000# Copyright (C) 2006-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation with Neumann boundary conditions. element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) a = inner(grad(v), grad(u))*dx L = inner(v, f)*dx + inner(v, g)*ds ufl-1.3.0/demo/NonlinearPoisson.ufl000066400000000000000000000004621226300046600172360ustar00rootroot00000000000000# nonlinear_poisson.ufl element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) u0 = Coefficient(element) f = Coefficient(element) a = (1+u0**2)*dot(grad(v), grad(u))*dx \ + 2*u0*u*dot(grad(v), grad(u0))*dx L = v*f*dx - (1+u0**2)*dot(grad(v), grad(u0))*dx ufl-1.3.0/demo/P5tet.ufl000066400000000000000000000014511226300046600147360ustar00rootroot00000000000000# Copyright (C) 2006-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # A fifth degree Lagrange finite element on a tetrahedron element = FiniteElement("Lagrange", tetrahedron, 5) ufl-1.3.0/demo/P5tri.ufl000066400000000000000000000014431226300046600147410ustar00rootroot00000000000000# Copyright (C) 2006-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # A fifth degree Lagrange finite element on a triangle element = FiniteElement("Lagrange", triangle, 5) ufl-1.3.0/demo/Poisson.ufl000066400000000000000000000021111226300046600153610ustar00rootroot00000000000000# Copyright (C) 2004-2008 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Sandve Alnes, 2009 # # Last changed: 2009-03-02 # # The bilinear form a(v, u) and linear form L(v) for Poisson's equation. element = FiniteElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) f = Coefficient(element) dx0_1 = dx(0, {"quadrature_order": 1}) dx0_2 = dx(0, {"quadrature_order": 2}) a = dot(grad(v), grad(u))*dx0_1 L = v*f*dx0_2 ufl-1.3.0/demo/PoissonDG.ufl000066400000000000000000000026021226300046600156010ustar00rootroot00000000000000# Copyright (C) 2006-2007 Kristiand Oelgaard and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2006-12-05 # Last changed: 2007-07-15 # # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation in a discontinuous Galerkin (DG) # formulation. element = FiniteElement("Discontinuous Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) n = triangle.n h = Constant(triangle) gN = Coefficient(element) alpha = 4.0 gamma = 8.0 a = inner(grad(v), grad(u))*dx \ - inner(avg(grad(v)), jump(u, n))*dS \ - inner(jump(v, n), avg(grad(u)))*dS \ + alpha/h('+')*dot(jump(v, n), jump(u, n))*dS \ - inner(grad(v), u*n)*ds \ - inner(v*n, grad(u))*ds \ + gamma/h*v*u*ds L = v*f*dx + v*gN*ds ufl-1.3.0/demo/PoissonSystem.ufl000066400000000000000000000020561226300046600165760ustar00rootroot00000000000000# Copyright (C) 2005-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by: Martin Sandve Alnes, 2009 # # Last changed: 2009-03-02 # # The bilinear form a(v, u) and linear form L(v) for # Poisson's equation in system form (vector-valued). cell = triangle element = VectorElement("Lagrange", cell, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) a = inner(grad(v), grad(u))*dx L = dot(v, f)*dx ufl-1.3.0/demo/PowAD.ufl000066400000000000000000000003321226300046600147040ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) w = Coefficient(element) L = w**5*v*dx a = derivative(L, w) ufl-1.3.0/demo/ProjectionSystem.ufl000066400000000000000000000002301226300046600172500ustar00rootroot00000000000000 element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) a = u*v*dx L = f*v*dx ufl-1.3.0/demo/QuadratureElement.ufl000066400000000000000000000023711226300046600173660ustar00rootroot00000000000000# Copyright (C) 2008 Kristian B. Oelgaard # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-31 # Last changed: 2008-03-31 # # The linearised bilinear form a(u,v) and linear form L(v) for # the nonlinear equation - div (1+u) grad u = f (non-linear Poisson) element = FiniteElement("Lagrange", triangle, 2) QE = FiniteElement("Quadrature", triangle, 3) sig = VectorElement("Quadrature", triangle, 3) v = TestFunction(element) u = TrialFunction(element) u0= Coefficient(element) C = Coefficient(QE) sig0 = Coefficient(sig) f = Coefficient(element) a = v.dx(i)*C*u.dx(i)*dx + v.dx(i)*2*u0*u*u0.dx(i)*dx L = v*f*dx - dot(grad(v), sig0)*dx ufl-1.3.0/demo/README000066400000000000000000000003011226300046600140760ustar00rootroot00000000000000To run these demos from within the source tree without needing to install UFL system-wide, update your paths according to export PATH="../scripts:$PATH" export PYTHONPATH="..:$PYTHONPATH" ufl-1.3.0/demo/RestrictedElement.ufl000066400000000000000000000026651226300046600173670ustar00rootroot00000000000000# Copyright (C) 2009 Kristian B. Oelgaard # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Restriction of a finite element. # The below syntax show how one can restrict a higher order Lagrange element # to only take into account those DOFs that live on the facets. # Restricted element CG_R = FiniteElement("Lagrange", triangle, 4)["facet"] u_r = TrialFunction(CG_R) v_r = TestFunction(CG_R) a = avg(v_r)*avg(u_r)*dS + v_r*u_r*ds #CG = FiniteElement("Lagrange", triangle, 4) #CG_R = CG["facet"] #u_r = TrialFunction(CG_R) #v_r = TestFunction(CG_R) #a = v_r('+')*u_r('+')*dS + v_r('-')*u_r('-')*dS + v_r*u_r*ds # Mixed element #CG = FiniteElement("Lagrange", triangle, 4) #CG_R = CG["facet"] #ME = CG * CG_R #u, u_r = TrialFunctions(ME) #v, v_r = TestFunctions(ME) #a = v*u*dx + v_r('+')*u_r('+')*dS + v_r('+')*u_r('+')*dS + v_r*u_r*ds ufl-1.3.0/demo/ShouldFail.ufl000066400000000000000000000003301226300046600157620ustar00rootroot00000000000000 # FIXME: This form passes validation but should fail since it mixes linear and bilinear terms. e = FiniteElement("CG", triangle, 1) f = Coefficient(e) v = TestFunction(e) u = TrialFunction(e) a = f*v*dx + u*v*dx ufl-1.3.0/demo/Source.ufl000066400000000000000000000002501226300046600151710ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) f = Coefficient(element) a = f*v*dx ufl-1.3.0/demo/Stiffness.ufl000066400000000000000000000002731226300046600157020ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) a = dot(grad(u), grad(v))*dx ufl-1.3.0/demo/StiffnessAD.ufl000066400000000000000000000006611226300046600161100ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-30 # element = FiniteElement("Lagrange", triangle, 1) w = Coefficient(element) # H1 semi-norm f = inner(grad(w), grad(w))/2*dx # grad(w) : grad(v) b = derivative(f, w) # stiffness matrix, grad(u) : grad(v) a = derivative(b, w) # adjoint, grad(v) : grad(u) astar = adjoint(a) # action of adjoint, grad(v) : grad(w) astaraction = action(astar) forms = [f, b, a, astar, astaraction] ufl-1.3.0/demo/Stokes.ufl000066400000000000000000000021741226300046600152100ustar00rootroot00000000000000# Copyright (C) 2005-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by: Martin Sandve Alnes (2009) # Date: 2009-03-02 # # The bilinear form a(v, u) and Linear form L(v) for the Stokes # equations using a mixed formulation (Taylor-Hood elements). cell = triangle P2 = VectorElement("Lagrange", cell, 2) P1 = FiniteElement("Lagrange", cell, 1) TH = P2 * P1 (v, q) = TestFunctions(TH) (u, p) = TrialFunctions(TH) f = Coefficient(P2) a = (inner(grad(v), grad(u)) - div(v)*p + q*div(u))*dx L = dot(v, f)*dx ufl-1.3.0/demo/StokesEquation.ufl000066400000000000000000000023351226300046600167150ustar00rootroot00000000000000# Copyright (C) 2005-2009 Anders Logg and Harish Narayanan # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # The bilinear form a(v, u) and Linear form L(v) for the Stokes # equations using a mixed formulation (Taylor-Hood elements) in # combination with the lhs() and rhs() operators to extract the # bilinear and linear forms from an expression F = 0. cell = triangle P2 = VectorElement("Lagrange", cell, 2) P1 = FiniteElement("Lagrange", cell, 1) TH = P2 * P1 (v, q) = TestFunctions(TH) (u, p) = TrialFunctions(TH) f = Coefficient(P2) F = (inner(grad(v), grad(u)) - div(v)*p + q*div(u))*dx - dot(v, f)*dx a = lhs(F) L = rhs(F) ufl-1.3.0/demo/SubDomain.ufl000066400000000000000000000016721226300046600156230ustar00rootroot00000000000000# Copyright (C) 2008 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # This example illustrates how to define a form over a # given subdomain of a mesh, in this case a functional. element = FiniteElement("CG", tetrahedron, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) M = f*dx(2) + f*ds(5) ufl-1.3.0/demo/SubDomains.ufl000066400000000000000000000020451226300046600160010ustar00rootroot00000000000000# Copyright (C) 2008 Anders Logg and Kristian B. Oelgaard # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # This simple example illustrates how forms can be defined on different sub domains. # It is supported for all three integral types. element = FiniteElement("CG", tetrahedron, 1) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx(0) + 10.0*v*u*dx(1) + v*u*ds(0) + 2.0*v*u*ds(1) + v('+')*u('+')*dS(0) + 4.3*v('+')*u('+')*dS(1) ufl-1.3.0/demo/TensorWeightedPoisson.ufl000066400000000000000000000017551226300046600202520ustar00rootroot00000000000000# Copyright (C) 2005-2007 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # The bilinear form a(v, u) and linear form L(v) for # tensor-weighted Poisson's equation. P1 = FiniteElement("Lagrange", triangle, 1) P0 = TensorElement("Discontinuous Lagrange", triangle, 0, shape=(2, 2)) v = TestFunction(P1) u = TrialFunction(P1) C = Coefficient(P0) a = inner(grad(v), C*grad(u))*dx ufl-1.3.0/demo/VectorLaplaceGradCurl.ufl000066400000000000000000000025631226300046600201120ustar00rootroot00000000000000# Copyright (C) 2007 Marie Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # The bilinear form a(v, u) and linear form L(v) for the Hodge Laplace # problem using 0- and 1-forms. Intended to demonstrate use of Nedelec # elements. def HodgeLaplaceGradCurl(element, felement): tau, v = TestFunctions(element) sigma, u = TrialFunctions(element) f = Coefficient(felement) a = ( inner(tau, sigma) - inner(grad(tau), u) +\ inner(v, grad(sigma)) + inner(curl(v), curl(u)) )*dx L = inner(v, f)*dx return a, L cell = tetrahedron order = 1 GRAD = FiniteElement("Lagrange", cell, order) CURL = FiniteElement("N1curl", cell, order) VectorLagrange = VectorElement("Lagrange", cell, order+1) a, L = HodgeLaplaceGradCurl(GRAD * CURL, VectorLagrange) ufl-1.3.0/demo/_TensorProductElement.ufl000066400000000000000000000017431226300046600202250ustar00rootroot00000000000000# Copyright (C) 2012 Marie E. Rognes (meg@simula.no) # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2012-08-16 # Last changed: 2012-08-16 V0 = FiniteElement("CG", triangle, 1) V1 = FiniteElement("DG", interval, 0) V2 = FiniteElement("DG", tetrahedron, 0) V = TensorProductElement(V0, V1, V2) u = TrialFunction(V) v = TestFunction(V) dxxx = dx*dx*dx a = u*v*dxxx ufl-1.3.0/demo/clean.sh000077500000000000000000000003471226300046600146510ustar00rootroot00000000000000#!/bin/bash # ufl-analyse output rm -f *_debug.py rm -f *.analysis rm -f *.repr rm -f *.str rm -f *.tree # python compiled files rm -f *.pyc # latex files rm -f *.aux *.log *.pdf *.tex *.ps *.dvi # dot files rm -f *.dot *.png ufl-1.3.0/doc/000077500000000000000000000000001226300046600130455ustar00rootroot00000000000000ufl-1.3.0/doc/man/000077500000000000000000000000001226300046600136205ustar00rootroot00000000000000ufl-1.3.0/doc/man/man1/000077500000000000000000000000001226300046600144545ustar00rootroot00000000000000ufl-1.3.0/doc/man/man1/form2ufl.1.gz000066400000000000000000000011501226300046600167060ustar00rootroot00000000000000%Mn0|AN-hzK k*ِd>JbA*Iwe;M@{!nT,qXgO %i.j5/jg;X]a!?`\d@`).]o$~Ä2N_,K6ENE^S:/7ud@>0޺AF˻i9ez~?#E-,gq/E^3w yE8xZq4B"8M4F{mZiKx>>4j]xhO T?8ulN<  VF8$F*s-Y ``&)k\>oaI=ٵ#=8{6NtޤIq|Zz# ~M"me3̃Ǽ2{PfAsC|^&G?D{xk踵h.77ǶIr3xh`Y@WEr/u6|PU:R&>*ɊdN)*V7 QPqE5A|9Z4#ؠj GG6Dk *4\zIQRTb[a7YxD3@ vτ6";F 0SyR IM˝|Ra ;}Zޮ6%h mwufl-1.3.0/doc/man/man1/ufl-convert.1.gz000066400000000000000000000016531226300046600174260ustar00rootroot00000000000000*MTn6+{,`+(zK*^'Qa[$75hidH-IGcJhу Cy捂>lWEy;Ƃ6zɺJ ha18JHA T4#BIe,7@*<7sB)Ă (xDMKBz)Ğ?.Ïxʞk Z+gܴ HJRou4uٞ[, P<8]ш *u W֙l̀ӛ݀N%Rۡl(W ~&dY<s[,nQ (RСQ\Q /:Y-G驓0̰*`^ [hmF7q֡4}wi,+?@@Nu@r-Y^ eEU *4XΑ!]f+iJ] Vd.=BC%^V@e8'&S{_{D5@2~5τ6"u‚nOD\t~GR^1kMq59 > )aY:w)i!)Lp~oGѴR,CztHûh;O>7,s!$aG*L!٦I- _Tx%:.wJ2p'K#䩶c\jZ_&=H +KRJbA*]N(Ћ@r9ÙUPl$?,w|a,ȷ>B+FQ\@x(?ThΰyB=Q5Pa@ ~ynѲq8Ka $G ]p&M(#Vhtgl "XݺY{"rጨCjtjМ<:4ݞ6Oi ZK'Th!5*@5YDD+@Au'ï\V@>qH=.$v2^B8{=$ [l\w%.0=KeaZMkx(ђÕI3{-$.M\QfA0+2 3ؗ~G@!UTSs(ҒҒ2@ϏH-QIRdo) ,oiN@n- v}w°w?D1W.se!]V ^@6\y9?¿+2Aoo=l^7ufl-1.3.0/doc/man/man1/ufl2py.1.gz000066400000000000000000000011621226300046600163760ustar00rootroot00000000000000MSK0W {j!u]%aQǶ,z$w$,zHkۭ62kH%F hDNC1>fERP&]@oX' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/DOLFIN.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/DOLFIN.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/DOLFIN" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/DOLFIN" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ufl-1.3.0/doc/sphinx/README000066400000000000000000000003071226300046600152360ustar00rootroot00000000000000To build the documentation, run `scripts/makedoc` from the top-level UFL directory. Running `make html` directly from within this directory won't work (unless the makedoc script has been run first). ufl-1.3.0/doc/sphinx/scripts/000077500000000000000000000000001226300046600160455ustar00rootroot00000000000000ufl-1.3.0/doc/sphinx/scripts/README_generate_modules.rst000066400000000000000000000021211226300046600231320ustar00rootroot00000000000000 Generate Modules ================ This script parses a directory tree looking for python modules and packages and creates ReST files appropriately to create code documentation with Sphinx. It also creates a modules index. Usage:: Usage: generate_modules.py [options] [exclude paths, ...] Note: By default this script will not overwrite already created files. Options: -h, --help show this help message and exit -n HEADER, --doc-header=HEADER Documentation Header (default=Project) -d DESTDIR, --dest-dir=DESTDIR Output destination directory -s SUFFIX, --suffix=SUFFIX module suffix (default=txt) -m MAXDEPTH, --maxdepth=MAXDEPTH Maximum depth of submodules to show in the TOC (default=4) -r, --dry-run Run the script without creating the files -f, --force Overwrite all the files -t, --no-toc Don't create the table of content file ufl-1.3.0/doc/sphinx/scripts/generate_index.py000077500000000000000000000050161226300046600214050ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (C) 2011 Marie E. Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2011-06-09 # Last changed: 2011-06-09 # # This is a naive utility script for adding some labels to generated # .rst and for creating a main level index. It is used by # scripts/makedoc after generating .rst # import os, sys index_template = """ ############################################# Documentation for UFL v%s ############################################# .. UFL Language Reference .. ====================== .. .. * (What is accessible?) UFL Library Reference ===================== * :ref:`UFL Programmer's Reference ` (Packages and Modules. Everything.) .. toctree:: :hidden: :maxdepth: 1 modules """ def insert_labels(directory, filenames): """ Insert labels based on filename for those files defined by the given filenames relative to directory """ for name in filenames: filename = os.path.join(directory, name) file = open(filename) text = file.read() file.close() label = "\n.. _%s_package:\n\n" % "_".join(name.split(".")[:-1]) modded_text = label + text print "Adding label to %s" % filename file = open(filename, "w") file.write(modded_text) file.close() def generate_index_file(output_dir, version): text = index_template % version filename = os.path.join(output_dir, "index.rst") print "Writing documentation index file to %s" % filename file = open(filename, "w") file.write(text) file.close() def main(input_dir, version): files = ["ufl.rst", "ufl.finiteelement.rst", "ufl.algorithms.rst"] insert_labels(input_dir, files) generate_index_file(input_dir, version) if __name__ == '__main__': if len(sys.argv) != 3: print "Usage: python generate_index.py input_directory version" exit() main(sys.argv[1], sys.argv[2]) ufl-1.3.0/doc/sphinx/scripts/generate_modules.py000077500000000000000000000237161226300046600217550ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ sphinx-autopackage-script This script parses a directory tree looking for python modules and packages and creates ReST files appropriately to create code documentation with Sphinx. It also creates a modules index (named modules.). """ # Copyright 2008 Société des arts technologiques (SAT), http://www.sat.qc.ca/ # Copyright 2010 Thomas Waldmann # All rights reserved. # # 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, see . # Modified by Marie E. Rognes (meg@simula.no), 2011 import os import optparse # automodule options OPTIONS = ['members', 'undoc-members', # 'inherited-members', # disabled because there's a bug in sphinx 'show-inheritance', ] INIT = '__init__.py' def makename(package, module): """Join package and module with a dot.""" # Both package and module can be None/empty. if package: name = package if module: name += '.' + module else: name = module return name def write_file(name, text, opts): """Write the output file for module/package .""" if opts.dryrun: return fname = os.path.join(opts.destdir, "%s.%s" % (name, opts.suffix)) if not opts.force and os.path.isfile(fname): print 'File %s already exists, skipping.' % fname else: print 'Creating file %s.' % fname f = open(fname, 'w') f.write(text) f.close() def format_heading(level, text): """Create a heading of [1, 2 or 3 supported].""" underlining = ['=', '-', '~', ][level-1] * len(text) return '%s\n%s\n\n' % (text, underlining) def format_directive(module, package=None): """Create the automodule directive and add the options.""" directive = '.. automodule:: %s\n' % makename(package, module) # MER: Treat stuff from __init__ a little differently. if "__init__" in module: directive += ' :%s:\n' % "noindex" else: for option in OPTIONS: directive += ' :%s:\n' % option return directive def create_module_file(package, module, opts): """Build the text of the file and write the file.""" text = format_heading(1, '%s Module' % module) text += format_heading(2, ':mod:`%s` Module' % module) text += format_directive(module, package) write_file(makename(package, module), text, opts) def create_package_file(root, master_package, subroot, py_files, opts, subs): """Build the text of the file and write the file.""" package = os.path.split(root)[-1] text = format_heading(1, '%s Package' % package) # add each package's module for py_file in py_files: if shall_skip(os.path.join(root, py_file)): continue is_package = py_file == INIT py_file = os.path.splitext(py_file)[0] py_path = makename(subroot, py_file) if is_package: heading = ':mod:`%s` Package' % package else: heading = ':mod:`%s` Module' % py_file text += format_heading(2, heading) text += format_directive(is_package and subroot or py_path, master_package) text += '\n' # build a list of directories that are packages (they contain an INIT file) subs = [sub for sub in subs if os.path.isfile(os.path.join(root, sub, INIT))] # if there are some package directories, add a TOC for theses subpackages if subs: text += format_heading(2, 'Subpackages') text += '.. toctree::\n\n' for sub in subs: text += ' %s.%s\n' % (makename(master_package, subroot), sub) text += '\n' write_file(makename(master_package, subroot), text, opts) def create_modules_toc_file(master_package, modules, opts, name='modules'): """ Create the module's index. """ text = format_heading(1, '%s Modules' % opts.header) text += '.. toctree::\n' text += ' :maxdepth: %s\n\n' % opts.maxdepth modules.sort() prev_module = '' for module in modules: # look if the module is a subpackage and, if yes, ignore it if module.startswith(prev_module + '.'): continue prev_module = module text += ' %s\n' % module write_file(name, text, opts) def shall_skip(module): """ Check if we want to skip this module. """ # skip it, if there is nothing (or just \n or \r\n) in the file return (os.path.getsize(module) < 3) def recurse_tree(path, excludes, opts): """ Look for every file in the directory tree and create the corresponding ReST files. """ # use absolute path for root, as relative paths like '../../foo' cause # 'if "/." in root ...' to filter out *all* modules otherwise path = os.path.abspath(path) # check if the base directory is a package and get is name if INIT in os.listdir(path): package_name = path.split(os.path.sep)[-1] else: package_name = None toc = [] tree = os.walk(path, False) for root, subs, files in tree: # keep only the Python script files py_files = sorted([f for f in files if os.path.splitext(f)[1] == '.py']) if INIT in py_files: py_files.remove(INIT) py_files.insert(0, INIT) # remove hidden ('.') and private ('_') directories subs = sorted([sub for sub in subs if sub[0] not in ['.', '_']]) # check if there are valid files to process # TODO: could add check for windows hidden files if "/." in root or "/_" in root \ or not py_files \ or is_excluded(root, excludes): continue if INIT in py_files: # we are in package ... if (# ... with subpackage(s) subs or # ... with some module(s) len(py_files) > 1 or # ... with a not-to-be-skipped INIT file not shall_skip(os.path.join(root, INIT)) ): subroot = root[len(path):].lstrip(os.path.sep).replace(os.path.sep, '.') create_package_file(root, package_name, subroot, py_files, opts, subs) toc.append(makename(package_name, subroot)) elif root == path: # if we are at the root level, we don't require it to be a package for py_file in py_files: if not shall_skip(os.path.join(path, py_file)): module = os.path.splitext(py_file)[0] create_module_file(package_name, module, opts) toc.append(makename(package_name, module)) # create the module's index if not opts.notoc: create_modules_toc_file(package_name, toc, opts) def normalize_excludes(rootpath, excludes): """ Normalize the excluded directory list: * must be either an absolute path or start with rootpath, * otherwise it is joined with rootpath * with trailing slash """ sep = os.path.sep f_excludes = [] for exclude in excludes: if not os.path.isabs(exclude) and not exclude.startswith(rootpath): exclude = os.path.join(rootpath, exclude) if not exclude.endswith(sep): exclude += sep f_excludes.append(exclude) return f_excludes def is_excluded(root, excludes): """ Check if the directory is in the exclude list. Note: by having trailing slashes, we avoid common prefix issues, like e.g. an exlude "foo" also accidentally excluding "foobar". """ sep = os.path.sep if not root.endswith(sep): root += sep for exclude in excludes: if root.startswith(exclude): return True return False def main(): """ Parse and check the command line arguments. """ parser = optparse.OptionParser(usage="""usage: %prog [options] [exclude paths, ...] Note: By default this script will not overwrite already created files.""") parser.add_option("-n", "--doc-header", action="store", dest="header", help="Documentation Header (default=Project)", default="Project") parser.add_option("-d", "--dest-dir", action="store", dest="destdir", help="Output destination directory", default="") parser.add_option("-s", "--suffix", action="store", dest="suffix", help="module suffix (default=txt)", default="txt") parser.add_option("-m", "--maxdepth", action="store", dest="maxdepth", help="Maximum depth of submodules to show in the TOC (default=4)", type="int", default=4) parser.add_option("-r", "--dry-run", action="store_true", dest="dryrun", help="Run the script without creating the files") parser.add_option("-f", "--force", action="store_true", dest="force", help="Overwrite all the files") parser.add_option("-t", "--no-toc", action="store_true", dest="notoc", help="Don't create the table of content file") (opts, args) = parser.parse_args() if not args: parser.error("package path is required.") else: rootpath, excludes = args[0], args[1:] if os.path.isdir(rootpath): # check if the output destination is a valid directory if opts.destdir and os.path.isdir(opts.destdir): excludes = normalize_excludes(rootpath, excludes) recurse_tree(rootpath, excludes, opts) else: print '%s is not a valid output destination directory.' % opts.destdir else: print '%s is not a valid directory.' % rootpath if __name__ == '__main__': main() ufl-1.3.0/doc/sphinx/source/000077500000000000000000000000001226300046600156565ustar00rootroot00000000000000ufl-1.3.0/doc/sphinx/source/_themes/000077500000000000000000000000001226300046600173025ustar00rootroot00000000000000ufl-1.3.0/doc/sphinx/source/_themes/fenics/000077500000000000000000000000001226300046600205515ustar00rootroot00000000000000ufl-1.3.0/doc/sphinx/source/_themes/fenics/README000066400000000000000000000001761226300046600214350ustar00rootroot00000000000000This theme is based on the default 'haiku' theme from Sphinx. Modified by Anders Logg for the FEniCS Project web pages, 2011. ufl-1.3.0/doc/sphinx/source/_themes/fenics/layout.html000066400000000000000000000044501226300046600227570ustar00rootroot00000000000000{# fenics/layout.html ~~~~~~~~~~~~~~~~~~ Sphinx layout template for the fenics theme. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. This theme is based on the default 'haiku' theme from Sphinx. Modified by Anders Logg for the FEniCS Project web pages, 2011. #} {% extends "basic/layout.html" %} {% set script_files = script_files + ['_static/platform_sniff.js'] %} {% set css_files = css_files + ['_static/print.css'] %} {# do not display relbars #} {% block relbar1 %}{% endblock %} {% block relbar2 %}{% endblock %} {% macro nav() %}

{%- block fenicsrel1 %} {%- endblock %} About     Installation     Documentation     Developers     Citing     Contact {%- block fenicsrel2 %} {%- endblock %}

{% endmacro %} {% block content %}
{%- block fenicsheader %} {%- if theme_full_logo != "false" %} {%- else %} {%- if logo -%} {%- endif -%}

{{ shorttitle|e }}

{{ title|striptags|e }}

{%- endif %} {%- endblock %}
{{ nav() }}
{#{%- if display_toc %}

Table Of Contents

{{ toc }}
{%- endif %}#} {% block body %}{% endblock %}
{{ nav() }}
{% endblock %} ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/000077500000000000000000000000001226300046600220405ustar00rootroot00000000000000ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/alert_info_32.png000066400000000000000000000022201226300046600251700ustar00rootroot00000000000000PNG  IHDR szzWIDATXýohUu?"JzћWzqzq ]d܆.d54܄;5TҴ?i4Gd Mm^ݷLKbн=r<| cp'̣@&Vx8At,('"I'{ckꗥ o`0DQ:!:OnzVk%{u՜% [#RqԪ~7;(>`fKe? !H16O & ['U@8d =r3͔G8&/˅-*de `<<,X(lN`u"coEj2 Ijxp p:ְR.pe–maiQrP P!TxH5[rJmSyf|a5 (=*"®k"ג?K\}S=*iHCo,*8[xIM+טPlVn*9.5x;Y,/Kޒ.x?DZ 75+p[V/pS$zCE0%+-U,l("@m٤[bܵ`jƢ<O^YsEMdա=!ux o~\(9OV/_,P^V0S)Uޫje/~MĮOۇ@rD $L/OcbsJ~ӛj]FINZA`?0x!0#sS5;x7l(_.X+NaS\@~-h/5?._z9.NAzS|lbW2`mC.S?+pG 2Æ `.P.*[^⤢s:מT$)܁x #\?0OeEI/ɫVY WYK\Pl o/HtKt˕ HE`7ޏ"rQD:_gCqG &Y%ߐ!$IENDB`ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/alert_warning_32.png000066400000000000000000000020441226300046600257060ustar00rootroot00000000000000PNG  IHDR szzIDATXML\U o((e &`uUvlUcM7mhbDn(u4F4ta1PB(-TZt޻y3yL;'}ssszX)`os;\df7x᜜z] FUqDtNDDԖsO^+239*zKΜ:ϜH24=g )@N$љmWPmGwV e ,X`Hܭ9S_Zt ' (PggPK4@*Bh * ;P@5) A@9 eunjnb..R-Q[ًȗt30'fQQpʍO8 P1avʮ 37 /azM,ýv Ä뭼eCp` ^=_}mBt4 B*cl) `A]Trh?~_Ke:p#dښiFPVt& cn*p  trfrHi`[B vpAg/IDAT u? .r/P5z=,%&;%tEXtdate:create2010-01-07T12:49:06+01:00:y%tEXtdate:modify2010-01-07T12:49:06+01:00KtIENDB`ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/dolfin.png000066400000000000000000000010731226300046600240220ustar00rootroot00000000000000PNG  IHDR*sRGB pHYsnu>tIME  0IDAT(mKTqϽe^9 ,AE-APЦE6B(pc?3RhM38@|m*l|ι b*;vd'*"#\NwvI X6cDilUeʚ/f{YI&"U+qw韹EfR&6KA gf}4;^fV8'x/ߡaA(+5Fmzl·ZJTUBEfݸw@-yf %i؅;+kVU'r1{\5DPZQ2FA(/&Jf׶8q=^}t:M_]ut`1:4<;D4R]7x H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F9IDATxlKHTa?g:ye" jW "0"ADvF("hU *+\t3 8̜˜og/KǠ|P>(Ri( 8@RUb$b3-pz7[zZxu(}7+j %  j}+f]Qnjܰh\?IC킴FSחl5oFu($&|)+Kz`Rgi $M \(Y+- &)WUnlW^";|ǡp JBw`rUtk!1nәm*N/!dJ dl(#b()~=zKUepa“q~maEZ=V \J |#`*lуjݔcG@> dk=1CaL#!EfµF6M]q.4zە>GLNyQ!P'ƜhiӜ1hgrP=IENDB`ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/fenics.css_t000066400000000000000000000157401226300046600243530ustar00rootroot00000000000000/* * fenics.css_t * ~~~~~~~~~~~~ * * Sphinx stylesheet -- fenics theme. * * Adapted from http://haiku-os.org/docs/Haiku-doc.css. * Original copyright message: * * Copyright 2008-2009, Haiku. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Francois Revol * Stephan Assmus * Braden Ewing * Humdinger * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * * This theme is based on the default 'haiku' theme from Sphinx. * Modified by Anders Logg for the FEniCS Project web pages, 2011. */ @import url("basic.css"); html { margin: 0px; padding: 0px; } body { line-height: 1.5; margin: auto; padding: 0px; font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; min-width: 59em; max-width: 70em; color: {{ theme_textcolor }}; } div.footer { padding: 8px; font-size: 11px; text-align: center; letter-spacing: 0.5px; } /* link colors and text decoration */ a:link { font-weight: bold; text-decoration: none; color: {{ theme_linkcolor }}; } a:visited { font-weight: bold; text-decoration: none; color: {{ theme_visitedlinkcolor }}; } a:hover, a:active { text-decoration: underline; color: {{ theme_hoverlinkcolor }}; } /* Some headers act as anchors, don't give them a hover effect */ h1 a:hover, a:active { text-decoration: none; color: {{ theme_headingcolor }}; } h2 a:hover, a:active { text-decoration: none; color: {{ theme_headingcolor }}; } h3 a:hover, a:active { text-decoration: none; color: {{ theme_headingcolor }}; } h4 a:hover, a:active { text-decoration: none; color: {{ theme_headingcolor }}; } a.headerlink { color: #a7ce38; padding-left: 5px; } a.headerlink:hover { color: #a7ce38; } /* basic text elements */ div.content { margin-top: 20px; margin-left: 40px; margin-right: 40px; margin-bottom: 50px; font-size: 0.9em; } /* heading and navigation */ div.header { position: relative; left: 0px; top: 0px; height: 85px; padding: 0 40px; background: #FFF url(bg-page.png) top left repeat-x; color: #ffffff; } div.header h1 { font-size: 1.6em; font-weight: normal; letter-spacing: 1px; color: {{ theme_headingcolor }}; border: 0; margin: 0; padding-top: 15px; } div.header h1 a { font-weight: normal; color: #ffffff; } div.header h2 { font-size: 1.3em; font-weight: normal; letter-spacing: 1px; text-transform: uppercase; color: #e0e0e0; border: 0; margin-top: -3px; padding: 0; } div.header img.rightlogo { float: right; } div.title { font-size: 1.3em; font-weight: bold; color: {{ theme_headingcolor }}; border-bottom: dotted thin #e0e0e0; margin-bottom: 25px; color: #ffffff; } div.topnav { background: #e0e0e0; } div.topnav p { margin-top: 0; margin-left: 40px; margin-right: 40px; margin-bottom: 0px; text-align: right; font-size: 0.8em; } div.bottomnav { background: #d81e28; } div.bottomnav p { margin-right: 40px; text-align: right; font-size: 0.8em; } a.uplink { font-weight: normal; } /* contents box */ table.index { margin: 0px 0px 30px 30px; padding: 1px; border-width: 1px; border-style: dotted; border-color: #e0e0e0; } table.index tr.heading { background-color: #e0e0e0; text-align: center; font-weight: bold; font-size: 1.1em; } table.index tr.index { background-color: #eeeeee; } table.index td { padding: 5px 20px; } table.index a:link, table.index a:visited { font-weight: normal; text-decoration: none; color: {{ theme_linkcolor }}; } table.index a:hover, table.index a:active { text-decoration: underline; color: {{ theme_hoverlinkcolor }}; } /* FEniCS User Guide styles and layout */ /* Rounded corner boxes */ /* Common declarations */ div.admonition { -webkit-border-radius: 10px; -khtml-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; border-style: dotted; border-width: thin; border-color: #dcdcdc; padding: 10px 15px 10px 15px; margin-bottom: 15px; margin-top: 15px; } div.note { padding: 10px 15px 10px 80px; background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat; min-height: 42px; } div.warning { padding: 10px 15px 10px 80px; background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat; min-height: 42px; } div.seealso { background: #e4ffde; } /* More layout and styles */ h1 { font-size: 1.3em; font-weight: bold; color: {{ theme_headingcolor }}; border-bottom: dotted thin #e0e0e0; margin-top: 30px; } h2 { font-size: 1.2em; font-weight: normal; color: #000000; border-bottom: dotted thin #e0e0e0; margin-top: 30px; } h3 { font-size: 1.1em; font-weight: normal; color: {{ theme_headingcolor }}; margin-top: 30px; } h4 { font-size: 1.0em; font-weight: normal; color: {{ theme_headingcolor }}; margin-top: 30px; } p { text-align: justify; } p.last { margin-bottom: 0; } ol { padding-left: 20px; } ul { padding-left: 5px; margin-top: 3px; } li { line-height: 1.3; } div.content ul > li { -moz-background-clip:border; -moz-background-inline-policy:continuous; -moz-background-origin:padding; background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em; list-style-image: none; list-style-type: none; padding: 0 0 0 1.666em; margin-bottom: 3px; } td { vertical-align: top; } tt { background-color: #e2e2e2; font-size: 1.0em; font-family: monospace; } pre { border-color: #0c3762; border-style: dotted; border-width: thin; margin: 0 0 12px 0; padding: 0.8em; background-color: #f0f0f0; } hr { border-top: 1px solid #ccc; border-bottom: 0; border-right: 0; border-left: 0; margin-bottom: 10px; margin-top: 20px; } /* printer only pretty stuff */ @media print { .noprint { display: none; } /* for acronyms we want their definitions inlined at print time */ acronym[title]:after { font-size: small; content: " (" attr(title) ")"; font-style: italic; } /* and not have mozilla dotted underline */ acronym { border: none; } div.topnav, div.bottomnav, div.header, table.index { display: none; } div.content { margin: 0px; padding: 0px; } html { background: #FFF; } } .viewcode-back { font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; margin: -1px -12px; padding: 0 12px; } div.title a:visited { color: #ffffff; } ufl-1.3.0/doc/sphinx/source/_themes/fenics/static/unknown.png000066400000000000000000000053621226300046600242530ustar00rootroot00000000000000PNG  IHDR* pHYs B(x OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxb_@`b *U E<߿IENDB`ufl-1.3.0/doc/sphinx/source/_themes/fenics/theme.conf000066400000000000000000000003631226300046600225240ustar00rootroot00000000000000[theme] inherit = basic stylesheet = fenics.css pygments_style = autumn [options] full_logo = false textcolor = #333333 headingcolor = #d81e28 linkcolor = #d81e28 visitedlinkcolor = #d81e28 hoverlinkcolor = #d8363f ufl-1.3.0/doc/sphinx/source/conf.py000066400000000000000000000155201226300046600171600ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath( os.path.join('..', '..', '..') )) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.1' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'UFL' copyright = u'FEniCS Project, http://www.fenicsproject.org/' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.2' # The full version, including alpha/beta/rc tags. release = '1.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # 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 patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # 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' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ["ufl.", "ufl.finiteelement", "ufl.algorithms."] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' #html_theme = 'fenics' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ["_themes"] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "FEniCS Project" # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # 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 = None # 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_domain_indices = 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, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'UFLdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # 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, documentclass [howto/manual]). #latex_documents = [] # 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 # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # Parameters affecting the LaTeX PNGs in the HTML files pngmath_latex_preamble = r" \usepackage{stmaryrd} " # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} # Use docstrings both from __init__ and class when using autodoc for # Python classes autoclass_content = "both" ufl-1.3.0/release.conf000066400000000000000000000002421226300046600145650ustar00rootroot00000000000000# Configuration file for fenics-release PACKAGE="ufl" BRANCH="master" FILES="ChangeLog ufl/__init__.py setup.py" POST_FILES="ChangeLog ufl/__init__.py setup.py" ufl-1.3.0/sandbox/000077500000000000000000000000001226300046600137365ustar00rootroot00000000000000ufl-1.3.0/sandbox/README000066400000000000000000000011331226300046600146140ustar00rootroot00000000000000This directory contains unofficial scripts, snippets, test codes, and whatnot. Main structure of this directory: operatortests/ - test snippets for operators (these should usually become unit tests) algorithmtests/ - test snippets for algorithms scripts/ - utility scripts that perform some minor task suggestions/ - for .ufl files with new suggested syntax profiling/ - scripts doing something to be profiled pylintlog/ - pylint script for doing static code analysis on ufl Please try to keep it tidy by at least making a personal folder for messy stuff. ufl-1.3.0/sandbox/algorithmtests/000077500000000000000000000000001226300046600170075ustar00rootroot00000000000000ufl-1.3.0/sandbox/algorithmtests/HyperElasticity1D_debug.py000066400000000000000000000013551226300046600240420ustar00rootroot00000000000000#!/usr/bin/env python from ufl import * from ufl.algorithms import * set_level(DEBUG) cell = interval element = FiniteElement("CG", cell, 2) u = Function(element) b = Constant(cell) K = Constant(cell) E = u.dx(0) + u.dx(0)**2 / 2 E = variable(E) Q = b*E**2 psi = K*(exp(Q)-1) f = psi*dx F = derivative(f, u) J = derivative(-F, u) forms = [f, F, J] print "=== f" + "="*70 print str(f) print "=== F" + "="*70 print str(F) print "=== J" + "="*70 print str(J) print "=== f" + "="*70 print str(strip_variables(f.form_data().form)) print "=== F" + "="*70 print str(strip_variables(F.form_data().form)) print "=== J" + "="*70 print str(strip_variables(J.form_data().form)) print print str(expand_indices(strip_variables(J.form_data().form))) ufl-1.3.0/sandbox/algorithmtests/HyperElasticitySVK_debug.py000066400000000000000000000030271226300046600242370ustar00rootroot00000000000000#!/usr/bin/env python from ufl import * from ufl.algorithms import * set_level(DEBUG) # Cell and its properties #cell = tetrahedron cell = triangle # Elements u_element = VectorElement("CG", cell, 1) p_element = FiniteElement("CG", cell, 1) # Test and trial functions v = TestFunction(u_element) w = TrialFunction(u_element) # Displacement at current and two previous timesteps u = Function(u_element) up = Function(u_element) upp = Function(u_element) # Time parameters dt = Constant(cell) # Material parameters lamda = Constant(cell) mu = Constant(cell) # Deformation gradient I = Identity(cell.d) F = I + grad(u).T # Right Cauchy-Green deformation tensor C = F.T*F # Green strain tensor E = (C-I)/2 E = variable(E) # Strain energy function psi(Q(Ef)), Saint Vernant-Kirchoff law psi = lamda/2 * tr(E)**2 + mu * inner(E, E) # First Piola-Kirchoff stress tensor P = F*diff(psi, E) # Energy functional (without acceleration term and external forces) a_f = psi*dx # Residual equation a_F = inner(P, grad(v))*dx \ # Jacobi matrix of residual equation a_J = derivative(a_F, u, w) forms = [a_f, a_F, a_J] print "=== f" + "="*70 print str(a_f) print "=== F" + "="*70 print str(a_F) print "=== J" + "="*70 print str(a_J) print "=== f" + "="*70 print str(strip_variables(a_f.form_data().form)) print "=== F" + "="*70 print str(strip_variables(a_F.form_data().form)) print "=== J" + "="*70 print str(strip_variables(a_J.form_data().form)) print #print str(expand_indices(strip_variables(a_J.form_data().form))) # pretty large expression... ufl-1.3.0/sandbox/algorithmtests/debug_renumber_indices.py000066400000000000000000000016431226300046600240500ustar00rootroot00000000000000 from ufl import * from ufl.classes import * from ufl.algorithms import * def printit(a, tree=False): print "-"*80 print "a =" print str(a) if isinstance(a, str): return if tree: print print tree_format(a) a = renumber_indices(a) print print "renumbered a =" print str(a) if tree: print print tree_format(a) a = expand_indices(a) print print "expanded a =" print str(a) if tree: print print tree_format(a) print velement = VectorElement("Lagrange", triangle, 1) v = Function(velement) telement = TensorElement("Lagrange", triangle, 1) A = Function(telement) I = Identity(2) #a = as_tensor(v[i], i)[k] * I[k,0] if True: a = A[j,q] b = as_tensor(a, q) h = b[i] g = as_tensor( h, (j, i) ) f = g[k,l] a = f * I[k,l] printit(a, True) #a = IndexSum( Product( Indexed( v , ufl-1.3.0/sandbox/algorithmtests/degree_estimation.py000066400000000000000000000036101226300046600230500ustar00rootroot00000000000000 from ufl import * from ufl.common import * from ufl.classes import * from ufl.algorithms import * from ufl.algorithms.transformations import Transformer, apply_transformer class DegreeEstimator(Transformer): def __init__(self): Transformer.__init__(self) def terminal(self, v): return 0 def expr(self, v, *ops): return max(ops) def form_argument(self, v): return v.element().degree() def spatial_derivative(self, v, f, i): return max(f-1, 0) def product(self, v, *ops): return sum(ops) def power(self, v, a, b): f, g = v.operands() try: gi = int(g) return a*gi except: pass # Something to a non-integer power... TODO: How to handle this? if b > 0: return a*b return a def estimate_max_quad_degree(e): return apply_transformer(e, DegreeEstimator()) from ufl.testobjects import * if __name__ == "__main__": print estimate_max_quad_degree(v) print estimate_max_quad_degree(u*v) print estimate_max_quad_degree(vv[0]) print estimate_max_quad_degree(u*vv[0]) print estimate_max_quad_degree(vu[0]*vv[0]) print estimate_max_quad_degree(u*v) print estimate_max_quad_degree(vu[i]*vv[i]) print V1 = FiniteElement("CG", triangle, 1) V2 = FiniteElement("CG", triangle, 2) VM = V1 + V2 v = TestFunction(V1) u = TrialFunction(V2) f, g = Functions(VM) print estimate_max_quad_degree(u) print estimate_max_quad_degree(v) print estimate_max_quad_degree(f) print estimate_max_quad_degree(g) print estimate_max_quad_degree(u*v) print estimate_max_quad_degree(f*v) print estimate_max_quad_degree(g*v) print estimate_max_quad_degree(g*u*v) print estimate_max_quad_degree(f*g*u.dx(0)*v.dx(0)) print estimate_max_quad_degree(g**3*v + f*v) ufl-1.3.0/sandbox/algorithmtests/derivative.py000066400000000000000000000021341226300046600215230ustar00rootroot00000000000000 from ufl import * from ufl.algorithms import * # Function spaces X = VectorElement('CG', triangle, 1) Y = FiniteElement('CG', triangle, 1) XY = X + Y # Solution function (in mixed space) u = Function(XY) # Basis functions (in mixed space) vv = TestFunction(XY) uu = TrialFunction(XY) # Form coefficients for do_split in (True, False): if do_split: x, y = split(u) else: x = Function(X) y = Function(Y) # Forms (ok! means verified in detail by hand) #L = dot(x,x)*dx # ok! #L = dot(x,x)*y*dx # ok! #L = inner(grad(x),grad(x))*dx # ok! L = inner(grad(x),grad(x))*dx + dot(x,x)*y*dx # ok! if do_split: F = derivative(L, u, vv) J = derivative(F, u, uu) else: F = derivative(L, (x,y), vv) J = derivative(F, (x,y), uu) print "="*80 print do_split print print print "F" print str(F.form_data().form) print print str(expand_indices(F.form_data().form)) print print "J" print str(J.form_data().form) print print str(expand_indices(J.form_data().form)) print ufl-1.3.0/sandbox/algorithmtests/fast_expand_indices.py000066400000000000000000000013661226300046600233610ustar00rootroot00000000000000 from ufl import * from ufl.common import * from ufl.classes import * from ufl.algorithms import * from ufl.testobjects import * if __name__ == "__main__": v = BasisFunction(element) u = BasisFunction(element) print expand_indices2(v) print expand_indices2(u*v) print expand_indices2(vv[0]) print expand_indices2(u*vv[0]) print expand_indices2(vu[0]*vv[0]) print expand_indices2(u*v) print expand_indices2(vu[i]*vv[i]) print expand_indices2(vu[i]*as_vector([2,3])[i]) print expand_indices2(as_vector(vu[i], i)[j]*vv[j]) print expand_indices2(expand_compounds( dot(dot(vu, as_tensor(vu[i]*vv[j], (i,j))), vv) ) ) print expand_indices(expand_compounds( dot(dot(vu, as_tensor(vu[i]*vv[j], (i,j))), vv) ) ) ufl-1.3.0/sandbox/algorithmtests/renumber_indices.py000066400000000000000000000042001226300046600226720ustar00rootroot00000000000000 from ufl import * from ufl.classes import * from ufl.algorithms import * velement = VectorElement("Lagrange", triangle, 1) v = Function(velement) telement = TensorElement("Lagrange", triangle, 1) A = Function(telement) I = Identity(2) def printit(a, tree=False): print "-"*80 print print "a =" print str(a) if isinstance(a, str): return if tree: print print tree_format(a) a = renumber_indices(a) print print "renumbered a =" print str(a) if tree: print print tree_format(a) a = expand_indices(a) print print "expanded a =" print str(a) if tree: print print tree_format(a) print if __name__ == "__main__": tests = [ \ # constant v[0], A[0,0], # constant to index mapping as_tensor(v[i], i)[0] , as_tensor(A[i,j], (j,i))[1,2] , # index to index mapping as_tensor(v[i], i)[j] * v[j] , as_tensor(A[k,l], (l,k))[j,i] * as_tensor(A[k,l], (l,k))[i,j] , # hidden implicit sum! as_tensor(A[i,j], j)[i] , # => sum_i # partial mapping as_tensor(A[i,0], i)[1] , as_tensor(A[1,i], i)[2] , # double layers as_tensor(A[j,:][i], (j, i))[k,l]*I[k,l], # double index meaning (v[i]*v[i]) * (v[i]*v[i]), (v[i]*v[i]) * (2*v[i]*v[i]), ] for a in tests: printit(a, True) #test2() # Failure on this one: #mixed = MixedElement(*[VectorElement('Lagrange', Cell('triangle', 1), 2, 2), # FiniteElement('Lagrange', Cell('triangle', 1), 1)], # **{'value_shape': (3,) }) #Indexed( # ComponentTensor( # Indexed( # ListTensor(Indexed(SpatialDerivative(BasisFunction(mixed, -2), MultiIndex((Index(12),))), MultiIndex((FixedIndex(0),))), # Indexed(SpatialDerivative(BasisFunction(mixed, -2), MultiIndex((Index(12),))), MultiIndex((FixedIndex(1),)))), # MultiIndex((Index(13),)) # ), # MultiIndex((Index(12), Index(13))) # ), # MultiIndex((Index(14), Index(15))) #) ufl-1.3.0/sandbox/algorithmtests/simplification_fail.py000066400000000000000000000007271226300046600233740ustar00rootroot00000000000000 # # An example that improved simplification could optimize. # from ufl import * from ufl.algorithms import expand_indices x = triangle.x e = (x[i] - x[i])*x[i] a = e*dx print a #{ sum_{i_0} ((x)[i_0]) * (((x)[i_0]) + -1 * ((x)[i_0])) } * dx0 fd = a.form_data() b = fd.form print b #{ sum_{i_0} ((x)[i_0]) * (((x)[i_0]) + -1 * ((x)[i_0])) } * dx0 print expand_indices(b) #{ ((x)[0]) * (((x)[0]) + -1 * ((x)[0])) + ((x)[1]) * (((x)[1]) + -1 * ((x)[1])) } * dx0 ufl-1.3.0/sandbox/algorithmtests/symmetry_expand_indices.py000066400000000000000000000005611226300046600243110ustar00rootroot00000000000000 from ufl import * from ufl.algorithms import * e = TensorElement("CG", triangle, 1, symmetry=True) f = Function(e) v = TestFunction(e) a = inner(f, v)*dx fd = a.form_data() b = fd.form c = expand_indices(b) print tree_format(c) print str(c) # (v_0)[0, 0] * (w_0)[0, 0] # + (v_0)[0, 1] * (w_0)[0, 1] # + (v_0)[0, 1] * (w_0)[0, 1] # + (v_0)[1, 1] * (w_0)[1, 1] ufl-1.3.0/sandbox/algorithmtests/test_expand_indices.py000066400000000000000000000014021226300046600233720ustar00rootroot00000000000000 from ufl import * from ufl.classes import * from ufl.algorithms import * def test1(): #name = "../../demo/StiffnessAD.ufl" name = "../../demo/Constant.ufl" forms = load_forms(name) for f in forms: fd = f.form_data() g = fd.form print print fd.name print print str(f) print print str(g) print print str(expand_indices(g)) print def test2(): element = FiniteElement("Lagrange", triangle, 2) v = TestFunction(element) u = TrialFunction(element) a = div(grad(v))*u*dx print tree_format(a) a = expand_derivatives(a) print tree_format(a) a = expand_indices(a) print tree_format(a) if __name__ == "__main__": test2() ufl-1.3.0/sandbox/algorithmtests/test_purge_list_tensors.py000066400000000000000000000005651226300046600243600ustar00rootroot00000000000000from ufl import * from ufl.algorithms import purge_list_tensors element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) #a = inner(as_tensor([v[0], v[1]]), as_tensor([u[0], u[1]])) a = inner(as_tensor([v[0], v[1]]), as_tensor([u[0], u[1].dx(0)]))*dx fd = a.form_data() b = fd.form c = purge_list_tensors(b) print str(c) ufl-1.3.0/sandbox/algorithmtests/test_restrictions.py000066400000000000000000000003301226300046600231440ustar00rootroot00000000000000 from ufl import * e = FiniteElement("DG", triangle, 1) f = Function(e) g = Function(e) a = (grad(f) + grad(g))('-') print a from ufl.algorithms import propagate_restrictions b = propagate_restrictions(a) print b ufl-1.3.0/sandbox/algorithmtests/uflfiles/000077500000000000000000000000001226300046600206205ustar00rootroot00000000000000ufl-1.3.0/sandbox/algorithmtests/uflfiles/common.ufl000066400000000000000000000002121226300046600226130ustar00rootroot00000000000000q = 1 cell = triangle element = FiniteElement("CG", cell, q) v = TestFunction(element) u = TrialFunction(element) f = Function(element) ufl-1.3.0/sandbox/algorithmtests/uflfiles/mass.ufl000066400000000000000000000000731226300046600222730ustar00rootroot00000000000000#include common.ufl # Mass form: a = u*v*dx # End of file ufl-1.3.0/sandbox/algorithmtests/uflfiles/stiffness.ufl000066400000000000000000000000621226300046600233320ustar00rootroot00000000000000#include common.ufl a = inner(grad(u),grad(v))*dx ufl-1.3.0/sandbox/algorithmtests/uflfiles/test.py000066400000000000000000000003131226300046600221460ustar00rootroot00000000000000 from ufl.algorithms import load_forms m = load_forms("mass.ufl") s = load_forms("stiffness.ufl") print "mass:" print "\n".join(str(f) for f in m) print "stiffness:" print "\n".join(str(f) for f in s) ufl-1.3.0/sandbox/gdimtest.py000066400000000000000000000006661226300046600161400ustar00rootroot00000000000000from dolfin import * def test(): mesh1 = UnitIntervalMesh(10) mesh2 = UnitSquareMesh(10,10) mesh3 = UnitCubeMesh(10,10,10) for mesh in (mesh1, mesh2, mesh3): cell = mesh.ufl_cell() d = cell.d x = cell.x n = cell.n I = Identity(d) assert I.shape() == (d,d) M = I[i,j]*n[i]*x[j]*ds value = assemble(M, mesh=mesh) assert abs(value - d) < 1e-10 test() ufl-1.3.0/sandbox/ipytest/000077500000000000000000000000001226300046600154375ustar00rootroot00000000000000ufl-1.3.0/sandbox/ipytest/graph.py000066400000000000000000000022141226300046600171110ustar00rootroot00000000000000 class A: def _repr_png_(self): import matplotlib.pyplot as plt import networkx as nx G = nx.Graph() G.add_nodes_from([1, 2,3]) G.add_edges_from([(1,2),(1,3), (2,3)]) fig = plt.figure() nx.draw_spring(G) #fig, ax = plt.subplots() from IPython.core.pylabtools import print_figure data = print_figure(fig, 'png') # We MUST close the figure, otherwise IPython's display machinery # will pick it up and send it as output, resulting in a double display plt.close(fig) return data if 0: # Other way found on internet: import networkx as nx import matplotlib.pyplot as plt import StringIO from matplotlib.figure import Figure class MyGraph(nx.Graph): def _repr_svg_(self): plt.ioff() # turn off interactive mode fig = plt.figure(figsize=(2,2)) ax = fig.add_subplot(111) nx.draw_shell(self, ax=ax) output = StringIO.StringIO() fig.savefig(output,format='svg') plt.ion() # turn on interactive mode return output.getvalue() ufl-1.3.0/sandbox/operatortests/000077500000000000000000000000001226300046600166545ustar00rootroot00000000000000ufl-1.3.0/sandbox/operatortests/test_as_tensor.py000066400000000000000000000010201226300046600222530ustar00rootroot00000000000000 from ufl import * from ufl.tensors import ListTensor n = FacetNormal(tetrahedron) components = [ \ (n, n), ((n, n), (n, n)), (((n, n), (n, n)), ((n, n), (n, n))), ((((n, n), (n, n)), ((n, n), (n, n))), (((n, n), (n, n)), ((n, n), (n, n)))), ] for c in components: print print print "c = ", c t = ListTensor(*c) t2 = as_tensor(c) print "equal =", t == t2 print "shape =", t.shape() print print str(t) print print repr(t) print print ufl-1.3.0/sandbox/operatortests/test_product.py000066400000000000000000000005331226300046600217460ustar00rootroot00000000000000 from ufl.testobjects import * from ufl.algorithms import expand_compounds def _show(x): print x.shape() print x.free_indices() print str(x) print repr(x) def show(x): print "--------------" _show(x) y = expand_compounds(x) _show(y) show(u*v) show(u*vv) show(vu*v) show(u*tv) show(tu*v) show(tu*vv) show(tu*tv) ufl-1.3.0/sandbox/physdim/000077500000000000000000000000001226300046600154135ustar00rootroot00000000000000ufl-1.3.0/sandbox/physdim/check_physical_dimensions.py000066400000000000000000000076401226300046600231750ustar00rootroot00000000000000 import operator from ufl.algorithms import Transformer, expand_compounds from physical_dimensions import PhysicalDimension class PhysicalDimensionChecker(Transformer): def __init__(self, xdim, function_dims): super(PhysicalDimensionChecker, self).__init__() self.xdim = xdim self.function_dims = function_dims self.warnings = [] self.nodim = PhysicalDimension({}) def warn(self, o, ops): opdims = ", ".join(map(str, ops)) w = "Operator %s has operands with dimensions %s." % (o._uflclass, opdims) self.warnings.append(w) def terminal(self, o): return self.nodim def coefficient(self, o): return self.function_dims.get(o, self.nodim) def argument(self, o): return self.function_dims.get(o, self.nodim) def spatial_coordinate(self, o, *ops): return self.xdim def expr(self, o, *ops): if any(ops): self.warn(o, ops) return self.nodim def indexed(self, o, *ops): return ops[0] # TODO: This and other types: #def list_tensor(self, o, *ops): # o0 = ops[0] # for o1 in ops: # if o1 != o0: # self.warn(o, ops) # return self.nodim # return o0 def sum(self, o, *ops): o0 = ops[0] for o1 in ops: if o1 != o0: self.warn(o, ops) return self.nodim return o0 def product(self, o, *ops): if any(ops): return reduce(operator.__mul__, ops) else: return self.nodim def division(self, o, *ops): if any(ops): return ops[0]/ops[1] else: return self.nodim def sqrt(self, o, op): return op**0.5 def power(self, o, *ops): if ops[1]: self.warn(o, ops) elif ops[0]: try: m = o.operands()[1] f = float(m) i = int(m) except: self.warn(o, ops) return self.nodim if f == i: return ops[0]**i else: return ops[0]**f else: return self.nodim def spatial_derivative(self, o, *ops): return ops[0] / self.xdim grad = spatial_derivative div = spatial_derivative curl = spatial_derivative rot = spatial_derivative def check_physical_dimensions(expr, xdim, function_dims): expr2 = expand_compounds(expr) alg = PhysicalDimensionChecker(xdim, function_dims) dim = alg.visit(expr) return dim, alg.warnings from ufl import * def test_check_physical_dimensions(): V = FiniteElement("CG", triangle, 1) f = Coefficient(V) g = Coefficient(V) m = PhysicalDimension("m") s = PhysicalDimension("s") d, w = check_physical_dimensions(f, m, {}) assert not w assert str(d) == "" d, w = check_physical_dimensions(f, m, {f: s}) assert not w assert str(d) == "s" d, w = check_physical_dimensions(f*2, m, {f: s}) assert not w assert str(d) == "s" d, w = check_physical_dimensions(f**2, m, {f: s}) if w: print w assert not w assert str(d) == "s^2" d, w = check_physical_dimensions(g*f**2, m, {f: s, g: m**2/s}) if w: print w assert not w assert str(d) == "m^2 s" # Test that sum gives no warning d, w = check_physical_dimensions(sqrt(g**2*f**2)*f + g*f**2, m, {f: s, g: m**2/s}) if w: print w assert not w assert str(d) == "m^2 s" # Test that sum gives warning d, w = check_physical_dimensions(f + g, m, {f: m/s, g: m**2/s}) #if w: print w assert w assert str(d) == "" # Test derivatives and x which uses xdim d, w = check_physical_dimensions(f.dx(0) + grad(f)[0] + div(grad(f))*triangle.x[0], m, {f: m/s}) if w: print w assert not w assert str(d) == "s^-1" if __name__ == "__main__": test_check_physical_dimensions() ufl-1.3.0/sandbox/physdim/physical_dimensions.py000066400000000000000000000044451226300046600220400ustar00rootroot00000000000000from collections import defaultdict class PhysicalDimension: def __init__(self, dims): self.counts = defaultdict(int) if isinstance(dims, str): self.counts[dims] = 1 elif isinstance(dims, dict): self.counts.update(dims) else: raise Exception("Invalid dimension type.") self.normalize() def normalize(self): keys = [d for (d,c) in self.counts.iteritems() if c == 0] for k in keys: del self.counts[k] def __mul__(self, other): keys = set(self.counts) | set(other.counts) counts = dict() for k in keys: c = self.counts.get(k, 0) + other.counts.get(k, 0) if c: counts[k] = c return PhysicalDimension(counts) def __div__(self, other): keys = set(self.counts) | set(other.counts) counts = dict() for k in keys: c = self.counts.get(k, 0) - other.counts.get(k, 0) if c: counts[k] = c return PhysicalDimension(counts) def __pow__(self, n): keys = set(self.counts) counts = dict() for k in keys: c = self.counts.get(k, 0)*n if c: if c == int(c): c = int(c) counts[k] = c return PhysicalDimension(counts) def __eq__(self, other): return self.counts == other.counts def __ne__(self, other): return self.counts != other.counts def __nonzero__(self): return bool(self.counts) def __bool__(self): return bool(self.counts) def __str__(self): def fmt(d): c = self.counts[d] return d if c == 1 else "%s^%g" % (d, c) return " ".join(map(fmt, sorted(self.counts.keys()))) def test_physical_dimension(): m = PhysicalDimension("m") s = PhysicalDimension("s") assert "m" == str(m) assert "s" == str(s) assert "m^2" == str(m**2) assert "s^2" == str(s**2) assert "m s^3" == str(m*s**2*m*s/m) assert "m s^2" == str(m*s**2) assert m == m assert m == PhysicalDimension("m") assert m != s assert m**2 == m**2 assert m**2 != s**2 assert m**3 != m**2 assert m**2*s != m*s**2 if __name__ == "__main__": test_physical_dimension() ufl-1.3.0/sandbox/profiling/000077500000000000000000000000001226300046600157275ustar00rootroot00000000000000ufl-1.3.0/sandbox/profiling/ExpandIndices2Fail.ufl000066400000000000000000000033531226300046600220370ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-12-22 # # Cell and its properties cell = tetrahedron d = cell.d n = cell.n x = cell.x # Elements u_element = VectorElement("CG", cell, 2) p_element = FiniteElement("CG", cell, 1) A_element = TensorElement("CG", cell, 1) # Test and trial functions v = TestFunction(u_element) w = TrialFunction(u_element) # Displacement at current and two previous timesteps u = Function(u_element) up = Function(u_element) upp = Function(u_element) # Time parameters dt = Constant(cell) # Fiber field A = Function(A_element) # External forces T = Function(u_element) p0 = Function(p_element) N = cell.n # Material parameters FIXME rho = Constant(cell) K = Constant(cell) c00 = Constant(cell) c11 = Constant(cell) c22 = Constant(cell) # Deformation gradient I = Identity(d) F = I + grad(u).T F = variable(F) Finv = inv(F) J = det(F) # Left Cauchy-Green deformation tensor B = F*F.T I1_B = tr(B) I2_B = (I1_B**2 - tr(B*B))/2 I3_B = J**2 # Right Cauchy-Green deformation tensor C = F.T*F I1_C = tr(C) I2_C = (I1_C**2 - tr(C*C))/2 I3_C = J**2 # Green strain tensor E = (C-I)/2 # Mapping of strain in fiber directions Ef = A*E*A.T # Strain energy function W(Q(Ef)) Q = c00*Ef[0,0]**2 + c11*Ef[1,1]**2 + c22*Ef[2,2]**2 # FIXME: insert some simple law here W = (K/2)*(exp(Q) - 1) # + p stuff # First Piola-Kirchoff stress tensor P = diff(W, F) # Acceleration term discretized with finite differences k = dt/rho acc = (u - 2*up + upp) # Residual equation # FIXME: Can contain errors, not tested! a_F = inner(acc, v)*dx \ + k*inner(P, grad(v))*dx \ - k*dot(J*Finv*T, v)*ds(0) \ - k*dot(J*Finv*p0*N, v)*ds(1) # Jacobi matrix of residual equation a_J = derivative(a_F, u, w) # Export forms forms = [a_F, a_J] ufl-1.3.0/sandbox/profiling/compile_form.py000077500000000000000000000053721226300046600207660ustar00rootroot00000000000000#!/usr/bin/env python """ Compile a linarized and partitioned graph for each integral for each form in a .ufl file. """ import sys, time _msg = None _t = None _time_log = [] def tic(msg): global _t, _msg if _t is not None: toc() _t = -time.time() _msg = msg def toc(): global _t, _msg, _time_log t = _t+time.time() # format message and seconds msgtext = (" for %s" % _msg) if _msg else "" if t < 1e-2: ttext = "0 s" else: if t >= 60: ttext = "%.2f min" % (t / 60) else: ttext = "%.2f s" % t # log and print text line = "Time taken%s: %s" % (msgtext, ttext) _time_log.append((line, _msg, t)) print line _t = None _msg = None from operator import itemgetter def print_timelog(): lines = [] msize = 0 tsize = 0 for l, m, t in _time_log: msize = max(msize, len(str(m))) tsize = max(tsize, len(str(t))) time_log = sorted(_time_log, key=itemgetter(2), reverse=True) format = "%%%ds: \t%%.2f" % msize for l, m, t in time_log: lines.append(format % (m, t)) print "\n".join(lines) from ufl.algorithms import load_forms, expand_indices, Graph, partition filename = sys.argv[1] if len(sys.argv) > 1 else "../../demo/PoissonSystem.ufl" print "Trying to load file ", filename tic("load_forms") forms = load_forms(filename) toc() for form in forms: fd = form.form_data() f = fd.form print print "="*80 print "== Handling form %s:" % fd.name for itg in f.integrals(): print print "="*80 print "== Preparing integral %s:" % str(itg.measure()) idstr = "(%s, %s)" % (fd.name, str(itg.measure())) e = itg.integrand() tic("expand_indices %s" % idstr) e = expand_indices(e) tic("Graph %s" % idstr) G = Graph(e) V, E = G tic("Vout %s" % idstr) Vout = G.Vout() tic("partition %s" % idstr) P, keys = partition(G) toc() print print "="*80 print "== Partition sizes:" print "|V| =", len(G.V()) print "|E| =", len(G.E()) print "|P| =", len(P) for i, p in enumerate(P): print "|p%d| =" % i, len(p) print print "="*80 print "== Showing all partitions:" for key, part in P.iteritems(): print "-"*60 print "-- Partition", key for i in part: v = V[i] if Vout[i]: ops = " applied to (%s)" % ", ".join("s%d" % j for j in Vout[i]) else: ops = "" print "s%d = %s%s" % (i, v._uflclass.__name__, ops) print print "="*80 print "== Timing summary:" print_timelog() ufl-1.3.0/sandbox/profiling/test_expand_indices2.py000066400000000000000000000010561226300046600224010ustar00rootroot00000000000000 from ufl.algorithms import * forms = load_forms("ExpandIndices2Fail.ufl") from time import time for f in forms: name = f.form_data().name #if name != "a_J": continue print "="*80 print "Form ", name #print str(f) print a1 = f a2 = f t = -time() a2 = expand_indices2(a2) t += time() print "expand_indices2 time: %.3f s" % t t = -time() a1 = expand_indices(a1) t += time() print "expand_indices time: %.3f s" % t print "repr(a1) == repr(a2): ", repr(a1) == repr(a2) ufl-1.3.0/sandbox/pylintlog/000077500000000000000000000000001226300046600157575ustar00rootroot00000000000000ufl-1.3.0/sandbox/pylintlog/cleanlogs.py000077500000000000000000000005341226300046600203050ustar00rootroot00000000000000#!/usr/bin/env python import os, sys files = sys.argv[1:] def delete(f): print "rm", f os.remove(f) for f in files: lines = [l.strip() for l in open(f).readlines()] if not lines: delete(f) else: for l in lines[1:]: # Skip first line if l: break else: delete(f) ufl-1.3.0/sandbox/pylintlog/import_pychecker.py000077500000000000000000000005671226300046600217130ustar00rootroot00000000000000#!/usr/bin/env python "Find potential bugs in UFL by static code analysis." # PyChecker skips previously loaded modules import os, sys, glob, shutil, re, logging, itertools try: import numpy except: pass try: import swiginac except: pass try: import sympy except: pass try: import sympycore except: pass import pychecker.checker import ufl ufl-1.3.0/sandbox/pylintlog/runpylint.sh000077500000000000000000000013141226300046600203610ustar00rootroot00000000000000#!/bin/bash # Change pylint behaviour # Defaults: FLAGS= # Errors only: FLAGS=-e # Delete old dirs rm -rf base rm -rf algorithms mkdirhier base mkdirhier algorithms # Check base system cd base pylint $FLAGS --files-output=y --ignore=algorithms ufl # Remove incorrect ufl.log errors sed -i s/E.\*No.\*ufl.log.\*// *.txt # Remove empty log files python ../cleanlogs.py *.txt cd .. # Check algorithms cd algorithms pylint $FLAGS --files-output=y ufl.algorithms # Remove incorrect ufl.log errors sed -i s/E.\*No.\*ufl.log.\*// *.txt # Remove empty log files python ../cleanlogs.py *.txt cd .. # Print sizes echo base/ lines: cat base/*.txt | uniq | wc -l echo algorithms/ lines: cat algorithms/*.txt | uniq | wc -l ufl-1.3.0/sandbox/scratch/000077500000000000000000000000001226300046600153655ustar00rootroot00000000000000ufl-1.3.0/sandbox/scratch/adjointbug.py000066400000000000000000000012621226300046600200660ustar00rootroot00000000000000 from dolfin import * mesh = UnitSquare(3,3) V1 = FunctionSpace(mesh, "CG", 1) V2 = FunctionSpace(mesh, "CG", 2) u = TrialFunction(V1) v = TestFunction(V2) u2 = Argument(V1) v2 = Argument(V2) print 'counts:' print u.count(), v.count() print u2.count(), v2.count() #-1 -2 #1 2 a = u*v*dx A = assemble(a) print 'A size:' print A.size(0), A.size(1) #49 16 # Old buggy version, won't work with pydolfin and new ufl if 1: b = adjoint(a) B = assemble(b) print 'B size:' print B.size(0), B.size(1) #49 16 # New with fix, won't work with old ufl if 1: b = adjoint(a, (u2, v2)) B = assemble(b) print 'B size again:' print B.size(0), B.size(1) #16 49 ufl-1.3.0/sandbox/scratch/bessel.cpp000066400000000000000000000017161226300046600173530ustar00rootroot00000000000000#include #include using std::tr1::cyl_bessel_i; using std::tr1::cyl_bessel_j; using std::tr1::cyl_bessel_k; using std::tr1::cyl_neumann; //#include //using boost::math::tr1::cyl_bessel_i; //using boost::math::tr1::cyl_bessel_j; //using boost::math::tr1::cyl_bessel_k; //using boost::math::tr1::cyl_neumann; // Compile with '$ g++ test_bessel.cpp -lboost_math_tr1' //#include //using boost::math::cyl_bessel_i; //using boost::math::cyl_bessel_j; //using boost::math::cyl_bessel_k; //using boost::math::cyl_neumann; int main(int argc, char * argv[]) { double nu = 0.0; double x = 1.23; double i = cyl_bessel_i(nu, x); double j = cyl_bessel_j(nu, x); double k = cyl_bessel_k(nu, x); double y = cyl_neumann(nu, x); std::cout << i << std::endl; std::cout << j << std::endl; std::cout << k << std::endl; std::cout << y << std::endl; return 0; } ufl-1.3.0/sandbox/scratch/booltest.py000066400000000000000000000024051226300046600175730ustar00rootroot00000000000000 class Equation: def __init__(self, lhs, rhs): self.lhs = lhs self.rhs = rhs def __nonzero__(self): return repr(self.lhs) == repr(self.rhs) def __eq__(self, other): return isinstance(other, Equation) and\ bool(self.lhs == other.lhs) and\ bool(self.rhs == other.rhs) def __repr__(self): return "Equation(%r, %r)" % (self.lhs, self.rhs) class Form: def __init__(self, a): self.a = a def __hash__(self): return hash(self.a) def __eq__(self, other): return Equation(self, other) def __repr__(self): return "Form(%r)" % (self.a,) def test(): forms = [Form(1), Form(2), Form(1), Form(0)] for i, f in enumerate(forms): for j, g in enumerate(forms): print '\nTesting forms', i, j, f, g print { f: 42, g: 84 } seq = f.a == g.a sne = f.a != g.a assert (f == g) == (f == g) assert bool((f == g) == (g == f)) == seq assert bool(f == g) == seq assert isinstance(eval(repr(f)), Form) assert isinstance(eval(repr(g)), Form) assert isinstance(eval(repr(f == g)), Equation) assert len({ f: 42, g: 84 }) == (1 if f == g else 2) test() ufl-1.3.0/sandbox/scratch/curltest.py000066400000000000000000000015001226300046600176000ustar00rootroot00000000000000 from ufl import * from ufl.classes import Form from ufl.algorithms import * cell = tetrahedron x, y, z = cell.x e = VectorElement("CG", cell, 1) v = TestFunction(e) f0 = as_vector(( y, -x, 0)) f1 = as_vector(( z, 0, -x)) f2 = as_vector(( 0, x, -y)) f3 = as_vector(( x, y, z)) a0 = -2*v[2]*dx a1 = +2*v[1]*dx a2 = (v[2] - v[0])*dx a3 = Form([]) for f, b in zip((f0, f1, f2, f3), (a0, a1, a2, a3)): print "."*80 print "f = ", f a = dot(curl(f), v)*dx print "\na = ", a a = expand_compounds(a) print "\na = ", a a = expand_derivatives(a) print "\na = ", a a = renumber_indices(a) print "\na = ", a a = expand_indices(a) print "\na = ", a print a == b # #curl (y, -x, 0) = #| i j k | #| ,x ,y ,z | #| y -x 0 | #= #0i + 0j + (-x,x -y,y)k = -2k # ufl-1.3.0/sandbox/scratch/derivativetest.py000066400000000000000000000011211226300046600207740ustar00rootroot00000000000000 from ufl import * e1 = FiniteElement("CG", triangle, 1) e2 = FiniteElement("CG", triangle, 2) m = e1 + e2 # Dummy functions g = Function(e1) f = Function(e1) # Mixed function, main unknown of nonlinear system kc = Function(m) k, c = split(kc) # Functional M = k**2*c**2*dx # Correct, makes basis function automatically: L1 = derivative(M, kc) # Correct, providing basis function manually: dkc = BasisFunction(m) dk, dc = split(dkc) L2 = derivative(M, kc, dkc) + f*dk*dx + g*dc*dx # Doesn't work, expecting BasisFunction, not Indexed: L3 = derivative(M, k, dk) + derivative(M, c, dc) ufl-1.3.0/sandbox/scratch/emptyform.py000066400000000000000000000006501226300046600177620ustar00rootroot00000000000000 from ufl import * cell = triangle e = VectorElement("CG", cell, 2) v = TestFunction(e) u = Function(e) I = Identity(cell.d) F = I + grad(u).T C = F.T*F C = variable(C) f = diff(tr(C), C) #a = inner(f, grad(v))*dx a = inner(f, f)*dx print "a:" print a fd = a.form_data() print "fd:" print fd print "fd form:" print fd.form from ufl.algorithms import expand_indices print "fd form expanded:" print expand_indices(fd.form) ufl-1.3.0/sandbox/scratch/inheritance.py000066400000000000000000000011001226300046600202200ustar00rootroot00000000000000 import ufl class SomeFunction(object): def __init__(self): pass def __getattr__(self, name): print "SomeFunction.getattr:", name return object.__dict__[name] class MyFunction(ufl.Function, SomeFunction): def __init__(self, V): ufl.Function.__init__(self, V) def __getattr__(self, name): print "MyFunction.getattr:", name return object.__dict__[name] V = ufl.FiniteElement("CG", "triangle", 1) v = ufl.TestFunction(V) f = MyFunction(V) print f._element a = f*v*ufl.dx print a print f._element print f.foo ufl-1.3.0/sandbox/scratch/logtest.py000066400000000000000000000010711226300046600174170ustar00rootroot00000000000000 import logging import ufl # Let the logger emit everything, but print only warnings l = ufl.get_logger() # Let a file handler store all log data hf = logging.FileHandler("testlog") # mode="a", e.g. append, as default, can be changed l.addHandler(hf) hl = ufl.log.ufl_logger.add_logfile() l.addHandler(hl) ufl.get_handler().setLevel(logging.DEBUG) hf.setLevel(logging.ERROR) hl.setLevel(logging.DEBUG) l.setLevel(logging.DEBUG) # Try getting some log messages f = ufl.algorithms.load_forms("../../demo/Stiffness.ufl") fd = f[0].form_data() print "end of script" ufl-1.3.0/sandbox/scratch/printdocstrings.py000066400000000000000000000020351226300046600211730ustar00rootroot00000000000000#!/usr/bin/env python import ufl missingmods = {} missing = {} terminals = {} operators = {} formoperators = {} other = {} for n in ufl.__all__: o = getattr(ufl, n) d = o.__doc__ if not d: missing[n] = d s = missingmods.get(o.__module__, set()) s.add(n) missingmods[o.__module__] = s elif "UFL operator:" in d: operators[n] = d elif "UFL form operator:" in d: formoperators[n] = d elif d.startswith("UFL "): terminals[n] = d else: other[n] = d def format_dict(di): return '\n'.join('%s: %s' % (n, di[n]) for n in sorted(di.keys())) sep = '\n' + '='*80 + '\n' print sep+"Terminals:" print format_dict(terminals) print sep+"Operators:" print format_dict(operators) print sep+"Form operators:" print format_dict(formoperators) print sep+"Other:" print format_dict(other) print sep+"Other names:" print '\n'.join(sorted(other.keys())) print sep+"Missing:" print '\n'.join(sorted(missing.keys())) print sep+"Missing by module:" print format_dict(missingmods) ufl-1.3.0/sandbox/scratch/replace.py000066400000000000000000000002341226300046600173510ustar00rootroot00000000000000 from ufl import * fe = VectorElement("CG", triangle, 1) f = Coefficient(fe) u0, u1 = split(f) f2 = as_vector((u1, u1)) print str(replace(u0*dx, {f: f2})) ufl-1.3.0/sandbox/scratch/restrictiontest.py000066400000000000000000000003411226300046600212020ustar00rootroot00000000000000 from ufl import * from ufl.algorithms import * e = FiniteElement("DG", triangle, 1) f = Function(e) v = TestFunction(e) L = f*v*dS print [propagate_restrictions(itg.integrand()) for itg in L.form_data().form.integrals()] ufl-1.3.0/sandbox/scratch/slotstest.py000066400000000000000000000004241226300046600200030ustar00rootroot00000000000000 class A(object): __slots__ = ('a',) def __init__(self): self.a = 1 class B(object): #__slots__ = ('b',) def __init__(self): self.b = 2 class C(A, B): __slots__ = ('c',) def __init__(self): self.c = 3 c = C() print vars(c) ufl-1.3.0/sandbox/scratch/test.py000066400000000000000000000017021226300046600167160ustar00rootroot00000000000000 from dolfin import cpp import ufl class SomeFunction(object): def __init__(self, value): self._a = value self._b = 2*value def __getattr__(self,name): return self.__dict__[name] def init(self, V, value): ufl.Function.__init__(self, V) cpp.Function.__init__(self) self._V = V def Function_factory(): return type("MyFunction", (ufl.Function, cpp.Function, Function), { "__init__": init }) class Function(object): def __new__(cls, V, value): # If the __new__ function is called because we are instantiating a sub class # of Function. Instantiate the class directly using objects __new__ if cls.__name__ != "Function": print "It's alive" return object.__new__(cls) return object.__new__(Function_factory()) V = ufl.FiniteElement("CG", "triangle", 1) v = ufl.TestFunction(V) f = Function(V, 3) print f._element print f._a L = f*v*ufl.dx print L ufl-1.3.0/sandbox/scratch/visco/000077500000000000000000000000001226300046600165105ustar00rootroot00000000000000ufl-1.3.0/sandbox/scratch/visco/dicttest.py000066400000000000000000000004231226300046600207040ustar00rootroot00000000000000 class EmptyDictType(dict): def __setitem__(self, key, value): raise Exception("This is a frozen unique empty dictionary object, inserting values is an error.") EmptyDict = EmptyDictType() d = EmptyDictType() print d.get('foo') d.update(k=3) print d d['f'] = 1 ufl-1.3.0/sandbox/scratch/visco/foo.py000066400000000000000000000001361226300046600176450ustar00rootroot00000000000000 import ufl ufl.expr.print_expr_statistics() import dolfin ufl.expr.print_expr_statistics() ufl-1.3.0/sandbox/scratch/visco/inherittest.py000066400000000000000000000013021226300046600214200ustar00rootroot00000000000000 class A(object): __slots__ = () class B(object): __slots__ = ('y',) class C(B, A): __slots__ = ('z',) a = A() b = B() b.y = 3 c = C() c.y = 1 c.z = 2 print hasattr(A,'__slots__') and A.__slots__ print hasattr(B,'__slots__') and B.__slots__ print hasattr(C,'__slots__') and C.__slots__ print hasattr(A,'__dict__') and A.__dict__ print hasattr(B,'__dict__') and B.__dict__ print hasattr(C,'__dict__') and C.__dict__ print hasattr(a,'__slots__') and a.__slots__ print hasattr(b,'__slots__') and b.__slots__ print hasattr(c,'__slots__') and c.__slots__ print hasattr(a,'__dict__') and a.__dict__ print hasattr(b,'__dict__') and b.__dict__ print hasattr(c,'__dict__') and c.__dict__ ufl-1.3.0/sandbox/scratch/visco/profiling.py000066400000000000000000000123401226300046600210530ustar00rootroot00000000000000from sys import getsizeof import ufl from ufl.algorithms import pre_traversal from ufl.classes import Terminal, Operator varname_labels = [\ ("num_unique_nodes", "Number of unique nodes: "), ("num_repeated_nodes", "Number of nodes with repetition: "), ("num_hash_values", "Number of unique hash values: "), ("num_repr_members", "Number of nodes with _repr member: "), ("footprint_of_repr", "Memory footprint of repr members: "), ("footprint_of_nodes", "Memory footprint of node objects: "), ] class ExpressionAnalysisReport(object): def __init__(self): self.num_repeated_nodes = 0 self.num_unique_nodes = 0 self.num_hash_values = 0 self.num_repr_members = 0 self.footprint_of_repr = 0 self.footprint_of_nodes = 0 def __iadd__(self, other): for k,v in vars(other).iteritems(): setattr(self, k, getattr(self, k) + v) return self def __isub__(self, other): for k,v in vars(other).iteritems(): setattr(self, k, getattr(self, k) - v) return self def __add__(self, other): n = ExpressionAnalysisReport() n += self n += other return n def __sub__(self, other): n = ExpressionAnalysisReport() n += self n -= other return n def __str__(self): return '\n'.join("%s %s" % (l,getattr(self,k)) for k, l in varname_labels) def accumulate_results(reports, e, visited_ids, hashes): # Count nodes with repetition for report in reports: report.num_repeated_nodes += 1 # Skip already visited, the rest is without repetition if id(e) in visited_ids: return visited_ids.add(id(e)) # Compute some info fp = getsizeof(e) h = hash(e) if not h in hashes: hashes.add(h) h = 1 else: h = 0 r = 1 if hasattr(e, '_repr') else 0 rfp = getsizeof(e._repr) if r else 0 # Accumulate into all reports for report in reports: report.num_unique_nodes += 1 report.footprint_of_nodes += fp report.num_hash_values += h report.num_repr_members += r report.footprint_of_repr += rfp def analyse_expression(expr): total_report = ExpressionAnalysisReport() terminal_report = ExpressionAnalysisReport() operator_report = ExpressionAnalysisReport() type_reports = dict((cl.__name__, ExpressionAnalysisReport()) for cl in ufl.classes.all_ufl_classes) visited_ids = set() hashes = set() for e in ufl.algorithms.pre_traversal(expr): # Get UFL class name cln = e.__class__._uflclass.__name__ # Pick reports to add to reports = [total_report, type_reports[cln]] if isinstance(e, Operator): reports.append(operator_report) elif isinstance(e, Terminal): reports.append(terminal_report) # Add results accumulate_results(reports, e, visited_ids, hashes) return total_report, terminal_report, operator_report, type_reports _item_template = """{label} {{total_report.{key}}} - terminals: {{terminal_report.{key}}} - operators: {{operator_report.{key}}}""" _class_template = " - {classname}:{spacing} {key}" def format_expression_analysis(total_report, terminal_report, operator_report, type_reports): classes = sorted(type_reports.keys()) maxlen = max([len(c) for c in classes] + [0]) parts = [] for k, l in varname_labels: tmp = _item_template.format(label=l, key=k) parts.append(tmp.format(total_report=total_report, terminal_report=terminal_report, operator_report=operator_report)) for c in classes: r = type_reports[c] if r.num_unique_nodes > 0: sp = "" if len(c) >= maxlen else (maxlen-len(c))*" " parts.append(_class_template.format(classname=c, spacing=sp, key=getattr(r,k))) return "\n".join(parts) def formatted_analysis(expr, classes=False): res = analyse_expression(expr) if not classes: res[3].clear() # TODO: A table formatting would perhaps be nicer return format_expression_analysis(*res) def rsizeof(obj, visited=None): if visited is None: visited = set() if id(obj) in visited: return 0 visited.add(id(obj)) s = getsizeof(obj) if 1: s += sum(rsizeof(o) for o in obj.operands()) elif hasattr(obj.__class__, '__slots__'): s += sum(rsizeof(getattr(obj,n)) for n in obj.__class__.__slots__) else: s += sum(rsizeof(o) for o in vars(obj).itervalues()) return s from pympler.asizeof import asizeof def test(): from ufl import FiniteElement, triangle, Coefficient, as_ufl V = FiniteElement("CG", triangle, 1) u = Coefficient(V) v = Coefficient(V) o = as_ufl(1) g = v+o t = as_ufl(2) f = g**t e = u*f print formatted_analysis(e, classes=True) #print rsizeof(V) for n in ['o', 't', 'u', 'v', 'g', 'f', 'e']: obj = eval(n) print n, getsizeof(obj), rsizeof(obj), asizeof(obj), type(obj).__name__ print id(o._index_dimensions) print id(t._index_dimensions) print id(ufl.common.EmptyDict) test() ufl-1.3.0/sandbox/scratch/visco/pytest.py000066400000000000000000000023721226300046600204160ustar00rootroot00000000000000 class B(object): __slots__ = () ccount = 0 dcount = 0 def __init__(self): B.ccount += 1 def __del__(self): B.dcount += 1 import weakref class A(B): #__slots__ = ('x','__weakref__') # This adds 8 bytes __slots__ = ('x',) #_cache = weakref.WeakValueDictionary() _cache = {} def __new__(cls, value): o = A._cache.get(value) if o is None: o = object.__new__(cls, value) A._cache[value] = o return o def __init__(self, value): if hasattr(self, 'x'): print 'has attr x' else: print 'has no attr x' B.__init__(self) self.x = value def __str__(self): return "A(%d)" % self.x import sys objects = [A(v) for v in range(5)] objects2 = [A(v) for v in range(5)] print sys.getsizeof(objects[0]) print [id(o) for o in objects] print [id(o) for o in objects2] print [o for o in objects] print [o for o in objects2] print B.ccount, B.dcount del objects print B.ccount, B.dcount del objects2 print B.ccount, B.dcount """ F: 18.9430084229 2.38723802567 J: expand_indices took 57 s 557.466621399 57.3990740776 408.347160339 54.8738880157 expand_indices took 53 s 355.462684631 53.2244179249 """ ufl-1.3.0/sandbox/scratch/visco/tmp.py000066400000000000000000000014021226300046600176570ustar00rootroot00000000000000a = [('Argument', 160), ('Label', 400), ('Variable', 440), ('Coefficient', 832), ('Division', 62128), ('Exp', 99264), ('FloatValue', 542592), ('Power', 1435984), ('IntValue', 5676696), ('Sum', 10477520), ('Product', 14031360), ('SpatialDerivative', 16739448), ('Indexed', 21631344), ('MultiIndex', 27601776)] print sum(x[1] for x in a) import ufl n = ufl.triangle.n v = n[ufl.i] ii = v.operands()[1] print type(ii) import sys print sys.getsizeof(ii) from ufl import * V = VectorElement("CG", tetrahedron, 1) u = Coefficient(V) W = FiniteElement("CG", triangle, 1) w = Coefficient(W) print sys.getsizeof(V) print sys.getsizeof(u) print sys.getsizeof(W) print sys.getsizeof(w) print for cl in ufl.classes.terminal_classes: print cl.__name__, sys.getsizeof(cl) ufl-1.3.0/sandbox/scratch/visco/uvisco.py000066400000000000000000000164641226300046600204050ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2012 Harish Narayanan DOLFIN = 0 if DOLFIN: from dolfin import * else: from ufl import * import time from sys import getsizeof import ufl.classes from ufl.algorithms import expand_indices, Graph from pympler.asizeof import asizeof #import sfc #sfc.set_level(DEBUG) #set_log_level(DEBUG) if DOLFIN: parameters["form_compiler"]["name"] = "sfc" # parameters["form_compiler"]["cpp_optimize"] = True ffc_options = { # "quadrature_degree": 2, # "eliminate_zeros": True, # "precompute_basis_const": True, # "precompute_ip_const": True, # "optimize": True, # "log_level" : DEBUG, } if DOLFIN: mesh = UnitCube(1,1,1) else: cell = tetrahedron # Reference fibre, sheet and sheet-normal directions if DOLFIN: f0 = Constant((1, 0, 0)) s0 = Constant((0, 1, 0)) n0 = Constant((0, 0, 1)) else: f0 = VectorConstant(cell) s0 = VectorConstant(cell) n0 = VectorConstant(cell) # Material parameters for Figure 7 in HolzapfelOgden2009 if DOLFIN: a = Constant(0.500) #kPa b = Constant(8.023) a_f = Constant(16.472) #kPa b_f = Constant(16.026) a_s = Constant(2.481) #kPa b_s = Constant(11.120) a_fs = Constant(0.356) #kPa b_fs = Constant(11.436) kappa = Constant(2.0e6) #kPa beta = Constant(9.0) else: a = Constant(cell) b = Constant(cell) a_f = Constant(cell) b_f = Constant(cell) a_s = Constant(cell) b_s = Constant(cell) a_fs = Constant(cell) b_fs = Constant(cell) kappa = Constant(cell) beta = Constant(cell) # Strain energy functions for the passive myocardium def psi_iso_inf(I1_bar, I4_f_bar, I4_s_bar, I8_fs_bar, I8_fn_bar): return(a/(2*b)*exp(b*(I1_bar - 3)) \ + a_f/(2*b_f)*(exp(b_f*(I4_f_bar - 1)**2) - 1) \ + a_s/(2*b_s)*(exp(b_s*(I4_s_bar - 1)**2) - 1) \ + a_fs/(2*b_fs)*(exp(b_fs*I8_fs_bar**2) - 1)) def psi_vol_inf(J): return(kappa*(1/(beta**2)*(beta*ln(J) + 1/(J**beta) - 1))) # Define the elastic response of the material def P(u): # Kinematics I = Identity(u.cell().d) # Identity tensor F = I + grad(u) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor J = variable(det(F)) # Jacobian C_bar = J**(-2.0/3.0)*C # Modified right Cauchy-Green tensor # Principle isotropic invariants I1_bar = variable(tr(C_bar)) I2_bar = variable(0.5*(tr(C_bar)**2 - tr(C_bar*C_bar))) # Anisotropic (quasi) invariants I4_f_bar = variable(inner(f0, C_bar*f0)) I4_s_bar = variable(inner(s0, C_bar*s0)) I8_fs_bar = variable(inner(f0, C_bar*s0)) I8_fn_bar = variable(inner(f0, C_bar*n0)) # Strain energy functions psi_iso = psi_iso_inf(I1_bar, I4_f_bar, I4_s_bar, I8_fs_bar, I8_fn_bar) psi_vol = psi_vol_inf(J) # Define the second Piola-Kirchhoff stress in terms of the invariants # S_bar = 2*(diff(psi_iso, I1_bar) + diff(psi_iso, I2_bar))*I \ # - 2*diff(psi_iso, I2_bar)*C_bar \ # + 2*diff(psi_iso, I4_f_bar)*outer(f0, f0) \ # + 2*diff(psi_iso, I4_s_bar)*outer(s0, s0) \ # + diff(psi_iso, I8_fs_bar)*(outer(f0, s0) + outer(s0, f0)) \ # + diff(psi_iso, I8_fn_bar)*(outer(f0, n0) + outer(n0, f0)) # Hand compute the second Piola-Kirchhoff stress in terms of the invariants S_bar = as_matrix([[a*exp(b*(I1_bar - 3)) + a_f*(2*I4_f_bar - 2)*exp(b_f*(I4_f_bar - 1)**2), I8_fs_bar*a_fs*exp(I8_fs_bar**2*b_fs), 0.0], [I8_fs_bar*a_fs*exp(I8_fs_bar**2*b_fs), a*exp(b*(I1_bar - 3)) + a_s*(2*I4_s_bar - 2)*exp(b_s*(I4_s_bar - 1)**2), 0.0], [0.0, 0.0, a*exp(b*(I1_bar - 3))]]) Dev_S_bar = S_bar - (1.0/3.0)*inner(S_bar, C)*inv(C) S_iso_inf = J**(-2.0/3.0)*Dev_S_bar S_vol_inf = J*diff(psi_vol, J)*inv(C) # Return the first Piola-Kirchhoff stress return (F*(S_iso_inf + S_vol_inf)) def build_forms(): if DOLFIN: V = VectorFunctionSpace(mesh, "Lagrange", 1) Q = FunctionSpace(mesh, "Lagrange", 1) u = Function(V) # Displacement from previous iteration else: V = VectorElement("Lagrange", tetrahedron, 1) Q = FiniteElement("Lagrange", tetrahedron, 1) u = Coefficient(V) # Displacement from previous iteration du = TrialFunction(V) # Incremental displacement v = TestFunction(V) # Test function F = inner(P(u), grad(v))*dx J = derivative(F, u, du) return F, J def printmem(): print ufl.expr.print_expr_statistics() print def find_the_memory_thief(expr): reprtot = 0 reprtot2 = 0 memuse = {} tmemuse = {} ids = set() for e in ufl.algorithms.pre_traversal(expr): if id(e) in ids: continue ids.add(id(e)) if hasattr(e, '_repr'): r = repr(e) reprtot += getsizeof(r) reprtot2 += len(r) n = type(e).__name__ memuse[n] = max(memuse.get(n,0), getsizeof(e)) tmemuse[n] = tmemuse.get(n,0) + getsizeof(e) worst = sorted(memuse.items(), key=lambda x: x[1]) tworst = sorted(tmemuse.items(), key=lambda x: x[1]) print print 'ids:', len(ids) print 'reprtot bytes:', reprtot print 'reprtot2 len: ', reprtot2 print 'totmem (MB):', (sum(x[1] for x in tworst) / float(1024**2)) print "-"*60, 'worst' print worst print "-"*60, 'tworst' print tworst print def print_expr_size(expr): print "::", getsizeof(expr), " ", asizeof(expr), " ", \ sum(1 for _ in ufl.algorithms.pre_traversal(expr)) msize, mtime = 0, 0 def process_form(F): global msize, mtime printmem() print "size of form", asizeof(F) print_expr_size(F.integrals(Measure.CELL)[0].integrand()) t1 = time.time() print 'starting preprocess' Ffd = F.compute_form_data() t2 = time.time() print 'preprocess took %d s' % (t2-t1) print "size of form data (in MB)", asizeof(Ffd)/float(1024**2) print_expr_size(Ffd.preprocessed_form.integrals(Measure.CELL)[0].integrand()) printmem() printmem() t1 = time.time() print 'starting expand_indices' eiF = expand_indices(Ffd.preprocessed_form) t2 = time.time() print 'expand_indices took %d s' % (t2-t1) #print "REPR LEN", len(repr(eiF)) msize = asizeof(eiF)/float(1024**2) mtime = t2-t1 print "size of expanded form (in MB)", msize print_expr_size(eiF.integrals(Measure.CELL)[0].integrand()) printmem() t1 = time.time() print 'starting graph building' FG = Graph(eiF.integrals(Measure.CELL)[0].integrand()) t2 = time.time() print 'graph building took %d s' % (t2-t1) print "size of graph (in MB)", asizeof(FG)/float(1024**2) printmem() return eiF def main(): F, J = build_forms() if 0: print '\n', '='*50, 'F' ei = process_form(F) if 1: print '\n', '='*50, 'J' ei = process_form(J) print '\n', '='*50, 'mem of only eiJ' del F del J printmem() find_the_memory_thief(ei.integrals(Measure.CELL)[0].integrand()) #print formatted_analysis(ei, classes=True) print print msize print mtime try: from guppy import hpy hp = hpy() print "heap:" print hp.heap() except: print "No guppy installed!" main() ufl-1.3.0/sandbox/scratch/visco/visco.py000066400000000000000000000122131226300046600202040ustar00rootroot00000000000000#!/usr/bin/python # Copyright (C) 2012 Harish Narayanan # Library imports and settings #import memory #memory.start() from dolfin import * from numpy import array #import sfc #sfc.set_level(DEBUG) #set_log_level(DEBUG) parameters["form_compiler"]["name"] = "sfc" # parameters["form_compiler"]["cpp_optimize"] = True ffc_options = { # "quadrature_degree": 2, # "eliminate_zeros": True, # "precompute_basis_const": True, # "precompute_ip_const": True, # "optimize": True, # "log_level" : DEBUG, } # Dimensions and mesh density of the domain width = 1 depth = 1 height = 1 n = 5 mesh = Box(0, width, 0, depth, 0, height, n*width, n*depth, n*height) # Reference fibre, sheet and sheet-normal directions f0 = Constant((1, 0, 0)) s0 = Constant((0, 1, 0)) n0 = Constant((0, 0, 1)) # Material parameters for Figure 7 in HolzapfelOgden2009 a = Constant(0.500) #kPa b = Constant(8.023) a_f = Constant(16.472) #kPa b_f = Constant(16.026) a_s = Constant(2.481) #kPa b_s = Constant(11.120) a_fs = Constant(0.356) #kPa b_fs = Constant(11.436) # Material parameters for compressibility kappa = Constant(2.0e6) #kPa beta = Constant(9.0) # Strain energy functions for the passive myocardium def psi_iso_inf(I1_bar, I4_f_bar, I4_s_bar, I8_fs_bar, I8_fn_bar): return(a/(2*b)*exp(b*(I1_bar - 3)) \ + a_f/(2*b_f)*(exp(b_f*(I4_f_bar - 1)**2) - 1) \ + a_s/(2*b_s)*(exp(b_s*(I4_s_bar - 1)**2) - 1) \ + a_fs/(2*b_fs)*(exp(b_fs*I8_fs_bar**2) - 1)) def psi_vol_inf(J): return(kappa*(1/(beta**2)*(beta*ln(J) + 1/(J**beta) - 1))) # Define the elastic response of the material def P(u): # Kinematics I = Identity(u.cell().d) # Identity tensor F = I + grad(u) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor J = variable(det(F)) # Jacobian C_bar = J**(-2.0/3.0)*C # Modified right Cauchy-Green tensor # Principle isotropic invariants I1_bar = variable(tr(C_bar)) I2_bar = variable(0.5*(tr(C_bar)**2 - tr(C_bar*C_bar))) # Anisotropic (quasi) invariants I4_f_bar = variable(inner(f0, C_bar*f0)) I4_s_bar = variable(inner(s0, C_bar*s0)) I8_fs_bar = variable(inner(f0, C_bar*s0)) I8_fn_bar = variable(inner(f0, C_bar*n0)) # Strain energy functions psi_iso = psi_iso_inf(I1_bar, I4_f_bar, I4_s_bar, I8_fs_bar, I8_fn_bar) psi_vol = psi_vol_inf(J) # Define the second Piola-Kirchhoff stress in terms of the invariants # S_bar = 2*(diff(psi_iso, I1_bar) + diff(psi_iso, I2_bar))*I \ # - 2*diff(psi_iso, I2_bar)*C_bar \ # + 2*diff(psi_iso, I4_f_bar)*outer(f0, f0) \ # + 2*diff(psi_iso, I4_s_bar)*outer(s0, s0) \ # + diff(psi_iso, I8_fs_bar)*(outer(f0, s0) + outer(s0, f0)) \ # + diff(psi_iso, I8_fn_bar)*(outer(f0, n0) + outer(n0, f0)) # Hand compute the second Piola-Kirchhoff stress in terms of the invariants S_bar = as_matrix([[a*exp(b*(I1_bar - 3)) + a_f*(2*I4_f_bar - 2)*exp(b_f*(I4_f_bar - 1)**2), I8_fs_bar*a_fs*exp(I8_fs_bar**2*b_fs), 0.0], [I8_fs_bar*a_fs*exp(I8_fs_bar**2*b_fs), a*exp(b*(I1_bar - 3)) + a_s*(2*I4_s_bar - 2)*exp(b_s*(I4_s_bar - 1)**2), 0.0], [0.0, 0.0, a*exp(b*(I1_bar - 3))]]) Dev_S_bar = S_bar - (1.0/3.0)*inner(S_bar, C)*inv(C) S_iso_inf = J**(-2.0/3.0)*Dev_S_bar S_vol_inf = J*diff(psi_vol, J)*inv(C) # Return the first Piola-Kirchhoff stress return (F*(S_iso_inf + S_vol_inf)) def sigma(u): I = Identity(u.cell().d) F = I + grad(u) J = det(F) return(1/J*P(u)*F.T) # Function spaces V = VectorFunctionSpace(mesh, "Lagrange", 1) Q = FunctionSpace(mesh, "Lagrange", 1) du = TrialFunction(V) # Incremental displacement v = TestFunction(V) # Test function u = Function(V) # Displacement from previous iteration # Boundary conditions back_condition = "x[0] == 0.0 && on_boundary" front_condition = "x[0] == %g && on_boundary" % depth left_condition = "x[1] == 0.0 && on_boundary" right_condition = "x[1] == %g && on_boundary" % width bottom_condition = "x[2] == 0.0 && on_boundary" top_condition = "x[2] == %g && on_boundary" % height back, front = compile_subdomains([back_condition, front_condition]) left, right = compile_subdomains([left_condition, right_condition]) bottom, top = compile_subdomains([bottom_condition, top_condition]) hold = Expression(("0.0", "0.0", "0.0")) # fs shear = Expression(("0.0", "gamma*depth", "0.0"), gamma=0.0, depth=depth) hold_back = DirichletBC(V, hold, back) shear_front = DirichletBC(V, shear, front) bcs = [hold_back, shear_front] F = inner(P(u), grad(v))*dx J = derivative(F, u, du) displacement_file = File("../output/displacement.pvd") stress_file = File("../output/stress.pvd") applied_gamma = 0.0 assemble(F, form_compiler_parameters=ffc_options) assemble(J, form_compiler_parameters=ffc_options) while applied_gamma <= 0.05: shear.gamma = applied_gamma solve(F == 0, u, bcs, J=J, form_compiler_parameters=ffc_options) applied_gamma = applied_gamma + 0.01 displacement_file << u stress = project(sigma(u)[0][1], Q) #fs stress_file << stress ufl-1.3.0/sandbox/scratch/zeroformtest.py000066400000000000000000000010451226300046600205020ustar00rootroot00000000000000 from ufl import * from ufl.algorithms import * cell = triangle x, y = cell.x e = FiniteElement("CG", cell, 1) v = TestFunction(e) f0 = as_vector((x, -y)) f1 = as_vector((x, y)) for f in (f0, f1): print "f = ", f print print "take 1:" a = (1+rot(f))*v*dx print "a1 = ", a print print a.form_data() print "a2 = ", expand_indices(a.form_data().form) print print "take 2:" a = rot(f)*v*dx print "a1 = ", a print print a.form_data() print "a2 = ", expand_indices(a.form_data().form) ufl-1.3.0/sandbox/scripts/000077500000000000000000000000001226300046600154255ustar00rootroot00000000000000ufl-1.3.0/sandbox/scripts/figures/000077500000000000000000000000001226300046600170715ustar00rootroot00000000000000ufl-1.3.0/sandbox/scripts/figures/files.tex000066400000000000000000000041341226300046600207170ustar00rootroot00000000000000\documentclass{beamer} \usepackage{graphicx} \begin{document} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{algebraoperator.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{compoundderivative.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{compoundtensoroperator.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{condition.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{constantvalue.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{derivative.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{expr.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{exprsub.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{formargument.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{function.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{geometricquantity.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{mathfunction.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{operator.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{operatorsub.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{restricted.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{terminal.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{terminalsub.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{utilitytype.pdf}\end{figure} \end{frame} \begin{frame} \begin{figure}\includegraphics[width=0.9\textwidth]{wrappertype.pdf}\end{figure} \end{frame} \end{document} ufl-1.3.0/sandbox/scripts/printimports.py000066400000000000000000000011521226300046600205500ustar00rootroot00000000000000#!/usr/bin/env python """Read *.py and print import statements for all classes and functions found in these files. Intended for __init__.py generation.""" import re from glob import glob files = glob("*.py") #prefix = "." # absolute_import syntax prefix = "" for f in files: lines = open(f).readlines() defs = [] for l in lines: for s in ("def ", "class "): if l.startswith(s): m = re.search(r"%s([^:(]*)" % s, l) d = m.groups()[0] defs.append(d) print print "from %s%s import %s" % (prefix, f[:-3], ", ".join(defs)) ufl-1.3.0/sandbox/scripts/rename.py000066400000000000000000000010601226300046600172430ustar00rootroot00000000000000#!/usr/bin/env python """Rename .ufl files from lower_score_naming to CamelCapsNaming.""" import os from glob import glob files = glob("*.ufl") files1 = [f for f in files if f.find("_") > 0] files2 = [f for f in files if f.find("_") < 0] jobs = [] for f in files1: a, b = f.split("_") g = a[0].upper() + a[1:] + b[0].upper() + b[1:] cmd = "hg rename %s %s" % (f, g) jobs.append(cmd) for f in files2: g = f[0].upper() + f[1:] cmd = "hg rename %s %s" % (f, g) jobs.append(cmd) for cmd in jobs: print cmd os.system(cmd) ufl-1.3.0/sandbox/scripts/renderclasstree.py000066400000000000000000000025671226300046600211760ustar00rootroot00000000000000#!/usr/bin/env python """Render the ufl.Expr class hierarchy in .dot format.""" from collections import defaultdict from ufl.classes import all_ufl_classes # Build lists of subclasses subgraphs = defaultdict(list) for c in all_ufl_classes: subgraphs[c.mro()[1].__name__].append(c.__name__) # Recursive graph formatting def format_children(parent, level, skipparent=True, maxlevels=None): if maxlevels is not None and maxlevels < 0: return "" children = subgraphs[parent] t = " "*level begin = t + "subgraph {\n" end = t + "}\n" g = "" for child in children: if child in subgraphs: g += begin g += format_children(child, level+1, skipparent=False, maxlevels=None if maxlevels is None else maxlevels-1) g += end if not skipparent: g += t + "%s -> %s;\n" % (child, parent) return g # Render graph body! # Root + some: body = format_children("object", 1, maxlevels=2) # Terminals: body = format_children("Terminal", 1, False) # Operators: body = format_children("Operator", 1, False) # All: body = format_children("object", 1) body = format_children("Expr", 1, False) # Set global formatting options format = """ node [shape=box, style=filled, color=lightgrey]; splines=true; """ # Combine everythig to a global graph dot = """strict digraph { %s %s }""" % (format, body) print dot ufl-1.3.0/sandbox/scripts/rendertrees.py000077500000000000000000000037041226300046600203300ustar00rootroot00000000000000#!/usr/bin/env python """Render the ufl.Expr class hierarchy in .dot format.""" from collections import defaultdict from ufl.classes import all_ufl_classes import sys, optparse # Get commandline options usage = """Render a subset of the UFL class hierarchy.""" def opt(long, short, t, default, help): return optparse.make_option("--%s" % long, "-%s" % short, action="store", type=t, dest=long, default=default, help=help) option_list = [ \ opt("parent", "p", "str", "object", "Parent of tree to render."), opt("skipparent", "s", "int", 1, "Skip parent when rendering tree."), opt("maxlevels", "m", "int", None, "Max levels of tree to render."), ] parser = optparse.OptionParser(usage=usage, option_list=option_list) args = sys.argv[1:] (options, args) = parser.parse_args(args=args) parent = options.parent or "object" skipparent = options.skipparent maxlevels = options.maxlevels level = 1 #options.level # Build lists of subclasses subgraphs = defaultdict(list) for c in all_ufl_classes: subgraphs[c.mro()[1].__name__].append(c.__name__) # Recursive graph formatting def format_children(parent, level, skipparent=True, maxlevels=None): if maxlevels is not None and maxlevels <= 0: return "" children = subgraphs[parent] t = " "*level begin = t + "subgraph {\n" end = t + "}\n" g = "" for child in children: if child in subgraphs: g += begin g += format_children(child, level+1, skipparent=False, maxlevels=None if maxlevels is None else maxlevels-1) g += end if not skipparent: g += t + "%s -> %s;\n" % (child, parent) return g # Render graph body! body = format_children(parent, level, skipparent, maxlevels) # Set global formatting options format = """ node [shape=box, style=filled, color=lightgrey]; splines=true; """ # Combine everythig to a global graph dot = """strict digraph { %s %s }""" % (format, body) print dot ufl-1.3.0/sandbox/scripts/rendertrees.sh000077500000000000000000000031251226300046600203070ustar00rootroot00000000000000#!/bin/bash CMD=neato CMD=twopi CMD=fdp CMD=circo CMD=dot ./rendertrees.py -pExpr -s0 -m1 | $CMD -Tpdf -o figures/exprsub.pdf ./rendertrees.py -pTerminal -s0 -m1 | $CMD -Tpdf -o figures/terminalsub.pdf ./rendertrees.py -pFormArgument -s0 -m1 | $CMD -Tpdf -o figures/formargument.pdf ./rendertrees.py -pOperator -s0 -m1 | $CMD -Tpdf -o figures/operatorsub.pdf ./rendertrees.py -pExpr -s0 | $CMD -Tpdf -o figures/expr.pdf ./rendertrees.py -pTerminal -s0 | $CMD -Tpdf -o figures/terminal.pdf ./rendertrees.py -pOperator -s0 | $CMD -Tpdf -o figures/operator.pdf ./rendertrees.py -pFunction -s0 | $CMD -Tpdf -o figures/function.pdf ./rendertrees.py -pGeometricQuantity -s0 | $CMD -Tpdf -o figures/geometricquantity.pdf ./rendertrees.py -pConstantValue -s0 | $CMD -Tpdf -o figures/constantvalue.pdf ./rendertrees.py -pUtilityType -s0 | $CMD -Tpdf -o figures/utilitytype.pdf ./rendertrees.py -pAlgebraOperator -s0 | $CMD -Tpdf -o figures/algebraoperator.pdf ./rendertrees.py -pCondition -s0 | $CMD -Tpdf -o figures/condition.pdf ./rendertrees.py -pMathFunction -s0 | $CMD -Tpdf -o figures/mathfunction.pdf ./rendertrees.py -pWrapperType -s0 | $CMD -Tpdf -o figures/wrappertype.pdf ./rendertrees.py -pRestricted -s0 | $CMD -Tpdf -o figures/restricted.pdf ./rendertrees.py -pDerivative -s0 | $CMD -Tpdf -o figures/derivative.pdf ./rendertrees.py -pCompoundDerivative -s0 | $CMD -Tpdf -o figures/compoundderivative.pdf ./rendertrees.py -pCompoundTensorOperator -s0 | $CMD -Tpdf -o figures/compoundtensoroperator.pdf ufl-1.3.0/sandbox/scripts/showclasses.py000066400000000000000000000005261226300046600203400ustar00rootroot00000000000000#!/usr/bin/env python """Print list of terminal and nonterminal ufl.Expr subclasses.""" from ufl.classes import * print print "--- Terminal classes:" for name in sorted(c.__name__ for c in terminal_classes): print name print print "--- Nonterminal classes:" for name in sorted(c.__name__ for c in nonterminal_classes): print name ufl-1.3.0/sandbox/scripts/summarizetodos.py000066400000000000000000000014001226300046600210570ustar00rootroot00000000000000#!/usr/bin/env python """Print summary of TODO and FIXME lines in files *.py.""" import os from glob import glob files = sorted(glob("*.py")) lines = [] num_todos = 0 num_fixmes = 0 for f in files: todos = [] fixmes = [] for l in open(f): if "TODO" in l: todos.append(l.strip()) if "FIXME" in l: fixmes.append(l.strip()) if todos or fixmes: lines += ["", "-"*80, f] if todos: n = len(todos) lines += ["%d TODOs:" % n] + todos num_todos += n if fixmes: n = len(fixmes) lines += ["%d FIXMEs:" % n] + fixmes num_fixmes += n print "\n".join(lines) print print "-"*80 print "Number of TODOs: ", num_todos print "Number of FIXMEs:", num_fixmes print ufl-1.3.0/sandbox/scripts/tensoralgebrastrings.py000066400000000000000000000016651226300046600222510ustar00rootroot00000000000000#!/usr/bin/env python """ To avoid typing errors, this code was used to generate expressions for the expansion of compound expressions like the cofactor, inverse, and deviatoric part of a matrix. """ import re from swiginac import * def matrix2Matrix(A): s = str(A) res = re.subn(r"A(.)(.)", r"A[\1,\2]", s) return "Matrix(%s)" % res[0] def cofac(A): return A.determinant() * A.inverse().transpose() def cofacstr(d): A = symbolic_matrix(d, d, "A") return matrix2Matrix(cofac(A)) def dev(A): d = A.rows() I = matrix(d, d) for i in range(d): I[i,i] = 1.0 alpha = sum(A[i,i] for i in range(d)) return A - alpha/d*I def devstr(d): A = symbolic_matrix(d, d, "A") return re.sub(r"(.)/", r"\1./", matrix2Matrix(dev(A))) print "Cofactors:" print print cofacstr(2) print print cofacstr(3) print print cofacstr(4) print print "Deviatoric:" print print devstr(2) print print devstr(3) print ufl-1.3.0/sandbox/scripts/testclasses.py000066400000000000000000000007201226300046600203330ustar00rootroot00000000000000#!/usr/bin/env python """Print all TestCase subclasses found in *.py.""" import os from glob import glob lines = [] for f in sorted(glob("*.py")): classes = [] for l in open(f): if "class" in l and "TestCase" in l: c = l c = c.split(" ")[1] c = c.split("(")[0] classes.append(c) lines.append("") lines.append(f) lines.append("tests = [%s]" % ", ".join(classes)) print "\n".join(lines) ufl-1.3.0/sandbox/subdomains/000077500000000000000000000000001226300046600161025ustar00rootroot00000000000000ufl-1.3.0/sandbox/subdomains/test_canonical_integrals.py000066400000000000000000000047441226300046600235230ustar00rootroot00000000000000"""Algorithm sketch to build canonical data structure for integrals over subdomains.""" from ufl import * # Transitional helper constructor from ufl.integral import Integral2 from ufl.algorithms.domain_analysis import (extract_domain_data_from_integral_dict, extract_integral_data_from_integral_dict) # Run for testing and inspection def test(): # Mock objects for compiler data and solver data comp1 = [1,2,3] comp2 = ('a', 'b') comp3 = {'1':1} sol1 = (0,3,5) sol2 = (0,3,7) # Basic UFL expressions for integrands V = FiniteElement("CG", triangle, 1) f = Coefficient(V) g = Coefficient(V) h = Coefficient(V) # Mock list of Integral objects (Integral2 is a dummy factory function for better constructor signature) integrals = {} integrals["cell"] = [# Integrals over 0 with no compiler_data: Integral2(f, "cell", 0, None, None), Integral2(g, "cell", 0, None, sol1), # Integrals over 0 with different compiler_data: Integral2(f**2, "cell", 0, comp1, None), Integral2(g**2, "cell", 0, comp2, None), # Integrals over 1 with same compiler_data object: Integral2(f**3, "cell", 1, comp1, None), Integral2(g**3, "cell", 1, comp1, sol1), # Integral over 0 and 1 with compiler_data object found in 0 but not 1 above: Integral2(f**4, "cell", (0,1), comp2, None), # Integral over 0 and 1 with its own compiler_data object: Integral2(g**4, "cell", (0,1), comp3, None), # Integral over 0 and 1 no compiler_data object: Integral2(h/3, "cell", (0,1), None, None), # Integral over everywhere with no compiler data: Integral2(h/2, "cell", Measure.DOMAIN_ID_EVERYWHERE, None, None), ] # Create form from all mock integrals to make test more realistic form = Form(integrals["cell"]) domain_data = extract_domain_data_from_integral_dict(form._dintegrals) integral_data = extract_integral_data_from_integral_dict(form._dintegrals) print print "Domain data:" print domain_data print print print "Integral data:" for ida in integral_data: print ida print test() ufl-1.3.0/sandbox/suggestions/000077500000000000000000000000001226300046600163105ustar00rootroot00000000000000ufl-1.3.0/sandbox/suggestions/element_tensor_product_suggestion.ufl000066400000000000000000000003201226300046600260450ustar00rootroot00000000000000 # Create two elements space_element = FiniteElement("Lagrange", "triangle", 1) time_element = FiniteElement("Lagrange", "interval", 1) # Create tensor product element element = space_element*time_element ufl-1.3.0/sandbox/suggestions/reverse_ad.py000066400000000000000000000060141226300046600210020ustar00rootroot00000000000000"""Reverse mode AD implementation.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-12-28 # Last changed: 2011-06-02 from itertools import izip from ufl.algorithms.pdiffs import PartialDerivativeComputer from ufl.differentiation import SpatialDerivative, VariableDerivative, CoefficientDerivative def reverse_ad(expr, G): # TODO: Finish this! # --- Forward sweep expressions have already been recorded as vertices in the DAG # TODO: Can't we just build the graph from expr in here? Need special treatement if a VariableDerivative! #G = build_graph(expr) V, E = G m = len(V) # We're computing df/fv: f, v = expr.operands() if isinstance(expr, SpatialDerivative): # Need to define dx_i/dx_j = delta_ij? pass if isinstance(expr, VariableDerivative): # Avoid putting contents of the differentiation Variable in graph, since it's not a Terminal anymore... TODO pass if isinstance(expr, CoefficientDerivative): # Define dw/dw := v (what we really mean by d/dw is d/dw_j where w = sum_j w_j phi_j, and what we really mean by v is phi_j for any j) pass # Initialize graph x = [0]*m # Size of v, could be larger if v is multiple thingys n = 1 x[:n] = TODO # Actually, we don't have these variables... # v is a MultiIndex instead of SpatialCoordinate, # or a Coefficient instead of a dof, # or a Variable (in which case the Variable shouldn't be traversed when building the graph) # ... but then again, we have many expressions that doesn't depend directly on v, but implicitly by definition... x[n:] = V[:] # = f_i( ) for j in dependencies of f_i #for i in range(n, m+1): # x[i] = V[i-n] # = f_i( ) for j in dependencies of f_i # Initialize xd gamma = 1 g = [0]*n xd = [0]*m xd[m-1] = gamma xd[:n] = g # Compute c[i,j] = df_i/dx_j TODO pdc = PartialDerivativeComputer() c = {} for i, v in enumerate(V): pdiffs = pdc(v) vi_edges = TODO for (j, dvidvj) in izip(vi_edges, pdiffs): c[(i,j)] = dvidvj # Reverse accumulation for i in range(m-1, n-1, -1): xdi = xd[i] for j in Eout[i-n]: # TODO: Correct edges, j should be the x indices of the operands of x[i] xd[j] += xdi*c[i,j] result = xd[:n] return result ufl-1.3.0/sandbox/uflfiles/000077500000000000000000000000001226300046600155475ustar00rootroot00000000000000ufl-1.3.0/sandbox/uflfiles/HyperElasticityWorkaround.ufl000066400000000000000000000036411226300046600234610ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-12-22 # # Cell and its properties cell = tetrahedron d = cell.d n = VectorConstant(cell) x = cell.x # Elements u_element = VectorElement("CG", cell, 2) p_element = FiniteElement("CG", cell, 1) A_element = TensorElement("CG", cell, 1) # Test and trial functions v = TestFunction(u_element) w = TrialFunction(u_element) # Displacement at current and two previous timesteps u = Function(u_element) up = Function(u_element) upp = Function(u_element) # Time parameters dt = Constant(cell) # Fiber field A = Function(A_element) # External forces T = Function(u_element) p0 = Function(p_element) N = n # Material parameters FIXME rho = Constant(cell) K = Constant(cell) c00 = Constant(cell) c11 = Constant(cell) c22 = Constant(cell) # Deformation gradient I = Identity(d) F = I + grad(u).T F = variable(F) Finv = inv(F) J = det(F) # Left Cauchy-Green deformation tensor B = F*F.T I1_B = tr(B) I2_B = (I1_B**2 - tr(B*B))/2 I3_B = J**2 # Right Cauchy-Green deformation tensor C = F.T*F I1_C = tr(C) I2_C = (I1_C**2 - tr(C*C))/2 I3_C = J**2 # Green strain tensor E = (C-I)/2 # Mapping of strain in fiber directions Ef = A*E*A.T # Strain energy function W(Q(Ef)) Q = c00*Ef[0,0]**2 + c11*Ef[1,1]**2 + c22*Ef[2,2]**2 # FIXME: insert some simple law here W = (K/2)*(exp(Q) - 1) # + p stuff # First Piola-Kirchoff stress tensor P = diff(W, F) # Acceleration term discretized with finite differences k = dt/rho acc = (u - 2*up + upp) # Residual equation # FIXME: Can contain errors, not tested! a_F = inner(acc, v)*dx \ + k*inner(P, grad(v))*dx \ - k*dot(J*Finv*T, v)*ds(0) \ - k*dot(J*Finv*p0*N, v)*ds(1) # Jacobi matrix of residual equation a_J = derivative(a_F, u, w) from ufl.algorithms import expand_derivatives, strip_variables a_F = strip_variables(expand_derivatives(a_F)) a_J = strip_variables(expand_derivatives(a_J)) # Export forms forms = [a_F, a_J] a, L = a_J, a_F ufl-1.3.0/sandbox/uflfiles/SimpleDiff.ufl000066400000000000000000000002171226300046600203010ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # element = FiniteElement("Lagrange", triangle, 1) f = Function(element) a = f.dx(0)*dx ufl-1.3.0/sandbox/uflfiles/SimpleDiff2.ufl000066400000000000000000000003231226300046600203610ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # cell = triangle element = VectorElement("Lagrange", cell, 1) f = Function(element) I = Identity(cell.d) a = f[i].dx(i)*I[j,j]*dx a = f[i].dx(i) *dx ufl-1.3.0/sandbox/uflfiles/SimpleDiff3.ufl000066400000000000000000000007421226300046600203670ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # cell = triangle velement = VectorElement("Lagrange", cell, 1) selement = VectorElement("Lagrange", cell, 1) u = Function(velement) du = u[i].dx(i) du = variable(du) p = du**4/2 s = diff(p, du) f = s*dx F = derivative(f, u) J = derivative(F, u) forms = [f, F, J] #from ufl.algorithms import expand_derivatives #a = f #print "------" #print a #print str(expand_derivatives(a)) #print repr(expand_derivatives(a)) #print "------" ufl-1.3.0/sandbox/uflfiles/SimpleDiff4.ufl000066400000000000000000000007661226300046600203760ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # cell = triangle velement = VectorElement("Lagrange", cell, 1) selement = VectorElement("Lagrange", cell, 1) u = Function(velement) v = TestFunction(velement) Du = grad(u) Du = variable(Du) p = exp(Du[0,0]**2) f = p*dx F = derivative(f, u, v) J = derivative(F, u) forms = [f, F, J] #from ufl.algorithms import expand_derivatives #a = J #print "------" #print a #print str(expand_derivatives(a)) #print repr(expand_derivatives(a)) #print "------" ufl-1.3.0/sandbox/uflfiles/SimpleDiff5.ufl000066400000000000000000000007651226300046600203760ustar00rootroot00000000000000# # Author: Martin Sandve Alnes # Date: 2008-10-03 # cell = triangle velement = VectorElement("Lagrange", cell, 1) selement = VectorElement("Lagrange", cell, 1) u = Function(velement) v = TestFunction(velement) Du = grad(u) Du = variable(Du) p = exp(Du[0,0]**2) S = diff(p, Du) F = inner(S, grad(v))*dx J = derivative(F, u) forms = [F, J] #from ufl.algorithms import expand_derivatives #print "------" #print a #print str(expand_derivatives(a)) #print repr(expand_derivatives(a)) #print "------" ufl-1.3.0/scripts/000077500000000000000000000000001226300046600137675ustar00rootroot00000000000000ufl-1.3.0/scripts/fixdates000077500000000000000000000005131226300046600155230ustar00rootroot00000000000000#!/usr/bin/env python import sys, os, glob, re, commands r = re.compile(r"(20..-..-..)") for fn in glob.glob("*.py"): st, op = commands.getstatusoutput("bzr log -l1 --line " + fn) m = r.search(op) date, = m.groups() st, op = commands.getstatusoutput("sed -i 's/Last changed.*/Last changed: %s/' %s" % (date, fn)) ufl-1.3.0/scripts/form2ufl000077500000000000000000000065231226300046600154570ustar00rootroot00000000000000#!/usr/bin/env python # # Simple conversion script from old FFC .form format to UFL format __authors__ = "Anders Logg" __date__ = "2008-08-01 -- 2009-03-15" import sys import re from os.path import exists def help(): print "Simple conversion script from old FFC .form format to UFL format." print "" print "Usage: form2ufl .form" def error(message=None): if not message is None: print message else: help() sys.exit(1) def split_at_closing_brace(s): num_left = num_right = 0 for (i, c) in enumerate(s): if c == "(": num_left += 1 elif c == ")": num_right += 1 if num_left == num_right: return s[:i + 1], s[i + 1:] return s, "" def replace_transp(match): expr1, expr2 = split_at_closing_brace(match.groups()[0]) return expr1 + ".T" + expr2 def replace_mult(match): start, end = split_at_closing_brace(match.groups()[0]) expr1 = start.split(",")[0][1:].strip() expr2 = start.split(",")[1][:-1].strip() if "+" in expr1: expr1 = "(%s)" % expr1 if "+" in expr2: expr2 = "(%s)" % expr2 return expr1 + "*" + expr2 + end def form2ufl(code): # List of simple replacements simple_replacements = ((r"\.form", ".ufl"), (r"\bdot\b", "inner"), (r"\bD\b", "Dx"), (r"\bmodulus\b", "abs"), (r'"interval"', "interval"), (r'"triangle"', "triangle"), (r'"tetrahedron"', "tetrahedron"), (r'MeshSize', "Constant"), (r'FacetNormal', "VectorConstant"), (r"Nedelec", "N1curl"), (r"VectorQuadratureElement\(", 'VectorElement("Quadrature", '), (r"QuadratureElement\(", 'FiniteElement("Quadrature", ')) # List of complex replacements complex_replacements = ((r"\btransp(.*)", replace_transp), (r"\bmult(.*)", replace_mult)) # Iterate over replacemens for (a, b) in simple_replacements + complex_replacements: code = re.sub(a, b, code) return code def main(args): # Check command-line arguments if not len(args) == 1: error() # Get prefix and filenames words = args[0].split(".form") if not (len(words) == 2 and words[1] == ""): error() prefix = words[0] form_filename = prefix + ".form" ufl_filename = prefix + ".ufl" # Check if outfile exists if exists(ufl_filename): error("File already exists: " + ufl_filename) print "Converting %s --> %s" % (form_filename, ufl_filename) # Read file try: file = open(form_filename, "r") code = file.read() file.close() except: error("Unable to read file: " + form_filename) # Convert to UFL code = form2ufl(code) # Write file try: file = open(ufl_filename, "w") file.write(code) file.close() except: error("Unable to write to file: " + ufl_filename) if __name__ == "__main__": main(sys.argv[1:]) sys.exit(0) ufl-1.3.0/scripts/makedoc000077500000000000000000000030411226300046600153160ustar00rootroot00000000000000# Copyright (C) 2011 Marie E. Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2011-06-09 # Last changed: 2011-06-27 # # This is a utility script for generating .rst and .html # documentation for UFL. # # Run from the top level UFL directory: # # ./scripts/makedoc # echo "" echo "--- Generating UFL html documentation" echo "" SPHINX_DIR=./doc/sphinx SPHINX_SCRIPT_DIR=$SPHINX_DIR/scripts SPHINX_SOURCE_DIR=$SPHINX_DIR/source # Generate .rst files $SPHINX_SCRIPT_DIR/generate_modules.py ufl --dest-dir=$SPHINX_SOURCE_DIR --suffix=rst --force echo "" echo "--- reSTructured text files generated in doc/sphinx/source/" echo "" # Generate index (and add some labels) VERSION=`grep '__version__' ufl/__init__.py | cut -d'"' -f2` $SPHINX_SCRIPT_DIR/generate_index.py $SPHINX_SOURCE_DIR $VERSION # Run sphinx make html cd $SPHINX_DIR make clean make html echo "" echo "--- HTML files generated in $SPHINX_DIR/build/html" echo "" ufl-1.3.0/scripts/ufl-analyse000077500000000000000000000057631226300046600161500ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-05-09" # Modified by Anders Logg, 2009. # Last changed: 2011-04-26 import sys, optparse from ufl.log import warning from ufl.algorithms import load_ufl_file, validate_form, ufl2latex, tree_format, preprocess # Get commandline options usage = """Analyse a .ufl file to find errors. Optionally write information about the forms for further inspection. Examples: ufl-analyse --quiet=0 --write=1 mass.ufl""" def opt(long, short, t, default, help): return optparse.make_option("--%s" % long, "-%s" % short, action="store", type=t, dest=long, default=default, help=help) option_list = [ \ opt("quiet", "q", "int", 1, "Do not print form information to screen."), opt("write", "w", "int", 0, "Write form information to file."), ] parser = optparse.OptionParser(usage=usage, option_list=option_list) args = sys.argv[1:] (options, args) = parser.parse_args(args=args) if not args: print "Missing files!" print parser.print_usage() sys.exit(-1) filenames = args write_file = options.write quiet = options.quiet # Handle each form file separately for filename in filenames: # Check file suffix if not filename.endswith(".ufl"): warning("Filename '%s' does not end with .ufl." % filename) # Load form file, which triggers many consistency # checks while the form is being built print "Loading form file '%s'" % filename try: # TODO: Forms that fail will usually fail inside this, which doesn't produce any log... Perhaps we should pass a log file to load_forms? data = load_ufl_file(filename) forms = data.forms except: print "Failed to load form file." raise outputfilename = filename + ".log" if write_file: outputfile = open(outputfilename, "w") def write(*items): text = " ".join(str(s) for s in items) if write_file: outputfile.write(text) outputfile.flush() if not quiet: print text # Analyse each form separately for form in forms: # Validate form validate_form(form) # Compute form metadata and extract preprocessed form form_data = form.compute_form_data() preprocessed_form = form_data.preprocessed_form # Print form data write("\nForm data:\n", str(form_data)) # Print different representations write("\n\nForm pretty-print (original):\n", str(form)) write("\n\nForm pretty-print (preprocessed):\n", str(preprocessed_form)) write("\n\nForm representation (original):\n", repr(form)) write("\n\nForm representation (preprocessed):\n", repr(preprocessed_form)) write("\n\nForm tree formatting (original):\n", tree_format(form)) write("\n\nForm tree formatting (preprocessed):\n", tree_format(preprocessed_form)) write("\n\nForm LaTeX code (preprocessed):\n", ufl2latex(form)) if write_file: outputfile.close() ufl-1.3.0/scripts/ufl-convert000077500000000000000000000164161226300046600161710ustar00rootroot00000000000000#!/usr/bin/env python import sys, os, optparse from pprint import pprint from ufl.algorithms import load_ufl_file, ufl2dot, tree_format, forms2latexdocument, preprocess # --- Utilities # Taken from http://ivory.idyll.org/blog/mar-07/replacing-commands-with-subprocess from subprocess import Popen, PIPE, STDOUT def get_status_output(cmd, input=None, cwd=None, env=None): pipe = Popen(cmd, shell=True, cwd=cwd, env=env, stdout=PIPE, stderr=STDOUT) (output, errout) = pipe.communicate(input=input) assert not errout status = pipe.returncode return (status, output) def runcmd(cmd): status, output = get_status_output(cmd) if status != 0: print "*** Error:" print output sys.exit(-1) def write_file(filename, text): "Write text to a file and close it." f = open(filename, "w") f.write(text) f.close() print "Wrote file %s" % filename # --- Option parsing usage = """Convert a .ufl file to some other format. Examples: ufl-convert -omydir -iyourdir -c -f -tpdf -s mass.ufl""" def opt(long, short, t, default, help): return optparse.make_option("--%s" % long, "-%s" % short, action="store", type=t, dest=long, default=default, help=help) option_list = [ \ # Directories: opt("outputdir", "o", "str", "", "Output directory."), opt("inputdir", "i", "str", "", "Input directory."), # Expression transformations: opt("compile", "c", "int", 0, "'Compile' forms: apply expression transformations like in a quadrature based form compilation."), opt("labeling", "l", "str", "repr", "Set to 'repr' or 'compact' for different naming of graph nodes."), # Output formats: opt("format", "f", "str", "", "Rendering format (str, repr, tree, dot, latex)."), opt("filetype", "t", "str", "", "Output file type (txt, py, dot, tex, ps, pdf, png)."), # Additional actions: opt("show", "s", "int", 0, "Open in an external viewer."), ] parser = optparse.OptionParser(usage=usage, option_list=option_list) args = sys.argv[1:] (options, args) = parser.parse_args(args=args) if not args: print "Missing files!" print parser.print_usage() sys.exit(-1) # --- Handle each file for arg in args: # 0) Get and check filename uflfilename = os.path.join(options.inputdir, arg) path, name = os.path.split(uflfilename) basename, ext = os.path.splitext(name) if ext != ".ufl": print "Expecting a .ufl file, not ", uflfilename sys.exit(-1) #print "uflfilename =", uflfilename #print "path =", path #print "name =", name #print "basename =", basename #print "ext =", ext # 1) Load forms #forms = load_forms(uflfilename) #formdatas = [f.compute_form_data() for f in forms] ufl_data = load_ufl_file(uflfilename) forms = ufl_data.forms #expressions = ufl_data.expressions # TODO: Allow rendering expressions without form stuff! # Preprocess forms for f in forms: f.compute_form_data(object_names=ufl_data.object_names) # 2) Transform forms compiled_forms = [] if options.compile: pass # TODO #compiled_forms = [compile_form(form) for form in forms] # 3) Render result format = options.format # Make format string conform if format == "latex": format = "tex" if format == "str": if options.compile: print "Warning: compile option not used." rendered = "\n\n".join("Form %s:\n%s\n" % (f.form_data().name, str(f)) for f in forms) #rendered = "\n\n".join("Form %s:\n%s\n" % (f.form_data().name, str(f.form_data())) for f in forms) elif format == "repr": if options.compile: print "Warning: compile option not used." rendered = "\n\n".join("Form %s:\n%s\n" % (f.form_data().name, repr(f)) for f in forms) elif format == "tree": if options.compile: print "Warning: compile option not used." rendered = "\n\n".join("Form %s:\n%s\n" % (f.form_data().name, tree_format(f)) for f in forms) elif format == "dot": if options.compile: print "Warning: compile option not used." data = [] nodeoffset = 0 for i, f in enumerate(forms): begin = (i == 0) end = (i == len(forms) - 1) dot, nodeoffset = ufl2dot(f, f.form_data().name, nodeoffset, begin, end, options.labeling, ufl_data.object_names) tmp = "/* Form %s: */\n%s\n" % (f.form_data().name, dot) data.append(tmp) rendered = "\n\n".join(data) elif format == "tex": rendered = forms2latexdocument(forms, uflfilename, compile=options.compile) else: print "Unknown rendering format ", format sys.exit(-1) # 4) Convert file format filetype = options.filetype # Default filetypes: if not filetype: if format == "str": filetype = "str" elif format == "repr": filetype = "repr" elif format == "tree": filetype = "tree" elif format == "dot": filetype = "dot" elif format == "tex": filetype = "tex" # Guess that the filetype is the ext, usually the case ext = filetype if ext and not ext.startswith("."): ext = "." + ext outputfilename = os.path.join(options.outputdir, basename + ext) # Pure text files: if filetype == "txt" or filetype == format: write_file(outputfilename, rendered) # Conversions from tex: elif format == "tex": texfile = os.path.join(options.outputdir, basename + ".tex") # TODO: Use a proper temp file? write_file(texfile, rendered) if filetype == "pdf": flags = "-file-line-error-style -interaction=nonstopmode" cmd = "pdflatex %s '%s'" % (flags, texfile) runcmd(cmd) if options.show: print outputfilename runcmd("evince '%s' &" % outputfilename) else: print "Unknown format and filetype combination:", format, filetype sys.exit(-1) # Conversions from dot: elif format == "dot": tempfile = os.path.join(options.outputdir, basename + ".dot") # TODO: Use a proper temp file? write_file(tempfile, rendered) if filetype in ("png", "ps", "svg", "gif", "dia", "imap", "cmapx"): # taken from "man dot" runcmd("dot -T%s -o'%s' '%s'" % (filetype, outputfilename, tempfile)) if options.show: runcmd("evince '%s' &" % outputfilename) elif filetype == "pdf": psfilename = os.path.join(options.outputdir, basename + ".ps") pdffilename = os.path.join(options.outputdir, basename + ".pdf") runcmd("dot -T%s -o'%s' '%s'" % (filetype, psfilename, tempfile)) runcmd("ps2pdf '%s' '%s'" % (psfilename, pdffilename)) if options.show: print pdffilename runcmd("evince '%s' &" % pdffilename) else: print "Unknown format and filetype combination:", format, filetype sys.exit(-1) # That's all we know! else: print "*** Error: Sorry, don't know how to render format '%s' for file type '%s'." \ % (format, filetype) print "Please try another combination, perhaps -fdot -tpdf?" sys.exit(-1) ufl-1.3.0/scripts/ufl-version000077500000000000000000000015661226300046600161760ustar00rootroot00000000000000#!/usr/bin/env python # Copyright (C) 2011 Anders Logg. # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2011-06-14 # Last changed: 2011-06-14 # This is a simple script that just prints the UFL version number. from ufl import __version__ print __version__ ufl-1.3.0/scripts/ufl2py000077500000000000000000000027031226300046600151400ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import with_statement import os, sys, optparse from ufl.algorithms import FormData, read_ufl_file, load_forms, validate_form, ufl2latex, tree_format # Get commandline options usage = """Convert a .ufl file to an executable .py file for debugging. Example: ufl2py Poisson.ufl""" def opt(long, short, t, default, help): return optparse.make_option("--%s" % long, "-%s" % short, action="store", type=t, dest=long, default=default, help=help) option_list = [] # opt("quiet", "q", "int", 1, "Do not print form information to screen."), # opt("write", "w", "int", 0, "Write form information to file."), # ] parser = optparse.OptionParser(usage=usage, option_list=option_list) args = sys.argv[1:] (options, args) = parser.parse_args(args=args) if not args: print "Missing files!" print parser.print_usage() sys.exit(-1) filenames = args header = """#!/usr/bin/env python from ufl import * set_level(DEBUG) """ footer = "" # Handle each form file separately for filename in filenames: if not filename.endswith(".ufl"): print "Warning: Filename '%s' doesn't end with .ufl." % filename # Read code fcode = read_ufl_file(filename) code = header + fcode + footer # Dump code to python file basename = os.path.splitext(os.path.basename(filename))[0] basename = "%s_debug" % basename pyname = "%s.py" % basename with file(pyname, "w") as f: f.write(code) ufl-1.3.0/setup.py000066400000000000000000000050101226300046600140060ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup from distutils import sysconfig from os.path import join as pjoin, split as psplit import sys import platform # Version number major = 1 minor = 3 #maintenance = '0+' maintenance = 0 #maintenance = '-alpha' #maintenance = '-beta' #maintenance = '-rc' if isinstance(maintenance, int): # Numbered release version = "%d.%d.%d" % (major, minor, maintenance) else: # Pre-release (-alpha, -beta, -rc) or dev version (+, .0+) version = "%d.%d%s" % (major, minor, maintenance) print print version print scripts = [pjoin("scripts", "ufl-analyse"), pjoin("scripts", "ufl-convert"), pjoin("scripts", "ufl-version"), pjoin("scripts", "ufl2py"), pjoin("scripts", "form2ufl")] if platform.system() == "Windows" or "bdist_wininst" in sys.argv: # In the Windows command prompt we can't execute Python scripts # without a .py extension. A solution is to create batch files # that runs the different scripts. batch_files = [] for script in scripts: batch_file = script + ".bat" f = open(batch_file, "w") f.write('python "%%~dp0\%s" %%*' % psplit(script)[1]) f.close() batch_files.append(batch_file) scripts.extend(batch_files) setup(name = "UFL", version = version, description = "Unified Form Language", author = "Martin Sandve Alnes, Anders Logg", author_email = "fenics@fenicsproject.org", url = "http://www.fenicsproject.org", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'Programming Language :: Python :: 2.5', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Software Development :: Compilers', 'Topic :: Software Development :: Libraries :: Python Modules', ], scripts = scripts, packages = ["ufl", "ufl.algorithms", "ufl.finiteelement"], package_dir = {"ufl": "ufl"}, data_files = [(pjoin("share", "man", "man1"), [pjoin("doc", "man", "man1", "ufl-analyse.1.gz"), pjoin("doc", "man", "man1", "ufl-convert.1.gz"), pjoin("doc", "man", "man1", "ufl-version.1.gz"), pjoin("doc", "man", "man1", "ufl2py.1.gz"), pjoin("doc", "man", "man1", "form2ufl.1.gz")])]) ufl-1.3.0/test/000077500000000000000000000000001226300046600132575ustar00rootroot00000000000000ufl-1.3.0/test/README000066400000000000000000000004141226300046600141360ustar00rootroot00000000000000To run these tests from within the source tree without needing to install the UFL Python module, update your PYTHONPATH and PATH by running source sourceme.sh in a bash shell or equivalent. If on other OSes, you must set the paths whichever way your OS requires. ufl-1.3.0/test/TODO000066400000000000000000000025111226300046600137460ustar00rootroot00000000000000 def reshape(x, shape): sh = x.shape() if shape == sh: return x else: pass # FIXME: Implement something like this for UFL # FIXME: Make Inner work with innermost indices instead of all in UFL? - Take back whatever code we need from newtests and delete it. These test files have been completely migrated to newtests: analyse_demos.py elements.py - Add missing lifting tests, both coverage and functionality - Need better testing of algorithms, covering all of { algorithms } x { types }. Improve tests file by file: R = Review M = Move somewhere more suitable A = Add something like this D = Done, looks good R test_elements.py A test_expr.py R test_classcoverage.py R test_literals.py A test_geometry.py A test_form_arguments.py R test_arithmetic.py R test_future_division.py R test_indices.py A test_tensoralgebra.py A test_listtensors.py R test_conditionals.py R test_lifting.py R test_evaluate.py R test_diff.py R test_derivative.py R test_forms.py R test_illegal.py R test_algorithms.py R test_transformations.py R test_expand_indices.py R test_split.py R test_simplify.py R test_str.py A test_expand_compounds.py A test_part_extracter.py R test_ffcforms.py R test_analyse_demos.py ufl-1.3.0/test/clean.sh000077500000000000000000000002171226300046600147000ustar00rootroot00000000000000#!/bin/bash # ufl-analyse output rm -f *_debug.py # python compiled files rm -f *.pyc # logs rm -f *.log # temp files from emacs rm -f *~ ufl-1.3.0/test/make_manual_testcase.py000077500000000000000000000041271226300046600200050ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import with_statement import os from os.path import join as pjoin import re from glob import glob #--- Build list of code snippets found in tex files --- path = "../doc/manual/chapters/" filenames = glob(pjoin(path, "*.tex")) codes = [] state = None for fn in filenames: with open(fn) as f: for l in f: if state is None: if re.match(r"^\\begin\{code\}", l): # TODO: Can get context information from this line by parsing comments? if "TESTME" in l: state = "" else: if re.match(r"^\\end\{code\}", l): codes.append(state) state = None else: state += l #--- Try executing all codes in an UFL environment --- def execute_codes(): code_prefix = """ from ufl import * from ufl.classes import * from ufl.algorithms import * """ code_suffix = """ completed = True """ failed = [] for code in codes: print "-------------------------" print code namespace = {} try: exec (code_prefix + code + code_suffix) in namespace except: print "Code execution failed." completed = namespace.get("completed", False) if completed: print "SUCCESS" else: print "FAILED" failed.append(code) #--- Generate unit test code --- def indent(text, spaces=4): return "\n".join(spaces*" " + t for t in text.split("\n")) testcode = """#!/usr/bin/env python __authors__ = "Automatically generated from .tex files" __date__ = "2009-02-07 -- 2009-02-07" from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import * from ufl.algorithms import * class ManualTestCase(UflTestCase): def setUp(self): pass %s if __name__ == "__main__": main() """ % "\n".join(" def test_%d(self):\n%s" % (i, indent(code, 8)) for (i, code) in enumerate(codes)) with open("manualtest.py", "w") as f: f.write(testcode) ufl-1.3.0/test/mock.py000077500000000000000000000043361226300046600145730ustar00rootroot00000000000000 from ufl import * from ufl.expr import Expr class MockExpr(Expr): "A mock type for unit testing." def __init__(self, shape=None, free_indices=None, index_dimensions=None, cell=None): Expr.__init__(self) self.fields = [] if not shape is None: self._shape = shape self.fields.append("shape") if not free_indices is None: self._free_indices = free_indices self.fields.append("free_indices") if not index_dimensions is None: self._index_dimensions = index_dimensions self.fields.append("index_dimensions") if not cell is None: self._cell = cell self.fields.append("cell") def shape(self): assert hasattr(self, "_shape") return self._shape def free_indices(self): assert hasattr(self, "_free_indices") return self._free_indices def index_dimensions(self): assert hasattr(self, "_index_dimensions") return self._index_dimensions def cell(self): assert hasattr(self, "_cell") return self._cell def matches(self, other): for field in self.fields: a = getattr(self, field)() b = getattr(other, field)() if not a == b: return False return True def __repr__(self): return "MockExpr(%s)" % ", ".join("%s=%s" % (k, repr(getattr(self, "_%s" % k))) for k in self.fields) def __iter__(self): raise NotImplementedError def _test(): a = MockExpr(shape=(1,)) b = MockExpr(shape=(2,)) assert not a.matches(b) i, j = indices(2) c = MockExpr(shape=(1,2), free_indices=(i,j), index_dimensions={i:2, j:3}, cell=triangle) d = MockExpr(shape=(1,2), free_indices=(i,j), index_dimensions={i:2, j:3}, cell=triangle) assert c.matches(d) e = FiniteElement("CG", triangle, 1) f = Coefficient(e) g = MockExpr(shape=(), free_indices=(), index_dimensions={}, cell=triangle) assert g.matches(f) h = MockExpr(shape=(1,), free_indices=(), index_dimensions={}, cell=triangle) assert not h.matches(f) if __name__ == "__main__": _test() ufl-1.3.0/test/sourceme.sh000066400000000000000000000003041226300046600154320ustar00rootroot00000000000000# Set python path to find the local uninstalled ufl version export PYTHONPATH="`pwd`/..:$PYTHONPATH" export PATH="`pwd`/../scripts:$PATH" echo PYTHONPATH is now $PYTHONPATH echo PATH is now $PATH ufl-1.3.0/test/template_test.py000077500000000000000000000027131226300046600165110ustar00rootroot00000000000000#!/usr/bin/env python """ This is a template file you can copy when making a new test case. Begin by copying this file to a filename matching test_*.py. The tests in the file will then automatically be run by ./test.py. Next look at the TODO markers below for places to edit. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * # TODO: Import only what you need from classes and algorithms: #from ufl.classes import ... #from ufl.algorithms import ... # TODO: Rename test case class to something unique and descriptive: class TemplateTestCase(UflTestCase): def setUp(self): super(TemplateTestCase, self).setUp() # TODO: If needed, add shared code here for setting up a test fixture def tearDown(self): # TODO: If needed, add shared code here for tearing down a test fixture super(TemplateTestCase, self).tearDown() # TODO: Add as many test_foo() functions as needed, use descriptive names def test_doing_this_should_have_that_effect(self): # TODO: Use the most descriptive assertion function, e.g.: self.assertTrue(42) self.assertEqual("hi", "h" + "i") self.assertIsInstance(triangle, Cell) self.assertRaises(NameError, lambda: unknown) # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test.py000077500000000000000000000034531226300046600146200ustar00rootroot00000000000000#!/usr/bin/env python """Run all tests""" __author__ = "Martin Alnes (martinal@simula.no) and Anders Logg (logg@simula.no)" __date__ = "2008-03-12 -- 2011-04-08" def discover_tests(args): import glob # Running tests from all test_foo.py files tests = sorted(f.replace(".py", "") for f in glob.glob("test_*.py")) # Demos are slow, allow running everything else easily... tests.remove("test_analyse_demos") # ... and always check demos last if not "skipdemos" in args: tests.append("test_analyse_demos") return tests def configureLogging(): # Emit all messages, show nothing on screen, # but write everything to log file import logging from ufl.log import ufl_logger sh = ufl_logger.get_handler() fh = ufl_logger.add_logfile(level = logging.DEBUG) ufl_logger.set_level(logging.DEBUG) sh.setLevel(logging.CRITICAL) #fh.setLevel(logging.DEBUG) def run_suite(tests): import unittest assert tests loader = unittest.TestLoader() modules = [__import__(test) for test in tests] suite = loader.loadTestsFromModule(modules[0]) for m in modules[1:]: suite.addTests(loader.loadTestsFromModule(m)) runner = unittest.TextTestRunner(verbosity=2) return runner.run(suite) def check_which_ufl(): import ufl print "******" print "* Testing ufl version", ufl.__version__ print "* which is installed at:", ufl.__file__ print "******" def main(args): check_which_ufl() tests = discover_tests(args) configureLogging() result = run_suite(tests) if result.wasSuccessful(): print "All tests finished successfully." return 0 else: print "Not all tests finished successfully." return 1 if __name__ == "__main__": import sys sys.exit(main(sys.argv[1:])) ufl-1.3.0/test/test_algorithms.py000077500000000000000000000142741226300046600170540ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-03-12 -- 2009-01-28" # Modified by Anders Logg, 2008 # Modified by Garth N. Wells, 2009 from ufltestcase import UflTestCase, main from pprint import * from ufl import * from ufl.algorithms import * from ufl.classes import Sum, Product # TODO: add more tests, covering all utility algorithms class AlgorithmsTestCase(UflTestCase): def setUp(self): super(AlgorithmsTestCase, self).setUp() element = FiniteElement("CG", triangle, 1) v = TestFunction(element) u = TrialFunction(element) c = Coefficient(element) f = Coefficient(element) n = triangle.n a = u*v*dx L = f*v*dx b = u*v*dx(0) +inner(c*grad(u),grad(v))*dx(1) + dot(n, grad(u))*v*ds + f*v*dx self.elements = (element,) self.arguments = (v, u) self.coefficients = (c, f) self.forms = (a, L, b) if False: print print form_info(a) print print form_info(L) print print form_info(b) print if False: print print [str(c) for c in self.coefficients] print print str(self.forms[2]) print print [str(b) for b in extract_arguments(self.forms[2])] print print self.coefficients print print repr(self.forms[2]) print print extract_arguments(self.forms[2]) print def tearDown(self): super(AlgorithmsTestCase, self).tearDown() def test_flatten(self): element = FiniteElement("CG", "triangle", 1) a = Coefficient(element) b = Coefficient(element) c = Coefficient(element) d = Coefficient(element) a = (a+b)+(c+d) fa = flatten(a) assert isinstance(a, Sum) and len(a.operands()) == 2 assert isinstance(fa, Sum) and len(fa.operands()) == 4 aa, ab = a.operands() assert isinstance(aa, Sum) and len(aa.operands()) == 2 assert isinstance(ab, Sum) and len(ab.operands()) == 2 a = (a*b)*(c*d) fa = flatten(a) assert isinstance(a, Product) and len(a.operands()) == 2 assert isinstance(fa, Product) and len(fa.operands()) == 4 aa, ab = a.operands() assert isinstance(aa, Product) and len(aa.operands()) == 2 assert isinstance(ab, Product) and len(ab.operands()) == 2 def test_arguments(self): assert self.arguments == tuple(extract_arguments(self.forms[0])) assert tuple(self.arguments[:1]) == tuple(extract_arguments(self.forms[1])) def test_coefficients(self): assert self.coefficients == tuple(extract_coefficients(self.forms[2])) def test_elements(self): #print elements(self.forms[2]) #print unique_elements(self.forms[2]) #print unique_classes(self.forms[2]) d = extract_duplications(self.forms[2].integrals(Measure.CELL)[0]._integrand) #pprint(list(d)) element1 = FiniteElement("CG", triangle, 1) element2 = FiniteElement("CG", triangle, 1) v = TestFunction(element1) u = TrialFunction(element2) a = u*v*dx self.assertEqual((element1, element2), extract_elements(a)) self.assertEqual((element1,), extract_unique_elements(a)) def test_walk(self): element = FiniteElement("CG", "triangle", 1) v = TestFunction(element) f = Coefficient(element) p = f*v a = p*dx prestore = [] def pre(o, stack): prestore.append((o, len(stack))) poststore = [] def post(o, stack): poststore.append((o, len(stack))) for itg in a.integrals(Measure.CELL): walk(itg.integrand(), pre, post) self.assertEqual(prestore, [(p, 0), (v, 1), (f, 1)]) # NB! Sensitive to ordering of expressions. self.assertEqual(poststore, [(v, 1), (f, 1), (p, 0)]) # NB! Sensitive to ordering of expressions. #print "\n"*2 + "\n".join(map(str,prestore)) #print "\n"*2 + "\n".join(map(str,poststore)) def test_traversal(self): element = FiniteElement("CG", "triangle", 1) v = TestFunction(element) f = Coefficient(element) g = Coefficient(element) p1 = f*v p2 = g*v s = p1 + p2 pre_traverse = list(pre_traversal(s)) post_traverse = list(post_traversal(s)) self.assertEqual(pre_traverse, [s, p1, v, f, p2, v, g]) # NB! Sensitive to ordering of expressions. self.assertEqual(post_traverse, [v, f, p1, v, g, p2, s]) # NB! Sensitive to ordering of expressions. def test_expand_indices(self): element = FiniteElement("Lagrange", triangle, 2) v = TestFunction(element) u = TrialFunction(element) def evaluate(form): return form.cell_integral()[0].integrand()((), { v: 3, u: 5 }) # TODO: How to define values of derivatives? a = div(grad(v))*u*dx #a1 = evaluate(a) a = expand_derivatives(a) #a2 = evaluate(a) a = expand_indices(a) #a3 = evaluate(a) # TODO: Compare a1, a2, a3 # TODO: Test something more def test_adjoint(self): cell = triangle V1 = FiniteElement("CG", cell, 1) V2 = FiniteElement("CG", cell, 2) u = TrialFunction(V1) v = TestFunction(V2) self.assertGreater(u.count(), v.count()) u2 = Argument(V1) v2 = Argument(V2) self.assertLess(u2.count(), v2.count()) a = u*v*dx a_arg_degrees = [arg.element().degree() for arg in extract_arguments(a)] self.assertEqual(a_arg_degrees, [2, 1]) b = adjoint(a) b_arg_degrees = [arg.element().degree() for arg in extract_arguments(b)] self.assertEqual(b_arg_degrees, [1, 2]) c = adjoint(a, (u2, v2)) c_arg_degrees = [arg.element().degree() for arg in extract_arguments(c)] self.assertEqual(c_arg_degrees, [1, 2]) d = adjoint(b) d_arg_degrees = [arg.element().degree() for arg in extract_arguments(d)] self.assertEqual(d_arg_degrees, [2, 1]) if __name__ == "__main__": main() ufl-1.3.0/test/test_analyse_demos.py000077500000000000000000000054411226300046600175220ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-09-28 -- 2008-09-28" from ufltestcase import UflTestCase, main import os from ufl.algorithms import load_ufl_file, validate_form # Taken from http://ivory.idyll.org/blog/mar-07/replacing-commands-with-subprocess from subprocess import Popen, PIPE, STDOUT def get_status_output(cmd, input=None, cwd=None, env=None): pipe = Popen(cmd, shell=True, cwd=cwd, env=env, stdout=PIPE, stderr=STDOUT) (output, errout) = pipe.communicate(input=input) assert not errout status = pipe.returncode return (status, output) from glob import glob class DemoTestCase(UflTestCase): def setUp(self): super(DemoTestCase, self).setUp() #for f in glob("ufl_analyse_tmp_form*"): # os.remove(f) def tearDown(self): #for f in glob("ufl_analyse_tmp_form*"): # os.remove(f) super(DemoTestCase, self).tearDown() def _test_all_demos(self): # Check all at once skip = set(glob("../demo/_*.ufl")) filenames = [f for f in sorted(glob("../demo/*.ufl")) if not f in skip] cmd = "ufl-analyse %s" % " ".join(filenames) status, output = get_status_output(cmd) self.assertEqual(status, 0) def get_demo_filenames(self): skiplist = glob("../demo/_*.ufl") #+ ["../demo/Hyperelasticity3D.ufl"] filenames = [] for f in sorted(glob("../demo/*.ufl")): if f in skiplist: print "Skipping demo %s" % f else: filenames.append(f) return filenames def xtest_each_demo_with_ufl_analyse(self): "Check each file from cmdline with ufl-analyse." for f in self.get_demo_filenames(): cmd = "ufl-analyse %s" % f status, output = get_status_output(cmd) self.assertEqual(status, 0) if status == 0: print "Successfully analysed %s without problems" % f else: name = "%s.analysis" % f print "Encountered problems when analysing %s "\ "(return code %s), see output in file %s" % (f, status, name) of = open(name, "w") of.write(output) of.close() print print output print def test_each_demo_with_validate_form(self): "Check each form in each file with validate_form." for filename in self.get_demo_filenames(): data = load_ufl_file(filename) for form in data.forms: try: validate_form(form) excepted = 0 except: excepted = 1 self.assertEqual(excepted, 0) if __name__ == "__main__": main() ufl-1.3.0/test/test_arithmetic.py000077500000000000000000000056661226300046600170410ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import Division, FloatValue, IntValue class ArithmeticTestCase(UflTestCase): def setUp(self): super(ArithmeticTestCase, self).setUp() def tearDown(self): super(ArithmeticTestCase, self).tearDown() def test_scalar_casting(self): f = as_ufl(2.0) r = as_ufl(4) self.assertIsInstance(f, FloatValue) self.assertIsInstance(r, IntValue) self.assertEqual(f, 2.0) self.assertEqual(r, 4) def test_ufl_float_division(self): d = triangle.x[0] / 10.0 # TODO: Use mock instead of x self.assertIsInstance(d, Division) def test_float_ufl_division(self): d = 3.14 / triangle.x[0] # TODO: Use mock instead of x self.assertIsInstance(d, Division) def test_float_division(self): d = as_ufl(20.0) / 10.0 self.assertIsInstance(d, FloatValue) self.assertEqual(d, 2) def test_int_division(self): # UFL treats all divisions as true division d = as_ufl(40) / 7 self.assertIsInstance(d, FloatValue) self.assertEqual(d, 40.0 / 7.0) #self.assertAlmostEqual(d, 40 / 7.0, 15) def test_float_int_division(self): d = as_ufl(20.0) / 5 self.assertIsInstance(d, FloatValue) self.assertEqual(d, 4) def test_floor_division_fails(self): f = as_ufl(2.0) r = as_ufl(4) s = as_ufl(5) self.assertRaises(NotImplementedError, lambda: r // 4) self.assertRaises(NotImplementedError, lambda: r // s) self.assertRaises(NotImplementedError, lambda: f // s) def test_elem_mult(self): self.assertEqual(elem_mult(2, 3), 6) v = as_vector((1,2,3)) u = as_vector((4,5,6)) self.assertEqual(elem_mult(v, u), as_vector((4,10,18))) def test_elem_mult_on_matrices(self): A = as_matrix(((1,2),(3,4))) B = as_matrix(((4,5),(6,7))) self.assertEqual(elem_mult(A, B), as_matrix(((4,10),(18,28)))) x, y = triangle.x A = as_matrix(((x,y),(3,4))) B = as_matrix(((4,5),(y,x))) self.assertEqual(elem_mult(A, B), as_matrix(((4*x,5*y),(3*y,4*x)))) x, y = triangle.x A = as_matrix(((x,y),(3,4))) B = Identity(2) self.assertEqual(elem_mult(A, B), as_matrix(((x,0),(0,4)))) def test_elem_div(self): x, y, z = tetrahedron.x A = as_matrix(((x,y,z),(3,4,5))) B = as_matrix(((7,8,9),(z,x,y))) self.assertEqual(elem_div(A, B), as_matrix(((x/7,y/8,z/9),(3/z,4/x,5/y)))) def test_elem_op(self): x, y, z = tetrahedron.x A = as_matrix(((x,y,z),(3,4,5))) self.assertEqual(elem_op(sin, A), as_matrix(((sin(x),sin(y),sin(z)), (sin(3),sin(4),sin(5))))) self.assertEqual(elem_op(sin, A).dx(0).shape(), (2, 3)) if __name__ == "__main__": main() ufl-1.3.0/test/test_automatic_differentiation.py000077500000000000000000000512041226300046600221150ustar00rootroot00000000000000#!/usr/bin/env python """ These tests should cover the behaviour of the automatic differentiation algorithm at a technical level, and are thus implementation specific. Other tests check for mathematical correctness of diff and derivative. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main from itertools import chain import ufl # This imports everything external code will see from ufl from ufl import * import ufl.algorithms from ufl.common import fast_post_traversal2 from ufl.conditional import Conditional from ufl.algorithms import expand_derivatives from ufl.algorithms.traversal import traverse_terminals2 class ExpressionCollection(object): def __init__(self, cell): self.cell = cell d = cell.d x = cell.x n = cell.n c = cell.volume h = cell.circumradius f = cell.facet_area s = cell.surface_area I = Identity(d) eps = PermutationSymbol(d) U = FiniteElement("U", cell, None) V = VectorElement("U", cell, None) W = TensorElement("U", cell, None) u = Coefficient(U) v = Coefficient(V) w = Coefficient(W) du = Argument(U) dv = Argument(V) dw = Argument(W) class ObjectCollection(object): pass self.shared_objects = ObjectCollection() for key,value in list(locals().items()): setattr(self.shared_objects, key, value) self.literals = list(map(as_ufl, [0, 1, 3.14, I, eps])) self.geometry = [x, n, c, h, f, s] self.functions = [u, du, v, dv, w, dw] self.terminals = [] self.terminals += self.literals self.terminals += self.geometry self.terminals += self.functions self.algebra = ([ u*2, v*2, w*2, u+2*u, v+2*v, w+2*w, 2/u, u/2, v/2, w/2, u**3, 3**u, ]) self.mathfunctions = ([ abs(u), sqrt(u), exp(u), ln(u), cos(u), sin(u), tan(u), acos(u), asin(u), atan(u), erf(u), bessel_I(1,u), bessel_J(1,u), bessel_K(1,u), bessel_Y(1,u), ]) self.variables = ([ variable(u), variable(v), variable(w), variable(w*u), 3*variable(w*u), ]) # Indexed, ListTensor, ComponentTensor, IndexSum i,j,k,l = indices(4) self.indexing = ([ v[0], w[d-1,0], v[i], w[i,j], v[:], w[0,:], w[:,0], v[...], w[0,...], w[...,0], v[i]*v[j], w[i,0]*v[j], w[d-1,j]*v[i], v[i]*v[i], w[i,0]*w[0,i], v[i]*w[0,i], v[j]*w[d-1,j], w[i,i], w[i,j]*w[j,i], as_tensor(v[i]*w[k,0], (k,i)), as_tensor(v[i]*w[k,0], (k,i))[:,l], as_tensor(w[i,j]*w[k,l], (k,j,l,i)), as_tensor(w[i,j]*w[k,l], (k,j,l,i))[0,0,0,0], as_vector((u,2,3)), as_matrix(((u**2,u**3),(u**4,u**5))), as_vector((u,2,3))[i], as_matrix(((u**2,u**3),(u**4,u**5)))[i,j]*w[i,j], ]) self.conditionals = ([ conditional(le(u, 1.0), 1, 0), conditional(eq(3.0, u), 1, 0), conditional(ne(sin(u), cos(u)), 1, 0), conditional(lt(sin(u), cos(u)), 1, 0), conditional(ge(sin(u), cos(u)), 1, 0), conditional(gt(sin(u), cos(u)), 1, 0), conditional(And(lt(u,3), gt(u,1)), 1, 0), conditional(Or(lt(u,3), gt(u,1)), 1, 0), conditional(Not(ge(u,0.0)), 1, 0), conditional(le(u,0.0), 1, 2), conditional(Not(ge(u,0.0)), 1, 2), conditional(And(Not(ge(u,0.0)), lt(u,1.0)), 1, 2), conditional(le(u,0.0), u**3, ln(u)), ]) self.restrictions = [u('+'), u('-'), v('+'), v('-'), w('+'), w('-')] if d > 1: i,j = indices(2) self.restrictions += ([ v('+')[i]*v('+')[i], v[i]('+')*v[i]('+'), (v[i]*v[i])('+'), (v[i]*v[j])('+')*w[i,j]('+'), ]) self.noncompounds = [] self.noncompounds += self.algebra self.noncompounds += self.mathfunctions self.noncompounds += self.variables self.noncompounds += self.indexing self.noncompounds += self.conditionals self.noncompounds += self.restrictions if d == 1: self.tensorproducts = [] else: self.tensorproducts = ([ dot(v,v), dot(v,w), dot(w,w), inner(v,v), inner(w,w), outer(v,v), outer(w,v), outer(v,w), outer(w,w), ]) if d == 1: self.tensoralgebra = [] else: self.tensoralgebra = ([ w.T, sym(w), skew(w), dev(w), det(w), tr(w), cofac(w), inv(w), ]) if d != 3: self.crossproducts = [] else: self.crossproducts = ([ cross(v,v), cross(v,2*v), cross(v,w[0,:]), cross(v,w[:,1]), cross(w[:,0],v), ]) self.compounds = [] self.compounds += self.tensorproducts self.compounds += self.tensoralgebra self.compounds += self.crossproducts self.all_expressions = [] self.all_expressions += self.terminals self.all_expressions += self.noncompounds self.all_expressions += self.compounds class ForwardADTestCase(UflTestCase): def setUp(self): super(ForwardADTestCase, self).setUp() self.expr = {} self.expr[1] = ExpressionCollection(cell1D) self.expr[2] = ExpressionCollection(cell2D) self.expr[3] = ExpressionCollection(cell3D) def tearDown(self): super(ForwardADTestCase, self).tearDown() def assertEqualTotalShape(self, value, expected): self.assertEqual(value.shape(), expected.shape()) self.assertEqual(set(value.free_indices()), set(expected.free_indices())) self.assertEqual(value.index_dimensions(), expected.index_dimensions()) def ad_algorithm(self, expr): #alt = 1 #alt = 4 #alt = 6 alt = 0 if alt == 0: return expand_derivatives(expr) elif alt == 1: return expand_derivatives(expr, apply_expand_compounds_before=True, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=True) elif alt == 2: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=True, use_alternative_wrapper_algorithm=False) elif alt == 3: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=False) elif alt == 4: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=True) elif alt == 5: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=False) def _test_no_derivatives_no_change(self, collection): for expr in collection: before = expr after = self.ad_algorithm(before) #print '\n', str(before), '\n', str(after), '\n' self.assertEqualTotalShape(before, after) self.assertEqual(before, after) def _test_no_derivatives_but_still_changed(self, collection): # Planning to fix these: for expr in collection: before = expr after = self.ad_algorithm(before) #print '\n', str(before), '\n', str(after), '\n' self.assertEqualTotalShape(before, after) #self.assertEqual(before, after) # Without expand_compounds self.assertNotEqual(before, after) # With expand_compounds def test_only_terminals_no_change(self): for d in (1,2,3): ex = self.expr[d] self._test_no_derivatives_no_change(ex.terminals) def test_no_derivatives_no_change(self): for d in (1,2,3): ex = self.expr[d] self._test_no_derivatives_no_change(ex.noncompounds) def xtest_compounds_no_derivatives_no_change(self): # This test fails with expand_compounds enabled for d in (1,2,3): ex = self.expr[d] self._test_no_derivatives_no_change(ex.compounds) def test_zero_derivatives_of_terminals_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_zero_derivatives_of_terminals_produce_the_right_types_and_shapes(ex) def _test_zero_derivatives_of_terminals_produce_the_right_types_and_shapes(self, collection): c = Constant(collection.shared_objects.cell) u = Coefficient(collection.shared_objects.U) v = Coefficient(collection.shared_objects.V) w = Coefficient(collection.shared_objects.W) for t in collection.terminals: for var in (u, v, w): before = derivative(t, var) # This will often get preliminary simplified to zero after = self.ad_algorithm(before) expected = 0*t #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) before = derivative(c*t, var) # This will usually not get simplified to zero after = self.ad_algorithm(before) expected = 0*t #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) def test_zero_diffs_of_terminals_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_zero_diffs_of_terminals_produce_the_right_types_and_shapes(ex) def _test_zero_diffs_of_terminals_produce_the_right_types_and_shapes(self, collection): c = Constant(collection.shared_objects.cell) u = Coefficient(collection.shared_objects.U) v = Coefficient(collection.shared_objects.V) w = Coefficient(collection.shared_objects.W) vu = variable(u) vv = variable(v) vw = variable(w) for t in collection.terminals: for var in (vu, vv, vw): before = diff(t, var) # This will often get preliminary simplified to zero after = self.ad_algorithm(before) expected = 0*outer(t, var) #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) before = diff(c*t, var) # This will usually not get simplified to zero after = self.ad_algorithm(before) expected = 0*outer(t, var) #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) def test_zero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_zero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(ex) def _test_zero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(self, collection): debug = 0 u = Coefficient(collection.shared_objects.U) v = Coefficient(collection.shared_objects.V) w = Coefficient(collection.shared_objects.W) #for t in chain(collection.noncompounds, collection.compounds): for t in collection.noncompounds: for var in (u, v, w): if debug: print '\n', '...: ', t.shape(), var.shape(), '\n' before = derivative(t, var) if debug: print '\n', 'before: ', str(before), '\n' after = self.ad_algorithm(before) if debug: print '\n', 'after: ', str(after), '\n' expected = 0*t if debug: print '\n', 'expected: ', str(expected), '\n' #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) def test_zero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_zero_diffs_of_noncompounds_produce_the_right_types_and_shapes(ex) def _test_zero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self, collection): debug = 0 u = Coefficient(collection.shared_objects.U) v = Coefficient(collection.shared_objects.V) w = Coefficient(collection.shared_objects.W) vu = variable(u) vv = variable(v) vw = variable(w) #for t in chain(collection.noncompounds, collection.compounds): for t in collection.noncompounds: for var in (vu, vv, vw): before = diff(t, var) if debug: print '\n', 'before: ', str(before), '\n' after = self.ad_algorithm(before) if debug: print '\n', 'after: ', str(after), '\n' expected = 0*outer(t, var) if debug: print '\n', 'expected: ', str(expected), '\n' #print '\n', str(expected), '\n', str(after), '\n', str(before), '\n' self.assertEqual(after, expected) def test_nonzero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_nonzero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(ex) def _test_nonzero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(self, collection): debug = 0 u = collection.shared_objects.u v = collection.shared_objects.v w = collection.shared_objects.w #for t in chain(collection.noncompounds, collection.compounds): for t in collection.noncompounds: for var in (u, v, w): # Include d/dx [z ? y: x] but not d/dx [x ? f: z] if isinstance(t, Conditional) and (var in fast_post_traversal2(t.operands()[0])): if debug: print "Depends on %s :: %s" % (str(var), str(t)) continue if debug: print '\n', '...: ', t.shape(), var.shape(), '\n' before = derivative(t, var) if debug: print '\n', 'before: ', str(before), '\n' after = self.ad_algorithm(before) if debug: print '\n', 'after: ', str(after), '\n' expected_shape = 0*t if debug: print '\n', 'expected_shape: ', str(expected_shape), '\n' #print '\n', str(expected_shape), '\n', str(after), '\n', str(before), '\n' if var in fast_post_traversal2(t): self.assertEqualTotalShape(after, expected_shape) self.assertNotEqual(after, expected_shape) else: self.assertEqual(after, expected_shape) def test_nonzero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self): for d in (1,2,3): ex = self.expr[d] self._test_nonzero_diffs_of_noncompounds_produce_the_right_types_and_shapes(ex) def _test_nonzero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self, collection): debug = 0 u = collection.shared_objects.u v = collection.shared_objects.v w = collection.shared_objects.w vu = variable(u) vv = variable(v) vw = variable(w) #for t in chain(collection.noncompounds, collection.compounds): for t in collection.noncompounds: t = replace(t, {u:vu, v:vv, w:vw}) for var in (vu, vv, vw): # Include d/dx [z ? y: x] but not d/dx [x ? f: z] if isinstance(t, Conditional) and (var in fast_post_traversal2(t.operands()[0])): if debug: print "Depends on %s :: %s" % (str(var), str(t)) continue before = diff(t, var) if debug: print '\n', 'before: ', str(before), '\n' after = self.ad_algorithm(before) if debug: print '\n', 'after: ', str(after), '\n' expected_shape = 0*outer(t, var) # expected shape, not necessarily value if debug: print '\n', 'expected_shape: ', str(expected_shape), '\n' #print '\n', str(expected_shape), '\n', str(after), '\n', str(before), '\n' if var in fast_post_traversal2(t): self.assertEqualTotalShape(after, expected_shape) self.assertNotEqual(after, expected_shape) else: self.assertEqual(after, expected_shape) def test_grad_coeff(self): for d in (1,2,3): collection = self.expr[d] u = collection.shared_objects.u v = collection.shared_objects.v w = collection.shared_objects.w for f in (u,v,w): before = grad(f) after = self.ad_algorithm(before) if before.shape() != after.shape(): print '\n', 'shapes:', before.shape(), after.shape() print '\n', str(before), '\n', str(after), '\n' self.assertEqualTotalShape(before, after) if f is u: # Differing by being wrapped in indexing types self.assertEqual(before, after) before = grad(grad(f)) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(before, after) # Differing by being wrapped in indexing types before = grad(grad(grad(f))) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(before, after) # Differing by being wrapped in indexing types def test_derivative_grad_coeff(self): for d in (1,2,3): collection = self.expr[d] u = collection.shared_objects.u v = collection.shared_objects.v w = collection.shared_objects.w for f in (u,v,w): before = derivative(grad(f),f) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) before = derivative(grad(grad(f)),f) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) before = derivative(grad(grad(grad(f))),f) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) if 0: print print 'B', f, "::", before print 'A', f, "::", after def xtest_derivative_grad_coeff_with_variation_components(self): for d in (1,2,3): collection = self.expr[d] v = collection.shared_objects.v w = collection.shared_objects.w dv = collection.shared_objects.dv dw = collection.shared_objects.dw for g,dg in ((v,dv),(w,dw)): # Pick a single component ii = (0,)*(g.rank()) f = g[ii] df = dg[ii] before = derivative(grad(g),f,df) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) before = derivative(grad(grad(g)),f,df) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) before = derivative(grad(grad(grad(g))),f,df) after = self.ad_algorithm(before) self.assertEqualTotalShape(before, after) #self.assertEqual(after, expected) if 0: print print 'B', f, "::", before print 'A', f, "::", after # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_book_snippets.py000077500000000000000000000352611226300046600175610ustar00rootroot00000000000000#!/usr/bin/env python """ This file contains snippets from the FEniCS book, and allows us to test that these can still run with future versions of UFL. Please don't change these and please do keep UFL compatible with these snippets as long as possible. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main from ufl import * from ufl.algorithms import * class BookTestCase(UflTestCase): def test_uflcode_269(self): # Finite element spaces cell = tetrahedron element = VectorElement("Lagrange", cell, 1) # Form arguments phi0 = TestFunction(element) phi1 = TrialFunction(element) u = Coefficient(element) c1 = Constant(cell) c2 = Constant(cell) # Deformation gradient Fij = dXi/dxj I = Identity(cell.d) F = I + grad(u) # Right Cauchy-Green strain tensor C with invariants C = variable(F.T*F) I_C = tr(C) II_C = (I_C**2 - tr(C*C))/2 # Mooney-Rivlin constitutive law W = c1*(I_C-3) + c2*(II_C-3) # Second Piola-Kirchoff stress tensor S = 2*diff(W, C) # Weak forms L = inner(F*S, grad(phi0))*dx a = derivative(L, u, phi1) def test_uflcode_316(self): shapestring = 'triangle' cell = Cell(shapestring) def test_uflcode_323(self): cell = tetrahedron def test_uflcode_356(self): cell = tetrahedron P = FiniteElement("Lagrange", cell, 1) V = VectorElement("Lagrange", cell, 2) T = TensorElement("DG", cell, 0, symmetry=True) TH = V*P ME = MixedElement(T, V, P) def test_uflcode_400(self): V = FiniteElement("CG", triangle, 1) f = Coefficient(V) g = Coefficient(V) h = Coefficient(V) w = Coefficient(V) v = TestFunction(V) u = TrialFunction(V) # ... a = w*dot(grad(u), grad(v))*dx L = f*v*dx + g**2*v*ds(0) + h*v*ds(1) def test_uflcode_469(self): V = FiniteElement("CG", triangle, 1) f = Coefficient(V) g = Coefficient(V) h = Coefficient(V) v = TestFunction(V) # ... dx02 = dx(0, { "integration_order": 2 }) dx14 = dx(1, { "integration_order": 4 }) dx12 = dx(1, { "integration_order": 2 }) L = f*v*dx02 + g*v*dx14 + h*v*dx12 def test_uflcode_552(self): element = FiniteElement("CG", triangle, 1) # ... phi = Argument(element) v = TestFunction(element) u = TrialFunction(element) def test_uflcode_563(self): cell = triangle element = FiniteElement("CG", cell, 1) # ... w = Coefficient(element) c = Constant(cell) v = VectorConstant(cell) M = TensorConstant(cell) def test_uflcode_574(self): V0 = FiniteElement("CG", triangle, 1) V1 = V0 # ... V = V0*V1 u = Coefficient(V) u0, u1 = split(u) def test_uflcode_582(self): V0 = FiniteElement("CG", triangle, 1) V1 = V0 # ... V = V0*V1 u = Coefficient(V) u0, u1 = split(u) # ... v0, v1 = TestFunctions(V) u0, u1 = TrialFunctions(V) f0, f1 = Coefficients(V) def test_uflcode_644(self): V = VectorElement("CG", triangle, 1) u = Coefficient(V) v = Coefficient(V) # ... A = outer(u, v) Aij = A[i, j] def test_uflcode_651(self): V = VectorElement("CG", triangle, 1) u = Coefficient(V) v = Coefficient(V) # ... Aij = v[j]*u[i] A = as_tensor(Aij, (i, j)) def test_uflcode_671(self): i = Index() j, k, l = indices(3) def test_uflcode_684(self): V = VectorElement("CG", triangle, 1) v = Coefficient(V) # ... th = pi/2 A = as_matrix([[ cos(th), -sin(th)], [ sin(th), cos(th)]]) u = A*v def test_uflcode_824(self): V = VectorElement("CG", triangle, 1) f = Coefficient(V) # ... df = Dx(f, i) df = f.dx(i) def test_uflcode_886(self): cell = triangle # ... g = sin(cell.x[0]) v = variable(g) f = exp(v**2) h = diff(f, v) # ... #print v #print h def test_python_894(self): # We don't have to keep the string output compatible, so no test here. pass #>>> print v #var0(sin((x)[0])) #>>> print h #d/d[var0(sin((x)[0]))] (exp((var0(sin((x)[0]))) ** 2)) def test_uflcode_930(self): condition = lt(1, 0) true_value = 1 false_value = 0 # ... f = conditional(condition, true_value, false_value) def test_uflcode_1003(self): # Not testable, but this is tested below anyway "a = derivative(L, w, u)" pass def test_uflcode_1026(self): element = FiniteElement("CG", triangle, 1) # ... v = TestFunction(element) u = TrialFunction(element) w = Coefficient(element) f = 0.5*w**2*dx F = derivative(f, w, v) J = derivative(F, w, u) def test_uflcode_1050(self): Vx = VectorElement("Lagrange", triangle, 1) Vy = FiniteElement("Lagrange", triangle, 1) u = Coefficient(Vx*Vy) x, y = split(u) f = inner(grad(x), grad(x))*dx + y*dot(x,x)*dx F = derivative(f, u) J = derivative(F, u) def test_uflcode_1085(self): cell = triangle # ... V = VectorElement("Lagrange", cell, 1) T = TensorElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) M = Coefficient(T) a = M[i,j]*u[k].dx(j)*v[k].dx(i)*dx astar = adjoint(a) def test_uflcode_1120(self): cell = triangle # ... V = FiniteElement("Lagrange", cell, 1) v = TestFunction(V) f = Coefficient(V) g = Coefficient(V) L = f**2 / (2*g)*v*dx L2 = replace(L, { f: g, g: 3}) L3 = g**2 / 6*v*dx def test_uflcode_1157(self): cell = triangle # ... V = FiniteElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) f = Coefficient(V) pde = u*v*dx - f*v*dx a, L = system(pde) def test_uflcode_1190(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) V = element u = TrialFunction(V) v = TestFunction(V) f = Coefficient(V) c = variable(Coefficient(V)) pde = c*u*v*dx - c*f*v*dx a, L = system(pde) # ... u = Coefficient(element) sL = diff(L, c) - action(diff(a, c), u) def test_uflcode_1195(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) V = element u = TrialFunction(V) v = TestFunction(V) f = Coefficient(V) c = variable(Coefficient(V)) pde = c*u*v*dx - c*f*v*dx a, L = system(pde) u = Coefficient(element) # ... sL = sensitivity_rhs(a, u, L, c) def test_uflcode_1365(self): e = 0 v = variable(e) f = sin(v) g = diff(f, v) def test_python_1426(self): # Covered by the below test pass #from ufl.algorithms import Graph #G = Graph(expression) #V, E = G def test_python_1446(self): cell = triangle V = FiniteElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) c = Constant(cell) f = Coefficient(V) e = c*f**2*u*v from ufl.algorithms import Graph, partition G = Graph(e) V, E, = G if 0: print "str(e) = %s\n" % str(e) print "\n".join("V[%d] = %s" % (i, v) for (i, v) in enumerate(V)), "\n" print "\n".join("E[%d] = %s" % (i, e) for (i, e) in enumerate(E)), "\n" def test_python_1512(self): cell = triangle V = FiniteElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) c = Constant(cell) f = Coefficient(V) e = c*f**2*u*v from ufl.algorithms import Graph, partition G = Graph(e) V, E, = G # ... Vin = G.Vin() Vout = G.Vout() def test_python_1557(self): cell = triangle V = FiniteElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) c = Constant(cell) f = Coefficient(V) e = c*f**2*u*v from ufl.algorithms import Graph, partition G = Graph(e) V, E, = G # ... partitions, keys = partition(G) for deps in sorted(partitions.keys()): P = partitions[deps] #print "The following depends on", tuple(deps) for i in sorted(P): #print "V[%d] = %s" % (i, V[i]) # ... v = V[i] def test_python_1843(self): def apply_ad(e, ad_routine): if isinstance(e, Terminal): return e ops = [apply_ad(o, ad_routine) for o in e.operands()] e = e.reconstruct(*ops) if isinstance(e, Derivative): e = ad_routine(e) return e def test_uflcode_1901(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) # ... v = Argument(element) w = Coefficient(element) def test_python_1942(self): def walk(expression, pre_action, post_action): pre_action(expression) for o in expression.operands(): walk(o) post_action(expression) def test_python_1955(self): def post_traversal(root): for o in root.operands(): yield post_traversal(o) yield root def test_python_1963(self): def post_action(e): #print str(e) pass cell = triangle V = FiniteElement("Lagrange", cell, 1) u = TrialFunction(V) v = TestFunction(V) c = Constant(cell) f = Coefficient(V) expression = c*f**2*u*v # ... for e in post_traversal(expression): post_action(e) def test_python_1990(self): from ufl.classes import IntValue, Sum expression = as_ufl(3) def int_operation(x): return 7 # ... if isinstance(expression, IntValue): result = int_operation(expression) elif isinstance(expression, Sum): result = sum_operation(expression) # etc. # ... self.assertTrue(result == 7) def test_python_2024(self): class ExampleFunction(MultiFunction): def __init__(self): MultiFunction.__init__(self) def terminal(self, expression): return "Got a Terminal subtype %s." % type(expression) def operator(self, expression): return "Got an Operator subtype %s." % type(expression) def argument(self, expression): return "Got an Argument." def sum(self, expression): return "Got a Sum." m = ExampleFunction() cell = triangle element = FiniteElement("Lagrange", cell, 1) x = cell.x if 0: print m(Argument(element)) print m(x) print m(x[0] + x[1]) print m(x[0] * x[1]) def test_python_2066(self): def apply(e, multifunction): ops = [apply(o, multifunction) for o in e.operands()] return multifunction(e, *ops) def test_python_2087(self): class Replacer(Transformer): def __init__(self, mapping): Transformer.__init__(self) self.mapping = mapping def operator(self, e, *ops): return e.reconstruct(*ops) def terminal(self, e): return self.mapping.get(e, e) f = Constant(triangle) r = Replacer({f: f**2}) g = r.visit(2*f) def test_python_2189(self): V = FiniteElement("Lagrange", triangle, 1) u = TestFunction(V) v = TrialFunction(V) f = Coefficient(V) # Note no *dx! This is an expression, not a form. a = dot(grad(f*u), grad(v)) ac = expand_compounds(a) ad = expand_derivatives(ac) ai = expand_indices(ad) af = tree_format(a) acf = tree_format(ac) adf = "\n", tree_format(ad) aif = tree_format(ai) if 0: print "\na: ", str(a), "\n", tree_format(a) print "\nac:", str(ac), "\n", tree_format(ac) print "\nad:", str(ad), "\n", tree_format(ad) print "\nai:", str(ai), "\n", tree_format(ai) def test_python_2328(self): cell = triangle x = cell.x e = x[0] + x[1] #print e((0.5, 0.7)) # prints 1.2 # ... self.assertEqual( e((0.5, 0.7)), 1.2 ) def test_python_2338(self): cell = triangle x = cell.x # ... c = Constant(cell) e = c*(x[0] + x[1]) #print e((0.5, 0.7), { c: 10 }) # prints 12.0 # ... self.assertEqual( e((0.5, 0.7), { c: 10 }), 12.0 ) def test_python_2349(self): element = VectorElement("Lagrange", triangle, 1) c = Constant(triangle) f = Coefficient(element) e = c*(f[0] + f[1]) def fh(x): return (x[0], x[1]) #print e((0.5, 0.7), { c: 10, f: fh }) # prints 12.0 # ... self.assertEqual( e((0.5, 0.7), { c: 10, f: fh }), 12.0 ) def test_python_2364(self): element = FiniteElement("Lagrange", triangle, 1) g = Coefficient(element) e = g**2 + g.dx(0)**2 + g.dx(1)**2 def gh(x, der=()): if der == (): return x[0]*x[1] if der == (0,): return x[1] if der == (1,): return x[0] #print e((2, 3), { g: gh }) # prints 49 # ... self.assertEqual( e((2, 3), { g: gh }), 49 ) def test_python_2462(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) V = element u = TrialFunction(V) v = TestFunction(V) f = Coefficient(V) c = variable(Coefficient(V)) pde = c*u*v*dx - c*f*v*dx a, L = system(pde) u = Coefficient(element) myform = a # ... #print repr(preprocess(myform).preprocessed_form) # ... r = repr(preprocess(myform).preprocessed_form) # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_classcoverage.py000077500000000000000000000400071226300046600175150ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-09-06 -- 2009-02-10" from ufltestcase import UflTestCase, main import ufl from ufl import * from ufl.constantvalue import as_ufl from ufl.classes import * from ufl.algorithms import * has_repr = set() has_dict = set() def test_object(a, shape, free_indices): # Check if instances of this type has certain memory consuming members if hasattr(a, '_repr'): has_repr.add(a.__class__.__name__) if hasattr(a, '__dict__'): has_dict.add(a.__class__.__name__) # Test reproduction via repr string r = repr(a) e = eval(r, globals()) assert hash(a) == hash(e) # Can't really test str more than that it exists s = str(a) # Check that some properties are at least available fi = a.free_indices() sh = a.shape() ce = a.cell() # Compare with provided properties if free_indices is not None: assert len(set(fi) ^ set(free_indices)) == 0 if shape is not None: if sh != shape: print "sh:", sh print "shape:", shape assert sh == shape def test_object2(a): # Check if instances of this type has certain memory consuming members if hasattr(a, '_repr'): has_repr.add(a.__class__.__name__) if hasattr(a, '__dict__'): has_dict.add(a.__class__.__name__) # Test reproduction via repr string r = repr(a) e = eval(r, globals()) assert hash(a) == hash(e) # Can't really test str more than that it exists s = str(a) # Check that some properties are at least available ce = a.cell() def test_form(a): # Test reproduction via repr string r = repr(a) e = eval(r, globals()) assert hash(a) == hash(e) # Can't really test str more than that it exists s = str(a) class ClasscoverageTest(UflTestCase): def setUp(self): pass def testExports(self): "Verify that ufl.classes exports all Expr subclasses." all_expr_classes = [] for m in vars(ufl).values(): if isinstance(m, type(ufl)): for c in vars(m).values(): if isinstance(c, type) and issubclass(c, Expr): all_expr_classes.append(c) missing_classes = set(c.__name__ for c in all_expr_classes)\ - set(c.__name__ for c in all_ufl_classes) if missing_classes: print "The following subclasses of Expr were not exported from ufl.classes:" print "\n".join(sorted(missing_classes)) self.assertEqual(missing_classes, set()) def testAll(self): # --- Elements: cell = triangle dim = cell.d e0 = FiniteElement("CG", cell, 1) e1 = VectorElement("CG", cell, 1) e2 = TensorElement("CG", cell, 1) e3 = MixedElement(e0, e1, e2) e13D = VectorElement("CG", tetrahedron, 1) # --- Terminals: v13D = Argument(e13D) f13D = Coefficient(e13D) v0 = Argument(e0) v1 = Argument(e1) v2 = Argument(e2) v3 = Argument(e3) test_object(v0, (), ()) test_object(v1, (dim,), ()) test_object(v2, (dim,dim), ()) test_object(v3, (dim*dim+dim+1,), ()) f0 = Coefficient(e0) f1 = Coefficient(e1) f2 = Coefficient(e2) f3 = Coefficient(e3) test_object(f0, (), ()) test_object(f1, (dim,), ()) test_object(f2, (dim,dim), ()) test_object(f3, (dim*dim+dim+1,), ()) c = Constant(cell) test_object(c, (), ()) c = VectorConstant(cell) test_object(c, (dim,), ()) c = TensorConstant(cell) test_object(c, (dim,dim), ()) a = FloatValue(1.23) test_object(a, (), ()) a = IntValue(123) test_object(a, (), ()) I = Identity(1) test_object(I, (1,1), ()) I = Identity(2) test_object(I, (2,2), ()) I = Identity(3) test_object(I, (3,3), ()) e = PermutationSymbol(2) test_object(e, (2,2), ()) e = PermutationSymbol(3) test_object(e, (3,3,3), ()) n = cell.n test_object(n, (dim,), ()) a = variable(v0) test_object(a, (), ()) a = variable(v1) test_object(a, (dim,), ()) a = variable(v2) test_object(a, (dim,dim), ()) a = variable(v3) test_object(a, (dim*dim+dim+1,), ()) a = variable(f0) test_object(a, (), ()) a = variable(f1) test_object(a, (dim,), ()) a = variable(f2) test_object(a, (dim,dim), ()) a = variable(f3) test_object(a, (dim*dim+dim+1,), ()) #a = MultiIndex() # --- Non-terminals: #a = Indexed() a = v2[i,j] test_object(a, (), (i,j)) a = v2[0,k] test_object(a, (), (k,)) a = v2[l,1] test_object(a, (), (l,)) a = f2[i,j] test_object(a, (), (i,j)) a = f2[0,k] test_object(a, (), (k,)) a = f2[l,1] test_object(a, (), (l,)) I = Identity(dim) a = inv(I) test_object(a, (dim,dim), ()) a = inv(v2) test_object(a, (dim,dim), ()) a = inv(f2) test_object(a, (dim,dim), ()) for v in (v0,v1,v2,v3): for f in (f0,f1,f2,f3): a = outer(v, f) test_object(a, None, None) for v,f in zip((v0,v1,v2,v3), (f0,f1,f2,f3)): a = inner(v, f) test_object(a, None, None) for v,f in zip((v1,v2,v3),(f1,f2,f3)): a = dot(v, f) sh = v.shape()[:-1] + f.shape()[1:] test_object(a, sh, None) a = cross(v13D, f13D) test_object(a, (3,), ()) #a = Sum() a = v0 + f0 + v0 test_object(a, (), ()) a = v1 + f1 + v1 test_object(a, (dim,), ()) a = v2 + f2 + v2 test_object(a, (dim,dim), ()) #a = Product() a = 3*v0*(2.0*v0)*f0*(v0*3.0) test_object(a, (), ()) #a = Division() a = v0 / 2.0 test_object(a, (), ()) a = v0 / f0 test_object(a, (), ()) a = v0 / (f0 + 7) test_object(a, (), ()) #a = Power() a = f0**3 test_object(a, (), ()) a = (f0*2)**1.23 test_object(a, (), ()) #a = ListTensor() a = as_vector([1.0, 2.0*f0, f0**2]) test_object(a, (3,), ()) a = as_matrix([[1.0, 2.0*f0, f0**2], [1.0, 2.0*f0, f0**2]]) test_object(a, (2,3), ()) a = as_tensor([ [[0.00, 0.01, 0.02], [0.10, 0.11, 0.12] ], [ [1.00, 1.01, 1.02], [1.10, 1.11, 1.12]] ]) test_object(a, (2,2,3), ()) #a = ComponentTensor() a = as_vector(v1[i]*f1[j], i) test_object(a, (dim,), (j,)) a = as_matrix(v1[i]*f1[j], (j,i)) test_object(a, (dim, dim), ()) a = as_tensor(v1[i]*f1[j], (i,j)) test_object(a, (dim, dim), ()) a = as_tensor(v2[i,j]*f2[j,k], (i,k)) test_object(a, (dim, dim), ()) a = dev(v2) test_object(a, (dim,dim), ()) a = dev(f2) test_object(a, (dim,dim), ()) a = dev(f2*f0+v2*3) test_object(a, (dim,dim), ()) a = sym(v2) test_object(a, (dim,dim), ()) a = sym(f2) test_object(a, (dim,dim), ()) a = sym(f2*f0+v2*3) test_object(a, (dim,dim), ()) a = skew(v2) test_object(a, (dim,dim), ()) a = skew(f2) test_object(a, (dim,dim), ()) a = skew(f2*f0+v2*3) test_object(a, (dim,dim), ()) a = v2.T test_object(a, (dim,dim), ()) a = f2.T test_object(a, (dim,dim), ()) a = transpose(f2*f0+v2*3) test_object(a, (dim,dim), ()) a = det(v2) test_object(a, (), ()) a = det(f2) test_object(a, (), ()) a = det(f2*f0+v2*3) test_object(a, (), ()) a = tr(v2) test_object(a, (), ()) a = tr(f2) test_object(a, (), ()) a = tr(f2*f0+v2*3) test_object(a, (), ()) a = cofac(v2) test_object(a, (dim,dim), ()) a = cofac(f2) test_object(a, (dim,dim), ()) a = cofac(f2*f0+v2*3) test_object(a, (dim,dim), ()) cond1 = le(f0, 1.0) cond2 = eq(3.0, f0) cond3 = ne(sin(f0), cos(f0)) cond4 = lt(sin(f0), cos(f0)) cond5 = ge(sin(f0), cos(f0)) cond6 = gt(sin(f0), cos(f0)) cond7 = And(cond1, cond2) cond8 = Or(cond1, cond2) cond9 = Not(cond8) a = conditional(cond1, 1, 2) b = conditional(cond2, f0**3, ln(f0)) test_object2(cond1) test_object2(cond2) test_object2(cond3) test_object2(cond4) test_object2(cond5) test_object2(cond6) test_object2(cond7) test_object2(cond8) test_object2(cond9) test_object(a, (), ()) test_object(b, (), ()) a = abs(f0) test_object(a, (), ()) a = sqrt(f0) test_object(a, (), ()) a = cos(f0) test_object(a, (), ()) a = sin(f0) test_object(a, (), ()) a = tan(f0) test_object(a, (), ()) a = cosh(f0) test_object(a, (), ()) a = sinh(f0) test_object(a, (), ()) a = tanh(f0) test_object(a, (), ()) a = exp(f0) test_object(a, (), ()) a = ln(f0) test_object(a, (), ()) a = asin(f0) test_object(a, (), ()) a = acos(f0) test_object(a, (), ()) a = atan(f0) test_object(a, (), ()) one = as_ufl(1) a = abs(one) test_object(a, (), ()) a = Sqrt(one) test_object(a, (), ()) a = Cos(one) test_object(a, (), ()) a = Sin(one) test_object(a, (), ()) a = Tan(one) test_object(a, (), ()) a = Cosh(one) test_object(a, (), ()) a = Sinh(one) test_object(a, (), ()) a = Tanh(one) test_object(a, (), ()) a = Acos(one) test_object(a, (), ()) a = Asin(one) test_object(a, (), ()) a = Atan(one) test_object(a, (), ()) a = Exp(one) test_object(a, (), ()) a = Ln(one) test_object(a, (), ()) # TODO: #a = SpatialDerivative() a = f0.dx(0) test_object(a, (), ()) a = f0.dx(i) test_object(a, (), (i,)) a = f0.dx(i,j,1) test_object(a, (), (i,j)) s0 = variable(f0) s1 = variable(f1) s2 = variable(f2) f = dot(s0*s1, s2) test_object(s0, (), ()) test_object(s1, (dim,), ()) test_object(s2, (dim,dim), ()) test_object(f, (dim,), ()) a = diff(f, s0) test_object(a, (dim,), ()) a = diff(f, s1) test_object(a, (dim,dim,), ()) a = diff(f, s2) test_object(a, (dim,dim,dim), ()) a = div(v1) test_object(a, (), ()) a = div(f1) test_object(a, (), ()) a = div(v2) test_object(a, (dim,), ()) a = div(f2) test_object(a, (dim,), ()) a = div(Outer(f1,f1)) test_object(a, (dim,), ()) a = grad(v0) test_object(a, (dim,), ()) a = grad(f0) test_object(a, (dim,), ()) a = grad(v1) test_object(a, (dim, dim), ()) a = grad(f1) test_object(a, (dim, dim), ()) a = grad(f0*v0) test_object(a, (dim,), ()) a = grad(f0*v1) test_object(a, (dim, dim), ()) a = nabla_div(v1) test_object(a, (), ()) a = nabla_div(f1) test_object(a, (), ()) a = nabla_div(v2) test_object(a, (dim,), ()) a = nabla_div(f2) test_object(a, (dim,), ()) a = nabla_div(Outer(f1,f1)) test_object(a, (dim,), ()) a = nabla_grad(v0) test_object(a, (dim,), ()) a = nabla_grad(f0) test_object(a, (dim,), ()) a = nabla_grad(v1) test_object(a, (dim, dim), ()) a = nabla_grad(f1) test_object(a, (dim, dim), ()) a = nabla_grad(f0*v0) test_object(a, (dim,), ()) a = nabla_grad(f0*v1) test_object(a, (dim, dim), ()) a = curl(v13D) test_object(a, (3,), ()) a = curl(f13D) test_object(a, (3,), ()) a = rot(v1) test_object(a, (), ()) a = rot(f1) test_object(a, (), ()) #a = PositiveRestricted(v0) #test_object(a, (), ()) a = v0('+') test_object(a, (), ()) a = v0('+')*f0 test_object(a, (), ()) #a = NegativeRestricted(v0) #test_object(a, (), ()) a = v0('-') test_object(a, (), ()) a = v0('-') + f0 test_object(a, (), ()) a = cell_avg(v0) test_object(a, (), ()) a = facet_avg(v0) test_object(a, (), ()) a = cell_avg(v1) test_object(a, (dim,), ()) a = facet_avg(v1) test_object(a, (dim,), ()) a = cell_avg(v1)[i] test_object(a, (), (i,)) a = facet_avg(v1)[i] test_object(a, (), (i,)) # --- Integrals: a = v0*dx test_form(a) a = v0*dx(0) test_form(a) a = v0*dx(1) test_form(a) a = v0*ds test_form(a) a = v0*ds(0) test_form(a) a = v0*ds(1) test_form(a) a = v0*dS test_form(a) a = v0*dS(0) test_form(a) a = v0*dS(1) test_form(a) a = v0*dot(v1,f1)*dx test_form(a) a = v0*dot(v1,f1)*dx(0) test_form(a) a = v0*dot(v1,f1)*dx(1) test_form(a) a = v0*dot(v1,f1)*ds test_form(a) a = v0*dot(v1,f1)*ds(0) test_form(a) a = v0*dot(v1,f1)*ds(1) test_form(a) a = v0*dot(v1,f1)*dS test_form(a) a = v0*dot(v1,f1)*dS(0) test_form(a) a = v0*dot(v1,f1)*dS(1) test_form(a) # --- Form transformations: a = f0*v0*dx + f0*v0*dot(f1,v1)*dx #b = lhs(a) # TODO #c = rhs(a) # TODO d = derivative(a, f1, v1) f = action(d) #e = action(b) # --- Check which classes have been created if Expr._class_usage_statistics: s = Expr._class_usage_statistics constructed = set(s.keys()) abstract = set((Expr, Terminal, Operator, FormArgument, ConstantBase, AlgebraOperator, Condition, BinaryCondition, MathFunction, BesselFunction, Restricted, ScalarValue, ConstantValue, IndexAnnotated, CompoundDerivative, Derivative, WrapperType, GeometricQuantity, CompoundTensorOperator, UtilityType)) unused = set(ufl.classes.all_ufl_classes) - constructed - abstract if unused: print print "The following classes were never instantiated in class coverage test:" print "\n".join(sorted(map(str,unused))) print # --- Check which classes had certain member variables if has_repr: print print "The following classes contain a _repr member:" print "\n".join(sorted(map(str,has_repr))) print if has_dict: print print "The following classes contain a __dict__ member:" print "\n".join(sorted(map(str,has_dict))) print # TODO: Add tests for bessel functions: # BesselI # BesselJ # BesselK # BesselY # Erf # TODO: Add tests for: # Label if __name__ == "__main__": main() ufl-1.3.0/test/test_conditionals.py000077500000000000000000000126131226300046600173640ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-08-20 -- 2012-11-30" from ufltestcase import UflTestCase, main from ufl import * #from ufl.algorithms import * from ufl.classes import * element = FiniteElement("Lagrange", triangle, 1) f = Coefficient(element) g = Coefficient(element) class ConditionalsTestCase(UflTestCase): def test_conditional_does_not_allow_bool_condition(self): # The reason for this test is that it protects from the case # conditional(a == b, t, f) in which a == b means comparing representations self.assertRaises(UFLException, lambda: conditional(True, 1, 0)) def test_eq_produces_ufl_expr(self): expr1 = eq(f, f) expr2 = eq(f, g) expr3 = eq(f, g) self.assertTrue(isinstance(expr1, EQ)) self.assertTrue(isinstance(expr2, EQ)) self.assertFalse(bool(expr1 == expr2)) self.assertTrue(bool(expr1 != expr2)) self.assertTrue(bool(expr2 == expr3)) def test_eq_oper_produces_bool(self): expr1 = f == f expr2 = f == g self.assertIsInstance(expr1, bool) self.assertIsInstance(expr2, bool) self.assertTrue(expr1) self.assertFalse(expr2) def xtest_eq_produces_ufl_expr(self): expr1 = f == g expr2 = eq(f, g) self.assertTrue(isinstance(expr1, EQ)) self.assertTrue(isinstance(expr2, EQ)) self.assertTrue(bool(expr1 == expr2)) self.assertFalse(bool(expr1 != expr2)) def test_eq_produces_ufl_expr(self): expr1 = eq(f, g) expr2 = eq(f, f) expr3 = f == g expr4 = f == f # Correct types: self.assertTrue(isinstance(expr1, EQ)) self.assertTrue(isinstance(expr2, EQ)) self.assertTrue(isinstance(expr3, bool)) self.assertTrue(isinstance(expr4, bool)) # Comparing representations correctly: self.assertTrue(bool(expr1 == eq(f,g))) self.assertTrue(bool(expr1 != eq(g,g))) self.assertTrue(bool(expr2 == eq(f,f))) self.assertTrue(bool(expr2 != eq(g,f))) # Bool evaluation yields actual bools: self.assertTrue(isinstance(bool(expr1), bool)) self.assertTrue(isinstance(bool(expr2), bool)) self.assertFalse(expr3) self.assertTrue(expr4) # Allow use in boolean python expression context: # NB! This means comparing representations! Required by dict and set. self.assertFalse(bool(expr1)) self.assertTrue(bool(expr2)) self.assertFalse(bool(expr3)) self.assertTrue(bool(expr4)) def test_ne_produces_ufl_expr(self): expr1 = ne(f, g) expr2 = ne(f, f) expr3 = f != g expr4 = f != f # Correct types: self.assertTrue(isinstance(expr1, NE)) self.assertTrue(isinstance(expr2, NE)) self.assertTrue(isinstance(expr3, bool)) self.assertTrue(isinstance(expr4, bool)) # Comparing representations correctly: self.assertTrue(bool(expr1 == ne(f, g))) self.assertTrue(bool(expr1 != ne(g, g))) self.assertTrue(bool(expr2 == ne(f, f))) self.assertTrue(bool(expr2 != ne(g, f))) self.assertFalse(bool(expr2 == expr3)) # Bool evaluation yields actual bools: self.assertTrue(isinstance(bool(expr1), bool)) self.assertTrue(isinstance(bool(expr2), bool)) # Allow use in boolean python expression context: # NB! This means the opposite of ==, i.e. comparing representations! self.assertTrue(bool(expr1)) self.assertFalse(bool(expr2)) self.assertTrue(bool(expr1)) self.assertFalse(bool(expr2)) def test_lt_produces_ufl_expr(self): expr1 = lt(f, g) expr2 = f < g # Correct types (no bools here!): self.assertTrue(isinstance(expr1, LT)) self.assertTrue(isinstance(expr2, LT)) # Representations are the same: self.assertTrue(bool(expr1 == expr2)) # Protection from misuse in boolean python expression context: self.assertRaises(UFLException, lambda: bool(expr1)) def test_gt_produces_ufl_expr(self): expr1 = gt(f, g) expr2 = f > g # Correct types (no bools here!): self.assertTrue(isinstance(expr1, GT)) self.assertTrue(isinstance(expr2, GT)) # Representations are the same: self.assertTrue(bool(expr1 == expr2)) # Protection from misuse in boolean python expression context: self.assertRaises(UFLException, lambda: bool(expr1)) def test_le_produces_ufl_expr(self): expr1 = le(f, g) expr2 = f <= g # Correct types (no bools here!): self.assertTrue(isinstance(expr1, LE)) self.assertTrue(isinstance(expr2, LE)) # Representations are the same: self.assertTrue(bool(expr1 == expr2)) # Protection from misuse in boolean python expression context: self.assertRaises(UFLException, lambda: bool(expr1)) def test_ge_produces_ufl_expr(self): expr1 = ge(f, g) expr2 = f >= g # Correct types (no bools here!): self.assertTrue(isinstance(expr1, GE)) self.assertTrue(isinstance(expr2, GE)) # Representations are the same: self.assertTrue(bool(expr1 == expr2)) # Protection from misuse in boolean python expression context: self.assertRaises(UFLException, lambda: bool(expr1)) if __name__ == "__main__": main() ufl-1.3.0/test/test_degree_estimation.py000066400000000000000000000110471226300046600203620ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-03-12 -- 2009-01-28" from ufltestcase import UflTestCase, main from pprint import * from ufl import * from ufl.algorithms import * class TestDegreeEstimation(UflTestCase): def test_total_degree_estimation(self): V1 = FiniteElement("CG", triangle, 1) V2 = FiniteElement("CG", triangle, 2) VV = VectorElement("CG", triangle, 3) VM = V1 * V2 v1 = Argument(V1) v2 = Argument(V2) f1, f2 = Coefficients(VM) vv = Argument(VV) vu = Argument(VV) x, y = triangle.x self.assertEqual(estimate_total_polynomial_degree(x), 1) self.assertEqual(estimate_total_polynomial_degree(x*y), 2) self.assertEqual(estimate_total_polynomial_degree(x**3), 3) self.assertEqual(estimate_total_polynomial_degree(x**3), 3) self.assertEqual(estimate_total_polynomial_degree((x-1)**4), 4) self.assertEqual(estimate_total_polynomial_degree(vv[0]), 3) self.assertEqual(estimate_total_polynomial_degree(v2*vv[0]), 5) self.assertEqual(estimate_total_polynomial_degree(vu[0]*vv[0]), 6) self.assertEqual(estimate_total_polynomial_degree(vu[i]*vv[i]), 6) self.assertEqual(estimate_total_polynomial_degree(v1), 1) self.assertEqual(estimate_total_polynomial_degree(v2), 2) # TODO: This should be 1, but 2 is expected behaviour now # because f1 is part of a mixed element with max degree 2. self.assertEqual(estimate_total_polynomial_degree(f1), 2) self.assertEqual(estimate_total_polynomial_degree(f2), 2) self.assertEqual(estimate_total_polynomial_degree(v2*v1), 3) # TODO: This should be 2, but 3 is expected behaviour now # because f1 is part of a mixed element with max degree 2. self.assertEqual(estimate_total_polynomial_degree(f1*v1), 3) self.assertEqual(estimate_total_polynomial_degree(f2*v1), 3) self.assertEqual(estimate_total_polynomial_degree(f2*v2*v1), 5) self.assertEqual(estimate_total_polynomial_degree(f2+3), 2) self.assertEqual(estimate_total_polynomial_degree(f2*3), 2) self.assertEqual(estimate_total_polynomial_degree(f2**3), 6) self.assertEqual(estimate_total_polynomial_degree(f2/3), 2) self.assertEqual(estimate_total_polynomial_degree(f2/v2), 4) self.assertEqual(estimate_total_polynomial_degree(f2/(x-1)), 3) self.assertEqual(estimate_total_polynomial_degree(v1.dx(0)), 0) self.assertEqual(estimate_total_polynomial_degree(f2.dx(0)), 1) self.assertEqual(estimate_total_polynomial_degree(f2*v2.dx(0)*v1.dx(0)), 2+1) self.assertEqual(estimate_total_polynomial_degree(f2), 2) self.assertEqual(estimate_total_polynomial_degree(f2**2), 4) self.assertEqual(estimate_total_polynomial_degree(f2**3), 6) self.assertEqual(estimate_total_polynomial_degree(f2**3*v1), 7) self.assertEqual(estimate_total_polynomial_degree(f2**3*v1 + f1*v1), 7) # Based on the arbitrary chosen math function heuristics... nx, ny = triangle.n self.assertEqual(estimate_total_polynomial_degree(sin(nx**2)), 0) self.assertEqual(estimate_total_polynomial_degree(sin(x**3)), 3+2) def test_some_compound_types(self): # NB! Although some compound types are supported here, # some derivatives and compounds must be preprocessed # prior to degree estimation. In generic code, this algorithm # should only be applied after preprocessing. etpd = estimate_total_polynomial_degree P2 = FiniteElement("CG", triangle, 2) V2 = VectorElement("CG", triangle, 2) u = Coefficient(P2) v = Coefficient(V2) self.assertEqual(etpd(u.dx(0)), 2-1) self.assertEqual(etpd(grad(u)), 2-1) self.assertEqual(etpd(nabla_grad(u)), 2-1) self.assertEqual(etpd(div(u)), 2-1) self.assertEqual(etpd(v.dx(0)), 2-1) self.assertEqual(etpd(grad(v)), 2-1) self.assertEqual(etpd(nabla_grad(v)), 2-1) self.assertEqual(etpd(div(v)), 2-1) self.assertEqual(etpd(nabla_div(v)), 2-1) self.assertEqual(etpd(dot(v, v)), 2+2) self.assertEqual(etpd(inner(v, v)), 2+2) self.assertEqual(etpd(dot(grad(u), grad(u))), 2-1 + 2-1) self.assertEqual(etpd(inner(grad(u), grad(u))), 2-1 + 2-1) self.assertEqual(etpd(dot(grad(v), grad(v))), 2-1 + 2-1) self.assertEqual(etpd(inner(grad(v), grad(v))), 2-1 + 2-1) if __name__ == "__main__": main() ufl-1.3.0/test/test_derivative.py000077500000000000000000000464221226300046600170450ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2009-02-17 -- 2009-02-17" from ufltestcase import UflTestCase, main import math from itertools import chain from ufl import * from ufl.constantvalue import as_ufl from ufl.algorithms import expand_indices, strip_variables, post_traversal, preprocess class DerivativeTestCase(UflTestCase): def setUp(self): super(DerivativeTestCase, self).setUp() self.cell = triangle self.element = FiniteElement("CG", self.cell, 1) self.v = TestFunction(self.element) self.u = TrialFunction(self.element) self.w = Coefficient(self.element) self.xv = (0.3, 0.7) self.uv = 7.0 self.vv = 13.0 self.wv = 11.0 def _test(self, f, df): x = self.xv u, v, w = self.u, self.v, self.w mapping = { v: self.vv, u: self.uv, w: self.wv } dfv1 = derivative(f(w), w, v) dfv2 = df(w, v) dfv1 = dfv1(x, mapping) dfv2 = dfv2(x, mapping) self.assertEqual(dfv1, dfv2) dfv1 = derivative(f(7*w), w, v) dfv2 = 7*df(7*w, v) dfv1 = dfv1(x, mapping) dfv2 = dfv2(x, mapping) self.assertEqual(dfv1, dfv2) # --- Literals def testScalarLiteral(self): def f(w): return as_ufl(1) def df(w, v): return zero() self._test(f, df) def testIdentityLiteral(self): def f(w): return Identity(2)[i,i] def df(w, v): return zero() self._test(f, df) # --- Form arguments def testCoefficient(self): def f(w): return w def df(w, v): return v self._test(f, df) def testArgument(self): def f(w): return self.v def df(w, v): return zero() self._test(f, df) # --- Geometry def testSpatialCoordinate(self): def f(w): return triangle.x[0] def df(w, v): return zero() self._test(f, df) def testFacetNormal(self): def f(w): return triangle.n[0] def df(w, v): return zero() self._test(f, df) def testCellSurfaceArea(self): def f(w): return triangle.surface_area def df(w, v): return zero() self._test(f, df) def testFacetArea(self): def f(w): return triangle.facet_area def df(w, v): return zero() self._test(f, df) def testCircumradius(self): def f(w): return triangle.circumradius def df(w, v): return zero() self._test(f, df) def testCellVolume(self): def f(w): return triangle.volume def df(w, v): return zero() self._test(f, df) # --- Basic operators def testSum(self): def f(w): return w + 1 def df(w, v): return v self._test(f, df) def testProduct(self): def f(w): return 3*w def df(w, v): return 3*v self._test(f, df) def testPower(self): def f(w): return w**3 def df(w, v): return 3*w**2*v self._test(f, df) def testDivision(self): def f(w): return w / 3.0 def df(w, v): return v / 3.0 self._test(f, df) def testDivision2(self): def f(w): return 3.0 / w def df(w, v): return -3.0 * v / w**2 self._test(f, df) def testExp(self): def f(w): return exp(w) def df(w, v): return v*exp(w) self._test(f, df) def testLn(self): def f(w): return ln(w) def df(w, v): return v / w self._test(f, df) def testCos(self): def f(w): return cos(w) def df(w, v): return -v*sin(w) self._test(f, df) def testSin(self): def f(w): return sin(w) def df(w, v): return v*cos(w) self._test(f, df) def testTan(self): def f(w): return tan(w) def df(w, v): return v*2.0/(cos(2.0*w) + 1.0) self._test(f, df) def testAcos(self): def f(w): return acos(w/1000) def df(w, v): return -(v/1000)/sqrt(1.0 - (w/1000)**2) self._test(f, df) def testAsin(self): def f(w): return asin(w/1000) def df(w, v): return (v/1000)/sqrt(1.0 - (w/1000)**2) self._test(f, df) def testAtan(self): def f(w): return atan(w) def df(w, v): return v/(1.0 + w**2) self._test(f, df) # FIXME: Add the new erf and bessel_* # --- Abs and conditionals def testAbs(self): def f(w): return abs(w) def df(w, v): return sign(w)*v self._test(f, df) def testConditional(self): def cond(w): return lt(1.0, 2.0) def f(w): return conditional(cond(w), 2*w, 3*w) def df(w, v): return 2*v self._test(f, df) def cond(w): return lt(2.0, 1.0) def f(w): return conditional(cond(w), 2*w, 3*w) def df(w, v): return 3*v self._test(f, df) def testConditional(self): # This will fail without bugfix in derivative def cond(w): return lt(w, 1.0) def f(w): return conditional(cond(w), 2*w, 3*w) def df(w, v): return (conditional(cond(w), 1, 0) * 2*v + conditional(cond(w), 0, 1) * 3*v) self._test(f, df) # --- Tensor algebra basics def testIndexSum(self): def f(w): # 3*w + 4*w**2 + 5*w**3 a = as_vector((w, w**2, w**3)) b = as_vector((3, 4, 5)) i, = indices(1) return a[i]*b[i] def df(w, v): return 3*v + 4*2*w*v + 5*3*w**2*v self._test(f, df) def testListTensor(self): v = variable(as_ufl(42)) f = as_tensor(( ( (0, 0), (0, 0) ), ( (v, 2*v), (0, 0) ), ( (v**2, 1), (2, v/2) ), )) self.assertEqual(f.shape(), (3,2,2)) g = as_tensor(( ( (0, 0), (0, 0) ), ( (1, 2), (0,0) ), ( (84, 0), (0, 0.5) ), )) self.assertEqual(g.shape(), (3,2,2)) dfv = diff(f, v) x = None for i in range(3): for j in range(2): for k in range(2): self.assertEqual(dfv[i,j,k](x), g[i,j,k](x)) # --- Coefficient and argument input configurations def assertEqualExpr(self, a, b): a2 = (a*dx).compute_form_data().preprocessed_form b2 = (b*dx).compute_form_data().preprocessed_form if not a2 == b2: print print str(a2) print print str(b2) print self.assertEqual(a2, b2) def assertEqualBySampling(self, actual, expected): ad = (actual*dx).compute_form_data() a = ad.preprocessed_form.integrals(Measure.CELL)[0].integrand() bd = (expected*dx).compute_form_data() b = bd.preprocessed_form.integrals(Measure.CELL)[0].integrand() self.assertEqual([ad.function_replace_map[ac] for ac in ad.original_coefficients], [bd.function_replace_map[bc] for bc in bd.original_coefficients]) n = ad.num_coefficients def make_value(c): z = 0.3 if isinstance(c, Coefficient) else 0.7 if c.shape() == (): return z * (0.1 + 0.9 * c.count() / n) elif len(c.shape()) == 1: return tuple((z * (j + 0.1 + 0.9 * c.count() / n) for j in range(c.shape()[0]))) else: raise NotImplementedError("Tensor valued expressions not supported here.") amapping = dict((c, make_value(c)) for c in chain(ad.original_coefficients, ad.original_arguments)) bmapping = dict((c, make_value(c)) for c in chain(bd.original_coefficients, bd.original_arguments)) acell = actual.cell() bcell = expected.cell() self.assertEqual(acell, bcell) if acell.d == 1: x = (0.3,) elif acell.d == 2: x = (0.3, 0.4) elif acell.d == 3: x = (0.3, 0.4, 0.5) av = a(x, amapping) bv = b(x, bmapping) if not av == bv: print "Tried to sample expressions to compare but failed:" print print str(a) print av print print str(b) print bv print self.assertEqual(av, bv) def test_single_scalar_coefficient_derivative(self): cell = triangle V = FiniteElement("CG", cell, 1) u = Coefficient(V) v = TestFunction(V) a = 3*u**2 b = derivative(a, u, v) self.assertEqualExpr(b, 3*(u*(2*v))) def test_single_vector_coefficient_derivative(self): cell = triangle V = VectorElement("CG", cell, 1) u = Coefficient(V) v = TestFunction(V) a = 3*dot(u,u) actual = derivative(a, u, v) expected = 3*(2*(u[i]*v[i])) self.assertEqualBySampling(actual, expected) def test_multiple_coefficient_derivative(self): cell = triangle V = FiniteElement("CG", cell, 1) W = VectorElement("CG", cell, 1) M = V*W uv = Coefficient(V) uw = Coefficient(W) v = TestFunction(M) vv, vw = split(v) a = sin(uv)*dot(uw,uw) actual = derivative(a, (uv,uw), split(v)) expected = cos(uv)*vv * (uw[i]*uw[i]) + (uw[j]*vw[j])*2 * sin(uv) self.assertEqualBySampling(actual, expected) actual = derivative(a, (uv,uw), v) expected = cos(uv)*vv * (uw[i]*uw[i]) + (uw[j]*vw[j])*2 * sin(uv) self.assertEqualBySampling(actual, expected) def test_indexed_coefficient_derivative(self): cell = triangle I = Identity(cell.d) V = FiniteElement("CG", cell, 1) W = VectorElement("CG", cell, 1) u = Coefficient(W) v = TestFunction(V) w = dot(u, nabla_grad(u)) #a = dot(w, w) a = (u[i]*u[k].dx(i)) * w[k] actual = derivative(a, u[0], v) dw = v*u[k].dx(0) + u[i]*I[0,k]*v.dx(i) expected = 2 * w[k] * dw self.assertEqualBySampling(actual, expected) def test_multiple_indexed_coefficient_derivative(self): cell = tetrahedron I = Identity(cell.d) V = FiniteElement("CG", cell, 1) V2 = V*V W = VectorElement("CG", cell, 1) u = Coefficient(W) w = Coefficient(W) v = TestFunction(V2) vu, vw = split(v) actual = derivative(cos(u[i]*w[i]), (u[2], w[1]), (vu, vw)) expected = -sin(u[i]*w[i])*(vu*w[2] + u[1]*vw) self.assertEqualBySampling(actual, expected) def test_segregated_derivative_of_convection(self): cell = tetrahedron V = FiniteElement("CG", cell, 1) W = VectorElement("CG", cell, 1) u = Coefficient(W) v = Coefficient(W) du = TrialFunction(V) dv = TestFunction(V) L = dot(dot(u, nabla_grad(u)), v) Lv = {} Lvu = {} for i in range(cell.d): Lv[i] = derivative(L, v[i], dv) for j in range(cell.d): Lvu[i,j] = derivative(Lv[i], u[j], du) for i in range(cell.d): for j in range(cell.d): form = Lvu[i,j]*dx fd = form.compute_form_data() pf = fd.preprocessed_form a = expand_indices(pf) #print (i,j), str(a) k = Index() for i in range(cell.d): for j in range(cell.d): actual = Lvu[i,j] expected = du*u[i].dx(j)*dv + u[k]*du.dx(k)*dv self.assertEqualBySampling(actual, expected) # --- User provided derivatives of coefficients def test_coefficient_derivatives(self): V = FiniteElement("Lagrange", triangle, 1) dv = TestFunction(V).reconstruct(count=0) du = TrialFunction(V).reconstruct(count=1) f = Coefficient(V).reconstruct(count=0) g = Coefficient(V).reconstruct(count=1) df = Coefficient(V).reconstruct(count=2) dg = Coefficient(V).reconstruct(count=3) u = Coefficient(V).reconstruct(count=4) cd = { f: df, g: dg } integrand = inner(f, g) expected = (df*dv)*g + f*(dg*dv) F = integrand*dx J = derivative(F, u, du, cd) fd = J.compute_form_data() actual = fd.preprocessed_form.integrals()[0].integrand() self.assertEqual((actual*dx).deprecated_signature(), (expected*dx).deprecated_signature()) self.assertEqual(replace(actual, fd.function_replace_map), expected) def test_vector_coefficient_derivatives(self): V = VectorElement("Lagrange", triangle, 1) VV = TensorElement("Lagrange", triangle, 1) dv = TestFunction(V).reconstruct(count=0) du = TrialFunction(V).reconstruct(count=1) df = Coefficient(VV).reconstruct(count=0) g = Coefficient(V).reconstruct(count=1) f = Coefficient(V).reconstruct(count=2) u = Coefficient(V).reconstruct(count=3) cd = { f: df } integrand = inner(f, g) i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)] expected = as_tensor(df[i2,i1]*dv[i1], (i2,))[i0]*g[i0] F = integrand*dx J = derivative(F, u, du, cd) fd = J.compute_form_data() actual = fd.preprocessed_form.integrals()[0].integrand() self.assertEqual((actual*dx).deprecated_signature(), (expected*dx).deprecated_signature()) #self.assertEqual(replace(actual, fd.function_replace_map), expected) def test_vector_coefficient_derivatives_of_product(self): V = VectorElement("Lagrange", triangle, 1) VV = TensorElement("Lagrange", triangle, 1) dv = TestFunction(V).reconstruct(count=0) du = TrialFunction(V).reconstruct(count=1) df = Coefficient(VV).reconstruct(count=0) g = Coefficient(V).reconstruct(count=1) dg = Coefficient(VV).reconstruct(count=2) f = Coefficient(V).reconstruct(count=3) u = Coefficient(V).reconstruct(count=4) cd = { f: df, g: dg } integrand = f[i]*g[i] i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)] expected = as_tensor(df[i2,i1]*dv[i1], (i2,))[i0]*g[i0] +\ f[i0]*as_tensor(dg[i4,i3]*dv[i3], (i4,))[i0] F = integrand*dx J = derivative(F, u, du, cd) fd = J.compute_form_data() actual = fd.preprocessed_form.integrals()[0].integrand() # Keeping this snippet here for a while for debugging purposes if 0: print '\n', 'str:' print str(actual) print str(expected) print '\n', 'repr:' print repr(actual) print repr(expected) from ufl.algorithms import tree_format open('actual.txt','w').write(tree_format(actual)) open('expected.txt', 'w').write(tree_format(expected)) print '\n', 'equal:' print str(actual) == str(expected) print repr(actual) == repr(expected) print actual == expected # Tricky case! These are equal in representation except # that the outermost sum/indexsum are swapped. # Sampling the expressions instead of comparing representations. x = (0, 0) funcs = {dv: (13, 14), f: (1,2), g: (3,4), df: ((5,6),(7,8)), dg: ((9,10),(11,12))} self.assertEqual(replace(actual,fd.function_replace_map)(x, funcs), expected(x, funcs)) # TODO: Add tests covering more cases, in particular mixed stuff # --- Some actual forms def testHyperElasticity(self): cell = interval element = FiniteElement("CG", cell, 2) w = Coefficient(element) v = TestFunction(element) u = TrialFunction(element) b = Constant(cell) K = Constant(cell) dw = w.dx(0) dv = v.dx(0) du = v.dx(0) E = dw + dw**2 / 2 E = variable(E) Q = b*E**2 psi = K*(exp(Q)-1) f = psi*dx F = derivative(f, w, v) J = derivative(F, w, u) form_data_f = f.compute_form_data() form_data_F = F.compute_form_data() form_data_J = J.compute_form_data() f = form_data_f.preprocessed_form F = form_data_F.preprocessed_form J = form_data_J.preprocessed_form f_expression = strip_variables(f.integrals(Measure.CELL)[0].integrand()) F_expression = strip_variables(F.integrals(Measure.CELL)[0].integrand()) J_expression = strip_variables(J.integrals(Measure.CELL)[0].integrand()) #classes = set(c.__class__ for c in post_traversal(f_expression)) Kv = .2 bv = .3 dw = .5 dv = .7 du = .11 E = dw + dw**2 / 2. Q = bv*E**2 expQ = float(exp(Q)) psi = Kv*(expQ-1) fv = psi Fv = 2*Kv*bv*E*(1+dw)*expQ*dv Jv = 2*Kv*bv*expQ*dv*du*(E + (1+dw)**2*(2*bv*E**2 + 1)) def Nv(x, derivatives): assert derivatives == (0,) return dv def Nu(x, derivatives): assert derivatives == (0,) return du def Nw(x, derivatives): assert derivatives == (0,) return dw w, b, K = form_data_f.original_coefficients mapping = { K: Kv, b: bv, w: Nw } fv2 = f_expression((0,), mapping) self.assertAlmostEqual(fv, fv2) w, b, K = form_data_F.original_coefficients v, = form_data_F.original_arguments mapping = { K: Kv, b: bv, v: Nv, w: Nw } Fv2 = F_expression((0,), mapping) self.assertAlmostEqual(Fv, Fv2) w, b, K = form_data_J.original_coefficients v, u = form_data_J.original_arguments mapping = { K: Kv, b: bv, v: Nv, u: Nu, w: Nw } Jv2 = J_expression((0,), mapping) self.assertAlmostEqual(Jv, Jv2) def test_mass_derived_from_functional(self): cell = triangle V = FiniteElement("CG", cell, 1) v = TestFunction(V) u = TrialFunction(V) w = Coefficient(V) f = (w**2/2)*dx L = w*v*dx a = u*v*dx F = derivative(f, w, v) J1 = derivative(L, w, u) J2 = derivative(F, w, u) # TODO: assert something # --- Interaction with replace def test_derivative_replace_works_together(self): cell = triangle V = FiniteElement("CG", cell, 1) v = TestFunction(V) u = TrialFunction(V) f = Coefficient(V) g = Coefficient(V) M = cos(f)*sin(g) F = derivative(M, f, v) J = derivative(F, f, u) JR = replace(J, { f: g }) F2 = -sin(f)*v*sin(g) J2 = -cos(f)*u*v*sin(g) JR2 = -cos(g)*u*v*sin(g) self.assertEqualBySampling(F, F2) self.assertEqualBySampling(J, J2) self.assertEqualBySampling(JR, JR2) # --- Scratch space def test_foobar(self): element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) du = TrialFunction(element) U = Coefficient(element) def planarGrad(u): return as_matrix([[u[0].dx(0), 0 ,u[0].dx(1)], [ 0 , 0 , 0 ], [u[1].dx(0), 0 ,u[1].dx(1)]]) def epsilon(u): return 0.5*(planarGrad(u)+planarGrad(u).T) def NS_a(u,v): return inner(epsilon(u),epsilon(v)) L = NS_a(U,v)*dx a = derivative(L, U, du) # TODO: assert something if __name__ == "__main__": main() ufl-1.3.0/test/test_diff.py000077500000000000000000000066631226300046600156160ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2009-02-17 -- 2009-02-17" from ufltestcase import UflTestCase, main import math from ufl import * from ufl.constantvalue import as_ufl from ufl.algorithms import expand_derivatives class DiffTestCase(UflTestCase): def setUp(self): super(DiffTestCase, self).setUp() self.xv = None self.vv = 5.0 self.v = variable(self.vv) def _test(self, f, df): x, v = self.xv, self.v dfv1 = diff(f(v), v) dfv2 = df(v) dfv1 = dfv1(x) dfv2 = dfv2(x) self.assertAlmostEqual(dfv1, dfv2) dfv1 = diff(f(7*v), v) dfv2 = 7*df(7*v) dfv1 = dfv1(x) dfv2 = dfv2(x) self.assertAlmostEqual(dfv1, dfv2) def testVariable(self): def f(v): return v def df(v): return as_ufl(1) self._test(f, df) def testSum(self): def f(v): return v + 1 def df(v): return as_ufl(1) self._test(f, df) def testProduct(self): def f(v): return 3*v def df(v): return as_ufl(3) self._test(f, df) def testPower(self): def f(v): return v**3 def df(v): return 3*v**2 self._test(f, df) def testDivision(self): def f(v): return v / 3.0 def df(v): return as_ufl(1.0/3.0) self._test(f, df) def testDivision2(self): def f(v): return 3.0 / v def df(v): return -3.0 / v**2 self._test(f, df) def testExp(self): def f(v): return exp(v) def df(v): return exp(v) self._test(f, df) def testLn(self): def f(v): return ln(v) def df(v): return 1.0/v self._test(f, df) def testSin(self): def f(v): return sin(v) def df(v): return cos(v) self._test(f, df) def testCos(self): def f(v): return cos(v) def df(v): return -sin(v) self._test(f, df) def testTan(self): def f(v): return tan(v) def df(v): return 2.0/(cos(2.0*v) + 1.0) self._test(f, df) # TODO: Check the following tests. They run into strange math domain errors. # def testAsin(self): # def f(v): return asin(v) # def df(v): return 1/sqrt(1.0 - v**2) # self._test(f, df) # def testAcos(self): # def f(v): return acos(v) # def df(v): return -1/sqrt(1.0 - v**2) # self._test(f, df) def testAtan(self): def f(v): return atan(v) def df(v): return 1/(1.0 + v**2) self._test(f, df) def testIndexSum(self): def f(v): # 3*v + 4*v**2 + 5*v**3 a = as_vector((v, v**2, v**3)) b = as_vector((3, 4, 5)) i, = indices(1) return a[i]*b[i] def df(v): return 3 + 4*2*v + 5*3*v**2 self._test(f, df) def testDiffX(self): cell = triangle x = cell.x f = x[0]**2 * x[1]**2 i, = indices(1) df1 = diff(f, x) df2 = as_vector(f.dx(i), i) xv = (2, 3) df10 = df1[0](xv) df11 = df1[1](xv) df20 = df2[0](xv) df21 = df2[1](xv) self.assertAlmostEqual(df10, df20) self.assertAlmostEqual(df11, df21) self.assertAlmostEqual(df10, 2*2*9) self.assertAlmostEqual(df11, 2*4*3) # TODO: More tests involving wrapper types and indices if __name__ == "__main__": main() ufl-1.3.0/test/test_domains.py000077500000000000000000000350361226300046600163340ustar00rootroot00000000000000#!/usr/bin/env python """ Tests of domain language and attaching domains to forms. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * from ufl.domains import as_domain #from ufl.classes import ... #from ufl.algorithms import ... all_cells = (cell1D, cell2D, cell3D, interval, triangle, tetrahedron, quadrilateral, hexahedron) class RegionConstructionTestCase(UflTestCase): def test_construct_domains_from_cells(self): for cell in all_cells: D1 = Domain(cell) D2 = as_domain(cell) self.assertFalse(D1 is D2) if 0: print for D in (D1, D2): print 'id', id(D) print 'str', str(D) print 'repr', repr(D) print self.assertEqual(D1, D2) def test_as_domain_from_cell_is_unique(self): for cell in all_cells: D1 = as_domain(cell) D2 = as_domain(cell) self.assertTrue(D1 is D2) def test_construct_domains_with_names(self): for cell in all_cells: D2 = Domain(cell, name="D2") D3 = Domain(cell, name="D3") self.assertNotEqual(D2, D3) def test_domains_sort_by_name(self): # This ordering is rather arbitrary, but at least this shows sorting is working domains1 = [Domain(cell, "D%s"%cell.cellname()) for cell in all_cells] domains2 = [Domain(cell, "D%s"%cell.cellname()) for cell in sorted(all_cells)] sdomains = sorted(domains1) self.assertNotEqual(sdomains, domains1) self.assertEqual(sdomains, domains2) def test_topdomain_creation(self): D = Domain(interval) self.assertEqual(D.geometric_dimension(), 1) D = Domain(triangle) self.assertEqual(D.geometric_dimension(), 2) D = Domain(tetrahedron) self.assertEqual(D.geometric_dimension(), 3) def xtest_numbered_subdomains_are_registered(self): # THIS IS DISABLED BECAUSE REGION REGISTERING IS UNSAFE D = Domain(triangle) D1 = D[1] D2 = D[2] self.assertEqual(D.regions(), [D1, D2]) self.assertEqual(D.region_names(), ['triangle_multiverse_1', 'triangle_multiverse_2']) def xtest_named_subdomain_groups_are_registered(self): # THIS IS DISABLED BECAUSE REGION REGISTERING IS UNSAFE D = Domain(triangle) DL = Region(D, (1, 2), 'DL') DR = Region(D, (2, 3), 'DR') self.assertEqual(D.regions(), [DL, DR]) self.assertEqual(D.region_names(), ['DL', 'DR']) class MeasuresOverRegionsTestCase(UflTestCase): def setUp(self): UflTestCase.setUp(self) self.cell = tetrahedron self.D = Domain(self.cell) self.DL = Region(self.D, (1, 2), 'DL') self.DR = Region(self.D, (2, 3), 'DR') def test_construct_spaces_over_regions(self): VL = FiniteElement("CG", self.DL, 1) VR = FiniteElement("CG", self.DR, 1) self.assertNotEqual(VL, VR) self.assertEqual(VL.reconstruct(domain=self.DR), VR) self.assertEqual(VR.reconstruct(domain=self.DL), VL) #self.assertEqual(VL.region(), self.DL) #self.assertEqual(VR.region(), self.DR) def test_construct_measures_over_regions(self): VL = FiniteElement("CG", self.DL, 1) VR = FiniteElement("CG", self.DR, 1) self.assertNotEqual(Coefficient(VL, count=3), Coefficient(VR, count=3)) self.assertEqual(Coefficient(VL, count=3), Coefficient(VL, count=3)) fl = Coefficient(VL) fr = Coefficient(VR) # Three ways to construct an equivalent form M_dxr1 = fr*dx(self.DR) M_dxr2 = fr*dx('DR') M_dx23 = fr*dx((2,3)) self.assertEqual(M_dxr1.compute_form_data().integral_data, M_dx23.compute_form_data().integral_data) self.assertEqual(M_dxr1, M_dxr2) #for M in (M_dxr1, M_dxr2, M_dx23): # self.assertEqual(M.integrals(Measure.CELL)[0].measure(), dx(self.DR)) # Construct a slightly more complex form, including overlapping subdomains M1 = fl*dx(self.DL) + fr*dx(self.DR) # TODO: Test handling of legal measures M2 = fl*dx("DL") + fr*dx("DR") # TODO: Test regions by name M3 = fl*dx((1,2)) + fr*dx((2,3)) # TODO: Test subdomains by number M4 = fl*dx(1) + fl*dx(2) + fr*dx(2) + fr*dx(3) # TODO: Test subdomains by number def test_detection_of_coefficients_integrated_outside_support(self): pass #M = fl*dx(DR) + fr*dx(DL) # TODO: Test handling of illegal measures def test_extract_domains_from_form(self): cell = triangle # FIXME def test_(self): cell = triangle # FIXME class FormDomainModelTestCase(UflTestCase): def test_everywhere_integrals_with_backwards_compatibility(self): D = Domain(triangle) V = FiniteElement("CG", D, 1) f = Coefficient(V) a = f*dx ida, = a.compute_form_data().integral_data # Check some integral data self.assertEqual(ida.domain_type, "cell") self.assertEqual(ida.domain_id, Measure.DOMAIN_ID_OTHERWISE) self.assertEqual(ida.metadata, {}) # Integrands are not equal because of renumbering itg1 = ida.integrals[0].integrand() itg2 = a.integrals()[0].integrand() self.assertEqual(type(itg1), type(itg2)) self.assertEqual(itg1.element(), itg2.element()) def test_mixed_elements_on_overlapping_regions(self): # Create domain and both disjoint and overlapping regions D = Domain(tetrahedron, 'D') DD = Region(D, (0,4), "DD") DL = Region(D, (1,2), "DL") DR = Region(D, (2,3), "DR") # Create function spaces on D V = FiniteElement("CG", D, 1) VD = FiniteElement("DG", DD, 1) VC = FiniteElement("R", DD, 0) VL = VectorElement("DG", DL, 2) VR = FiniteElement("CG", DR, 3) # Create mixed space over all these spaces M = MixedElement(V, VD, VC, VL, VR) # Check that we can get the degree for each value component of the mixed space self.assertEqual(M.degree(0), 1) self.assertEqual(M.degree(1), 1) self.assertEqual(M.degree(2), 0) self.assertEqual(M.degree(3), 2) # Vector element self.assertEqual(M.degree(4), 2) self.assertEqual(M.degree(5), 2) self.assertEqual(M.degree(6), 3) self.assertEqual(M.degree(), 3) # Check that we can get the domain for each value component of the mixed space self.assertEqual(M.domain(0), D) self.assertEqual(M.domain(1), DD) self.assertEqual(M.domain(2), DD) self.assertEqual(M.domain(3), DL) # Vector element self.assertEqual(M.domain(4), DL) self.assertEqual(M.domain(5), DL) self.assertEqual(M.domain(6), DR) #self.assertEqual(M.domain(), None) # FIXME: What? # Create a mixed function and fetch components with names for more readable test code below m = Coefficient(M) md = m[1] mc = m[2] ml = as_vector((m[3], m[4], m[5])) mr = m[6] # These should all work out fine with function and integration domains perfectly matching a = m[0]**2*dx(D) ad = (md**2 + mc**2)*dx(DD) al = ml**2*dx(DL) ar = mr**2*dx(DR) # TODO: Check properties of forms, maybe by computing and inspecting form data. # These should all work out fine with because integration domains are contained in the function domains ad = m[0]**2*dx(DD) al = m[0]**2*dx(DL) ar = m[0]**2*dx("DR") a0 = m[0]**2*dx(0) a12 = m[0]**2*dx((1,2)) a3 = m[0]**2*dx(3) # TODO: Check properties of forms, maybe by computing and inspecting form data. # These should fail because the functions are undefined on the integration domains #self.assertRaises(UFLException, lambda: mr**2*dx(DD)) # FIXME: Make this fail #self.assertRaises(UFLException, lambda: mr**2*dx(DD)) # FIXME: Make this fail def test_form_domain_model(self): # Create domains with different celltypes # TODO: Figure out PyDOLFIN integration with Domain and Region. # TODO: Allow Domain class to carry domain marker data? Instead of attaching to measure with dx[markers]. #DA = Domain(tetrahedron, 'DA', markers="arbitrary data") DA = Domain(tetrahedron, 'DA') DB = Domain(hexahedron, 'DB') # Check python protocol behaviour self.assertNotEqual(DA, DB) self.assertEqual(set((DA,DA)), set((DA,))) self.assertEqual(set((DB,DB)), set((DB,))) self.assertEqual(set((DA,DB)), set((DB,DA))) self.assertEqual(sorted((DA,DB,DA,DB)), sorted((DB,DA,DA,DB))) # Check basic properties self.assertEqual(DA.name(), 'DA') self.assertEqual(DA.geometric_dimension(), 3) self.assertEqual(DA.topological_dimension(), 3) self.assertEqual(DA.cell(), tetrahedron) # Check region/domain getters self.assertEqual(DA.top_domain(), DA) self.assertEqual(DA.subdomain_ids(), None) #self.assertEqual(DA.region_names(), []) #self.assertEqual(DA.regions(), []) #BDA = Boundary(DA) # TODO #IDAB = Intersection(DA,DB) # TODO #ODAB = Overlap(DA,DB) # TODO # Create overlapping regions of each domain DAL = Region(DA, (1,2), "DAL") DAR = Region(DA, (2,3), "DAR") DBL = Region(DB, (0,1), "DBL") DBR = Region(DB, (1,4), "DBR") # Check that regions are available through domains #self.assertEqual(DA.region_names(), ['DAL', 'DAR']) #self.assertEqual(DA.regions(), [DAL, DAR]) #self.assertEqual(DA["DAR"], DAR) #self.assertEqual(DA["DAL"], DAL) # Create function spaces on DA VA = FiniteElement("CG", DA, 1) VAL = FiniteElement("CG", DAL, 1) VAR = FiniteElement("CG", DAR, 1) # Create function spaces on DB VB = FiniteElement("CG", DB, 1) VBL = FiniteElement("CG", DBL, 1) VBR = FiniteElement("CG", DBR, 1) # Check that regions are available through elements self.assertEqual(VA.domain(), DA) self.assertEqual(VAL.domain(), DAL) self.assertEqual(VAR.domain(), DAR) # Create functions in each space on DA fa = Coefficient(VA) fal = Coefficient(VAL) far = Coefficient(VAR) # Create functions in each space on DB fb = Coefficient(VB) fbl = Coefficient(VBL) fbr = Coefficient(VBR) # Checks of coefficient domains is covered well in the mixed element test # Create measure objects directly based on domain and region objects dxa = dx(DA) dxal = dx(DAL) dxar = dx('DAR') # Get Region by name # Create measure proxy objects from strings and ints, requiring # domains and regions to be part of their integrands dxb = dx('DB') # Get Domain by name dxbl = dx(Region(DB, (1,4), 'DBL2')) # Provide a region with different name but same subdomain ids as DBL dxbr = dx((1,4)) # Assume unique Domain and provide subdomain ids explicitly # Not checking measure objects in detail, as long as # they carry information to construct integrals below # they are good to go. # Create integrals on each region with matching spaces and measures Ma = fa*dxa Mar = far*dxar Mal = fal*dxal Mb = fb*dxb Mbr = fbr*dxbr Mbl = fbl*dxbl # TODO: Check forms, by getting and inspecting domains somehow. # TODO: How do we express the distinction between "everywhere" and "nowhere"? subdomain_ids=None vs []? # Create forms from integrals over overlapping regions on same top domain Marl = Mar + Mal Mbrl = Mbr + Mbl # Create forms from integrals over top domain and regions Mac = Ma + Marl Mbc = Mb + Mbrl # Create forms from separate top domains Mab = Ma + Mb # Create forms from separate top domains with overlapping regions Mabrl = Mac + Mbc #self.assertFalse(True) # Getting here, but it's not bloody likely that everything above is actually working. Add assertions! class UnusedCases: def xtest_subdomain_stuff(self): # Old sketch, not working D = Domain(triangle) D1 = D[1] D2 = D[2] D3 = D[3] DL = Region(D, (D1, D2), 'DL') DR = Region(D, (D2, D3), 'DR') DM = Overlap(DL, DR) self.assertEqual(DM, D2) VL = VectorElement(DL, "CG", 1) VR = FiniteElement(DR, "CG", 2) V = VL*VR def sub_elements_on_subdomains(W): # Get from W: (already there) subelements = (VL, VR) # Get from W: subdomains = (D1, D2, D3) # Build in W: dom2elm = { D1: (VL,), D2: (VL,VR), D3: (VR,), } # Build in W: elm2dom = { VL: (D1,D2), VR: (D2,D3) } # ElementSwitch represents joining of elements restricted to disjunct subdomains. # An element restricted to a domain union becomes a switch # of elements restricted to each disjoint subdomain VL_D1 = VectorElement(D1, "CG", 1) VL_D2 = VectorElement(D2, "CG", 1) VLalt = ElementSwitch({D1: VL_D1, D2: VL_D2}) # Ditto VR_D2 = FiniteElement(D2, "CG", 2) VR_D3 = FiniteElement(D3, "CG", 2) VRalt = ElementSwitch({D2: VR_D2, D3: VR_D3}) # A mixed element of ElementSwitches is mixed only on the overlapping domains: Valt1 = VLalt*VRalt Valt2 = ElementSwitch({D1: VL_D1, D2: VL_D2*VR_D2, D3: VR_D3}) ul, ur = TrialFunctions(V) vl, vr = TestFunctions(V) # Implemented by user: al = dot(ul,vl)*dx(DL) ar = ur*vr*dx(DR) # Disjunctified by UFL: alonly = dot(ul,vl)*dx(D1) # integral_1 knows that only subelement VL is active am = (dot(ul,vl) + ur*vr)*dx(D2) # integral_2 knows that both subelements are active aronly = ur*vr*dx(D3) # integral_3 knows that only subelement VR is active # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_elements.py000077500000000000000000000165531226300046600165210ustar00rootroot00000000000000#!/usr/bin/env python # Last changed: 2013-04-04 from ufltestcase import UflTestCase, main from ufl import * from ufl.geometry import cellname2dim all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron) # TODO: cover all valid element definitions class ElementsTestCase(UflTestCase): def test_scalar_galerkin(self): for cell in all_cells: for p in range(1,10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG"): element = FiniteElement(family, cell, p) self.assertEqual(element.value_shape(), ()) self.assertEqual(element, eval(repr(element))) def test_vector_galerkin(self): for cell in all_cells: dim = cell.d #shape = () if dim == 1 else (dim,) shape = (dim,) for p in range(1,10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG"): element = VectorElement(family, cell, p) self.assertEqual(element.value_shape(), shape) self.assertEqual(element, eval(repr(element))) for i in range(dim): c = element.extract_component(i) self.assertEqual(c[0], ()) def test_tensor_galerkin(self): for cell in all_cells: dim = cell.d #shape = () if dim == 1 else (dim,dim) shape = (dim,dim) for p in range(1,10): for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG"): element = TensorElement(family, cell, p) self.assertEqual(element.value_shape(), shape) self.assertEqual(element, eval(repr(element))) for i in range(dim): for j in range(dim): c = element.extract_component((i,j)) self.assertEqual(c[0], ()) def test_tensor_symmetry(self): for cell in all_cells: dim = cell.d for p in range(1,10): for s in (None, True, {(0,1): (1,0)}): # Symmetry dict is invalid for interval cell if isinstance(s,dict) and cell == interval: continue for family in ("Lagrange", "CG", "Discontinuous Lagrange", "DG"): if isinstance(s, dict): element = TensorElement(family, cell, p, shape=(dim,dim), symmetry=s) else: element = TensorElement(family, cell, p, symmetry=s) self.assertEqual(element.value_shape(), (dim,dim)) self.assertEqual(element, eval(repr(element))) for i in range(dim): for j in range(dim): c = element.extract_component((i,j)) self.assertEqual(c[0], ()) def test_mixed_tensor_symmetries(self): from ufl.algorithms import expand_indices, expand_compounds S = FiniteElement('CG', triangle, 1) V = VectorElement('CG', triangle, 1) T = TensorElement('CG', triangle, 1, symmetry=True) # M has dimension 4+1, symmetries are 2->1 M = T*S P = Coefficient(M) M = inner(P, P)*dx M2 = expand_indices(expand_compounds(M)) self.assertTrue('[1]' in str(M2)) self.assertTrue('[2]' not in str(M2)) # M has dimension 2+(1+4), symmetries are 5->4 M = V*(S*T) P = Coefficient(M) M = inner(P, P)*dx M2 = expand_indices(expand_compounds(M)) self.assertTrue('[4]' in str(M2)) self.assertTrue('[5]' not in str(M2)) def test_bdm(self): for cell in (triangle, tetrahedron): dim = cell.d element = FiniteElement("BDM", cell, 1) self.assertEqual(element.value_shape(), (dim,)) self.assertEqual(element, eval(repr(element))) def test_vector_bdm(self): for cell in (triangle, tetrahedron): dim = cell.d element = VectorElement("BDM", cell, 1) self.assertEqual(element.value_shape(), (dim,dim)) self.assertEqual(element, eval(repr(element))) def test_mixed(self): for cell in (triangle, tetrahedron): dim = cell.d velement = VectorElement("CG", cell, 2) pelement = FiniteElement("CG", cell, 1) TH1 = MixedElement(velement, pelement) TH2 = velement * pelement self.assertEqual(TH1.value_shape(), (dim+1,)) self.assertEqual(TH2.value_shape(), (dim+1,)) self.assertEqual(repr(TH1), repr(TH2)) self.assertEqual(TH1, eval(repr(TH2))) self.assertEqual(TH2, eval(repr(TH1))) def test_nested_mixed(self): for cell in (triangle, tetrahedron): dim = cell.d velement = VectorElement("CG", cell, 2) pelement = FiniteElement("CG", cell, 1) TH1 = MixedElement((velement, pelement), pelement) TH2 = velement * pelement * pelement self.assertEqual(TH1.value_shape(), (dim+2,)) self.assertEqual(TH2.value_shape(), (dim+2,)) self.assertEqual(repr(TH1), repr(TH2)) self.assertEqual(TH1, eval(repr(TH2))) self.assertEqual(TH2, eval(repr(TH1))) def test_quadrature_scheme(self): for cell in (triangle, tetrahedron): for q in (None, 1, 2, 3): element = FiniteElement("CG", cell, 1, quad_scheme=q) self.assertEqual(element.quadrature_scheme(), q) self.assertEqual(element, eval(repr(element))) def test_missing_cell(self): # These special cases are here to allow missing # cell in PyDOLFIN Constant and Expression for cell in (triangle, None): element = FiniteElement("Real", cell, 0) self.assertEqual(element, eval(repr(element))) element = FiniteElement("Undefined", cell, None) self.assertEqual(element, eval(repr(element))) element = VectorElement("Lagrange", cell, 1, dim=2) self.assertEqual(element, eval(repr(element))) element = TensorElement("DG", cell, 1, shape=(2,2)) self.assertEqual(element, eval(repr(element))) def test_invalid_degree(self): cell = triangle for degree in (1, None): element = FiniteElement("CG", cell, degree) self.assertEqual(element, eval(repr(element))) element = VectorElement("CG", cell, degree) self.assertEqual(element, eval(repr(element))) def test_lobatto(self): cell = interval for degree in (1, 2, None): element = FiniteElement("Lob", cell, degree) self.assertEqual(element, eval(repr(element))) element = FiniteElement("Lobatto", cell, degree) self.assertEqual(element, eval(repr(element))) def test_radau(self): cell = interval for degree in (0, 1, 2, None): element = FiniteElement("Rad", cell, degree) self.assertEqual(element, eval(repr(element))) element = FiniteElement("Radau", cell, degree) self.assertEqual(element, eval(repr(element))) if __name__ == "__main__": main() ufl-1.3.0/test/test_equals.py000077500000000000000000000046331226300046600161730ustar00rootroot00000000000000#!/usr/bin/env python """ Test of expression comparison. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * class TestExprEquals(UflTestCase): def test_comparison_of_coefficients(self): V = FiniteElement("CG", triangle, 1) U = FiniteElement("CG", triangle, 2) Ub = FiniteElement("CG", triangle, 2) v1 = Coefficient(V, count=1) v1b = Coefficient(V, count=1) v2 = Coefficient(V, count=2) u1 = Coefficient(U, count=1) u2 = Coefficient(U, count=2) u2b = Coefficient(Ub, count=2) # Itentical objects self.assertTrue(v1 == v1) self.assertTrue(u2 == u2) # Equal but distinct objects self.assertTrue(v1 == v1b) self.assertTrue(u2 == u2b) # Different objects self.assertFalse(v1 == v2) self.assertFalse(u1 == u2) self.assertFalse(v1 == u1) self.assertFalse(v2 == u2) def test_comparison_of_products(self): V = FiniteElement("CG", triangle, 1) v = Coefficient(V) u = Coefficient(V) a = (v * 2) * u b = (2 * v) * u c = 2 * (v * u) self.assertTrue(a == b) self.assertFalse(a == c) self.assertFalse(b == c) def test_comparison_of_sums(self): V = FiniteElement("CG", triangle, 1) v = Coefficient(V) u = Coefficient(V) a = (v + 2) + u b = (2 + v) + u c = 2 + (v + u) self.assertTrue(a == b) self.assertFalse(a == c) self.assertFalse(b == c) def test_comparison_of_deeply_nested_expression(self): V = FiniteElement("CG", triangle, 1) v = Coefficient(V, count=1) u = Coefficient(V, count=1) w = Coefficient(V, count=2) def build_expr(a): for i in range(100): if i % 3 == 0: a = a + i elif i % 3 == 1: a = a * i elif i % 3 == 2: a = a**i return a a = build_expr(u) b = build_expr(v) c = build_expr(w) self.assertTrue(a == b) self.assertFalse(a == c) self.assertFalse(b == c) # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_evaluate.py000077500000000000000000000204151226300046600165030ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2009-02-13 -- 2009-02-13" from ufltestcase import UflTestCase, main import math from ufl import * from ufl.constantvalue import as_ufl class EvaluateTestCase(UflTestCase): def testScalars(self): s = as_ufl(123) e = s((5,7)) v = 123 self.assertEqual(e, v) def testZero(self): s = as_ufl(0) e = s((5,7)) v = 0 self.assertEqual(e, v) def testIdentity(self): cell = triangle I = Identity(cell.d) s = 123*I[0,0] e = s((5,7)) v = 123 self.assertEqual(e, v) s = 123*I[1,0] e = s((5,7)) v = 0 self.assertEqual(e, v) def testCoords(self): cell = triangle x = cell.x s = x[0] + x[1] e = s((5,7)) v = 5 + 7 self.assertEqual(e, v) def testFunction1(self): cell = triangle element = FiniteElement("CG", cell, 1) f = Coefficient(element) s = 3*f e = s((5,7), { f: 123 }) v = 3*123 self.assertEqual(e, v) def testFunction2(self): cell = triangle element = FiniteElement("CG", cell, 1) f = Coefficient(element) def g(x): return x[0] s = 3*f e = s((5,7), { f: g }) v = 3*5 self.assertEqual(e, v) def testArgument2(self): cell = triangle element = FiniteElement("CG", cell, 1) f = Argument(element) def g(x): return x[0] s = 3*f e = s((5,7), { f: g }) v = 3*5 self.assertEqual(e, v) def testAlgebra(self): cell = triangle x = cell.x s = 3*(x[0] + x[1]) - 7 + x[0]**(x[1]/2) e = s((5,7)) v = 3*(5. + 7.) - 7 + 5.**(7./2) self.assertEqual(e, v) def testIndexSum(self): cell = triangle x = cell.x i, = indices(1) s = x[i]*x[i] e = s((5,7)) v = 5**2 + 7**2 self.assertEqual(e, v) def testIndexSum2(self): cell = triangle x = cell.x I = Identity(cell.d) i, j = indices(2) s = (x[i]*x[j])*I[i,j] e = s((5,7)) #v = sum_i sum_j x_i x_j delta_ij = x_0 x_0 + x_1 x_1 v = 5**2 + 7**2 self.assertEqual(e, v) def testMathFunctions(self): x = triangle.x[0] s = sin(x) e = s((5,7)) v = math.sin(5) self.assertEqual(e, v) s = cos(x) e = s((5,7)) v = math.cos(5) self.assertEqual(e, v) s = tan(x) e = s((5,7)) v = math.tan(5) self.assertEqual(e, v) s = ln(x) e = s((5,7)) v = math.log(5) self.assertEqual(e, v) s = exp(x) e = s((5,7)) v = math.exp(5) self.assertEqual(e, v) s = sqrt(x) e = s((5,7)) v = math.sqrt(5) self.assertEqual(e, v) def testListTensor(self): x, y = triangle.x[0], triangle.x[1] m = as_matrix([[x, y], [-y, -x]]) s = m[0,0] + m[1,0] + m[0,1] + m[1,1] e = s((5,7)) v = 0 self.assertEqual(e, v) s = m[0,0] * m[1,0] * m[0,1] * m[1,1] e = s((5,7)) v = 5**2*7**2 self.assertEqual(e, v) def testComponentTensor1(self): x = triangle.x m = as_vector(x[i], i) s = m[0] * m[1] e = s((5,7)) v = 5*7 self.assertEqual(e, v) def testComponentTensor2(self): x = triangle.x xx = outer(x,x) m = as_matrix(xx[i,j], (i,j)) s = m[0,0] + m[1,0] + m[0,1] + m[1,1] e = s((5,7)) v = 5*5 + 5*7 + 5*7 + 7*7 self.assertEqual(e, v) def testComponentTensor3(self): x = triangle.x xx = outer(x,x) m = as_matrix(xx[i,j], (i,j)) s = m[0,0] * m[1,0] * m[0,1] * m[1,1] e = s((5,7)) v = 5*5 * 5*7 * 5*7 * 7*7 self.assertEqual(e, v) def testCoefficient(self): V = FiniteElement("CG", triangle, 1) f = Coefficient(V) e = f**2 def eval_f(x): return x[0]*x[1]**2 self.assertEqual(e((3,7), {f: eval_f}), (3*7**2)**2) def testCoefficientDerivative(self): V = FiniteElement("CG", triangle, 1) f = Coefficient(V) e = f.dx(0)**2 + f.dx(1)**2 def eval_f(x, derivatives): if not derivatives: return eval_f.c*x[0]*x[1]**2 # assume only first order derivative d, = derivatives if d == 0: return eval_f.c*x[1]**2 if d == 1: return eval_f.c*x[0]*2*x[1] # shows how to attach data to eval_f eval_f.c = 5 self.assertEqual(e((3,7), {f: eval_f}), (5*7**2)**2 + (5*3*2*7)**2) def test_dot(self): x = cell2D.x s = dot(x,2*x) e = s((5,7)) v = 2*(5*5+7*7) self.assertEqual(e, v) def test_inner(self): x = cell2D.x xx = as_matrix(((2*x[0],3*x[0]), (2*x[1],3*x[1]))) s = inner(xx,2*xx) e = s((5,7)) v = 2*((5*2)**2 + (5*3)**2 + (7*2)**2 + (7*3)**2) self.assertEqual(e, v) def test_outer(self): x = cell2D.x xx = outer(outer(x, x), as_vector((2,3))) s = inner(xx,2*xx) e = s((5,7)) v = 2 * (5**2 + 7**2)**2 * (2**2 + 3**2) self.assertEqual(e, v) def test_cross(self): x = cell3D.x xv = (3, 5, 7) # Test cross product of triplets of orthogonal # vectors, where |a x b| = |a| |b| ts = [\ [as_vector((x[0],0,0)), as_vector((0,x[1],0)), as_vector((0,0,x[2]))], [as_vector((x[0],x[1],0)), as_vector((x[1],-x[0],0)), as_vector((0,0,x[2]))], [as_vector((0, x[0],x[1])), as_vector((0, x[1],-x[0])), as_vector((x[2], 0, 0))], [as_vector((x[0], 0, x[1])), as_vector((x[1], 0, -x[0])), as_vector((0, x[2], 0))], ] for t in ts: for i in range(3): for j in range(3): cij = cross(t[i],t[j]) dij = dot(cij,cij) eij = dij(xv) tni = dot(t[i],t[i])(xv) tnj = dot(t[j],t[j])(xv) vij = tni*tnj if i != j else 0 self.assertEqual(eij, vij) def xtest_dev(self): x = cell2D.x xv = (5, 7) xx = outer(x, x) s1 = dev(2*xx) s2 = 2*(xx - xx.T) # FIXME e = inner(s1,s1)(xv) v = inner(s2,s2)(xv) self.assertEqual(e, v) def test_skew(self): x = cell2D.x xv = (5, 7) xx = outer(x, x) s1 = skew(2*xx) s2 = (xx - xx.T) e = inner(s1,s1)(xv) v = inner(s2,s2)(xv) self.assertEqual(e, v) def test_sym(self): x = cell2D.x xv = (5, 7) xx = outer(x, x) s1 = sym(2*xx) s2 = (xx + xx.T) e = inner(s1,s1)(xv) v = inner(s2,s2)(xv) self.assertEqual(e, v) def test_tr(self): x = cell2D.x xv = (5, 7) xx = outer(x, x) s = tr(2*xx) e = s(xv) v = 2*sum(xv[i]**2 for i in (0,1)) self.assertEqual(e, v) def test_det2D(self): x = cell2D.x xv = (5, 7) a, b = 6.5, -4 xx = as_matrix(((x[0],x[1]),(a,b))) s = det(2*xx) e = s(xv) v = 2**2 * (5*b - 7*a) self.assertEqual(e, v) def xtest_det3D(self): # FIXME x = cell3D.x xv = (5, 7, 9) a, b, c = 6.5, -4, 3 d, e, f = 2, 3, 4 xx = as_matrix(((x[0],x[1], x[2]), (a,b,c), (d,e,f))) s = det(2*xx) e = s(xv) v = 2**3 * (xv[0]*(b*f - e*c) - xv[1]*(a*f - c*d) + xv[2]*(a*e - b*d)) self.assertEqual(e, v) def test_cofac(self): pass # TODO def test_inv(self): pass # TODO if __name__ == "__main__": main() ufl-1.3.0/test/test_expand_indices.py000077500000000000000000000241551226300046600176570ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2009-03-19 -- 2012-03-20" # Modified by Anders Logg, 2008 # Modified by Garth N. Wells, 2009 from ufltestcase import UflTestCase, main import math from pprint import * from ufl import * from ufl.algorithms import * from ufl.classes import Sum, Product # TODO: Test expand_indices2 throuroughly for correctness, then efficiency: #expand_indices, expand_indices2 = expand_indices2, expand_indices class ExpandIndicesTestCase(UflTestCase): def setUp(self): cell = triangle element = FiniteElement("Lagrange", cell, 1) velement = VectorElement("Lagrange", cell, 1) telement = TensorElement("Lagrange", cell, 1) self.sf = Coefficient(element) self.sf2 = Coefficient(element) self.vf = Coefficient(velement) self.tf = Coefficient(telement) # Note: the derivatives of these functions make no sense, but # their unique constant values are used for validation. def SF(x, derivatives=()): # first order derivatives if derivatives == (0,): return 0.30 elif derivatives == (1,): return 0.31 # second order derivatives elif derivatives == (0,0): return 0 elif derivatives in ((1,0), (0,1)): return 0 elif derivatives == (1,1): return 0 # function value assert derivatives == () return 3 def SF2(x, derivatives=()): # first order derivatives if derivatives == (0,): return 0.30 elif derivatives == (1,): return 0.31 # second order derivatives elif derivatives == (0,0): return 3.300 elif derivatives in ((1,0), (0,1)): return 3.310 elif derivatives == (1,1): return 3.311 # function value assert derivatives == () return 3 def VF(x, derivatives=()): # first order derivatives if derivatives == (0,): return (0.50, 0.70) elif derivatives == (1,): return (0.51, 0.71) # second order derivatives elif derivatives == (0,0): return (0.20, 0.21) elif derivatives in ((1,0), (0,1)): return (0.30, 0.31) elif derivatives == (1,1): return (0.40, 0.41) # function value assert derivatives == () return (5, 7) def TF(x, derivatives=()): # first order derivatives if derivatives == (0,): return ((1.10, 1.30), (1.70, 1.90)) elif derivatives == (1,): return ((1.11, 1.31), (1.71, 1.91)) # second order derivatives elif derivatives == (0,0): return ((10.00, 10.01), (10.10, 10.11)) elif derivatives in ((1,0), (0,1)): return ((12.00, 12.01), (12.10, 12.11)) elif derivatives == (1,1): return ((11.00, 11.01), (11.10, 11.11)) # function value assert derivatives == () return ((11, 13), (17, 19)) self.x = (1.23, 3.14) self.mapping = { self.sf: SF, self.sf2: SF2, self.vf: VF, self.tf: TF } def compare(self, f, value): debug = 0 if debug: print 'f', f g = expand_derivatives(f) if debug: print 'g', g gv = g(self.x, self.mapping) self.assertAlmostEqual(gv, value) g = expand_indices(g) if debug: print 'g', g gv = g(self.x, self.mapping) self.assertAlmostEqual(gv, value) g = renumber_indices(g) if debug: print 'g', g gv = g(self.x, self.mapping) self.assertAlmostEqual(gv, value) def test_basic_expand_indices(self): sf = self.sf vf = self.vf tf = self.tf compare = self.compare # Simple expressions with no indices or derivatives to expand compare(sf, 3) compare(sf + 1, 4) compare(sf - 2.5, 0.5) compare(sf/2, 1.5) compare(sf/0.5, 6) compare(sf**2, 9) compare(sf**0.5, 3**0.5) compare(sf**3, 27) compare(0.5**sf, 0.5**3) compare(sf * (sf/6), 1.5) compare(sin(sf), math.sin(3)) compare(cos(sf), math.cos(3)) compare(exp(sf), math.exp(3)) compare(ln(sf), math.log(3)) # Simple indexing compare(vf[0], 5) compare(vf[0] + 1, 6) compare(vf[0] - 2.5, 2.5) compare(vf[0]/2, 2.5) compare(vf[0]/0.5, 10) compare(vf[0]**2, 25) compare(vf[0]**0.5, 5**0.5) compare(vf[0]**3, 125) compare(0.5**vf[0], 0.5**5) compare(vf[0] * (vf[0]/6), 5*(5./6)) compare(sin(vf[0]), math.sin(5)) compare(cos(vf[0]), math.cos(5)) compare(exp(vf[0]), math.exp(5)) compare(ln(vf[0]), math.log(5)) # Double indexing compare(tf[1,1], 19) compare(tf[1,1] + 1, 20) compare(tf[1,1] - 2.5, 16.5) compare(tf[1,1]/2, 9.5) compare(tf[1,1]/0.5, 38) compare(tf[1,1]**2, 19**2) compare(tf[1,1]**0.5, 19**0.5) compare(tf[1,1]**3, 19**3) compare(0.5**tf[1,1], 0.5**19) compare(tf[1,1] * (tf[1,1]/6), 19*(19./6)) compare(sin(tf[1,1]), math.sin(19)) compare(cos(tf[1,1]), math.cos(19)) compare(exp(tf[1,1]), math.exp(19)) compare(ln(tf[1,1]), math.log(19)) def test_expand_indices_index_sum(self): sf = self.sf vf = self.vf tf = self.tf compare = self.compare # Basic index sums compare(vf[i]*vf[i], 5*5+7*7) compare(vf[j]*tf[i,j]*vf[i], 5*5*11 + 5*7*13 + 5*7*17 + 7*7*19) compare(vf[j]*tf.T[j,i]*vf[i], 5*5*11 + 5*7*13 + 5*7*17 + 7*7*19) compare(tf[i,i], 11 + 19) compare(tf[i,j]*(tf[j,i]+outer(vf, vf)[i,j]), (5*5+11)*11 + (7*5+17)*13 + (7*5+13)*17 + (7*7+19)*19) compare( as_tensor( as_tensor(tf[i,j], (i,j))[k,l], (l,k) )[i,i], 11 + 19 ) def test_expand_indices_derivatives(self): sf = self.sf vf = self.vf tf = self.tf compare = self.compare # Basic derivatives compare(sf.dx(0), 0.3) compare(sf.dx(1), 0.31) compare(sf.dx(i)*vf[i], 0.30*5 + 0.31*7) compare(vf[j].dx(i)*vf[i].dx(j), 0.50*0.50 + 0.51*0.70 + 0.70*0.51 + 0.71*0.71) def test_expand_indices_hyperelasticity(self): sf = self.sf vf = self.vf tf = self.tf compare = self.compare # Deformation gradient I = Identity(2) u = vf F = I + grad(u) # F = (1 + vf[0].dx(0), vf[0].dx(1), vf[1].dx(0), 1 + vf[1].dx(1)) # F = (1 + 0.50, 0.51, 0.70, 1 + 0.71) F00 = 1 + 0.50 F01 = 0.51 F10 = 0.70 F11 = 1 + 0.71 compare(F[0,0], F00) compare(F[0,1], F01) compare(F[1,0], F10) compare(F[1,1], F11) J = det(F) compare(J, (1 + 0.50)*(1 + 0.71) - 0.70*0.51) # Strain tensors C = F.T*F # Cij = sum_k Fki Fkj C00 = F00*F00 + F10*F10 C01 = F00*F01 + F10*F11 C10 = F01*F00 + F11*F10 C11 = F01*F01 + F11*F11 compare(C[0,0], C00) compare(C[0,1], C01) compare(C[1,0], C10) compare(C[1,1], C11) E = (C-I)/2 E00 = (C00-1)/2 E01 = (C01 )/2 E10 = (C10 )/2 E11 = (C11-1)/2 compare(E[0,0], E00) compare(E[0,1], E01) compare(E[1,0], E10) compare(E[1,1], E11) # Strain energy Q = inner(E, E) Qvalue = E00**2 + E01**2 + E10**2 + E11**2 compare(Q, Qvalue) K = 0.5 psi = (K/2)*exp(Q) compare(psi, 0.25*math.exp(Qvalue)) def test_expand_indices_div_grad(self): sf = self.sf sf2 = self.sf2 vf = self.vf tf = self.tf compare = self.compare a = div(grad(sf)) compare(a, 0) a = div(grad(sf2)) compare(a, 3.300 + 3.311) if 0: Dvf = grad(vf) Lvf = div(Dvf) Lvf2 = dot(Lvf,Lvf) pp = (Lvf2*dx).compute_form_data().preprocessed_form.integrals()[0].integrand() print 'vf', vf.shape(), str(vf) print 'Dvf', Dvf.shape(), str(Dvf) print 'Lvf', Lvf.shape(), str(Lvf) print 'Lvf2', Lvf2.shape(), str(Lvf2) print 'pp', pp.shape(), str(pp) a = div(grad(vf)) compare(dot(a,a), (0.20+0.40)**2 + (0.21+0.41)**2) a = div(grad(tf)) compare(inner(a,a), (10.00+11.00)**2 + (10.01+11.01)**2 + (10.10+11.10)**2 + (10.11+11.11)**2) def test_expand_indices_nabla_div_grad(self): sf = self.sf sf2 = self.sf2 vf = self.vf tf = self.tf compare = self.compare a = nabla_div(nabla_grad(sf)) compare(a, 0) a = nabla_div(nabla_grad(sf2)) compare(a, 3.300 + 3.311) a = nabla_div(nabla_grad(vf)) compare(dot(a,a), (0.20+0.40)**2 + (0.21+0.41)**2) a = nabla_div(nabla_grad(tf)) compare(inner(a,a), (10.00+11.00)**2 + (10.01+11.01)**2 + (10.10+11.10)**2 + (10.11+11.11)**2) def xtest_expand_indices_list_tensor_problem(self): print print '='*40 # TODO: This is the case marked in the expand_indices2 implementation #as not working. Fix and then try expand_indices2 on other tests! V = VectorElement("CG", triangle, 1) w = Coefficient(V) v = as_vector([w[0], 0]) a = v[i]*w[i] # TODO: Compare print type(a), str(a) A, comp = a.operands() print type(A), str(A) print type(comp), str(comp) ei1 = expand_indices(a) ei2 = expand_indices2(a) print str(ei1) print str(ei2) print '='*40 print if __name__ == "__main__": main() ufl-1.3.0/test/test_ffcforms.py000077500000000000000000000241161226300046600165040ustar00rootroot00000000000000#!/usr/bin/env python """Unit tests including all demo forms from FFC 0.5.0. The forms are modified (with comments) to work with the UFL notation which differs from the FFC notation in some places.""" __author__ = "Anders Logg (logg@simula.no) et al." __date__ = "2008-04-09 -- 2008-09-26" __copyright__ = "Copyright (C) 2008 Anders Logg et al." __license__ = "GNU GPL version 3 or any later version" # Examples copied from the FFC demo directory, examples contributed # by Johan Jansson, Kristian Oelgaard, Marie Rognes, and Garth Wells. from ufltestcase import UflTestCase, main from ufl import * class FFCTestCase(UflTestCase): def testConstant(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) c = Constant("triangle") d = VectorConstant("triangle") a = c*dot(grad(v), grad(u))*dx # FFC notation: L = dot(d, grad(v))*dx L = inner(d, grad(v))*dx def testElasticity(self): element = VectorElement("Lagrange", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) def eps(v): # FFC notation: return grad(v) + transp(grad(v)) return grad(v) + (grad(v)).T # FFC notation: a = 0.25*dot(eps(v), eps(u))*dx a = 0.25*inner(eps(v), eps(u))*dx def testEnergyNorm(self): element = FiniteElement("Lagrange", "tetrahedron", 1) v = Coefficient(element) a = (v*v + dot(grad(v), grad(v)))*dx def testEquation(self): element = FiniteElement("Lagrange", "triangle", 1) k = 0.1 v = TestFunction(element) u = TrialFunction(element) u0 = Coefficient(element) F = v*(u - u0)*dx + k*dot(grad(v), grad(0.5*(u0 + u)))*dx a = lhs(F) L = rhs(F) def testFunctionOperators(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) # FFC notation: a = sqrt(1/modulus(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx + v*u*sqrt(f*g)*g*dx a = sqrt(1/abs(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx + v*u*sqrt(f*g)*g*dx def testHeat(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u1 = TrialFunction(element) u0 = Coefficient(element) c = Coefficient(element) f = Coefficient(element) k = Constant("triangle") a = v*u1*dx + k*c*dot(grad(v), grad(u1))*dx L = v*u0*dx + k*v*f*dx def testMass(self): element = FiniteElement("Lagrange", "tetrahedron", 3) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx def testMixedMixedElement(self): P3 = FiniteElement("Lagrange", "triangle", 3) element = (P3 * P3) * (P3 * P3) def testMixedPoisson(self): q = 1 BDM = FiniteElement("Brezzi-Douglas-Marini", "triangle", q) DG = FiniteElement("Discontinuous Lagrange", "triangle", q - 1) mixed_element = BDM * DG (tau, w) = TestFunctions(mixed_element) (sigma, u) = TrialFunctions(mixed_element) f = Coefficient(DG) a = (dot(tau, sigma) - div(tau)*u + w*div(sigma))*dx L = w*f*dx def testNavierStokes(self): element = VectorElement("Lagrange", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) w = Coefficient(element) # FFC notation: a = v[i]*w[j]*D(u[i], j)*dx a = v[i]*w[j]*Dx(u[i], j)*dx def testNeumannProblem(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) # FFC notation: a = dot(grad(v), grad(u))*dx a = inner(grad(v), grad(u))*dx # FFC notation: L = dot(v, f)*dx + dot(v, g)*ds L = inner(v, f)*dx + inner(v, g)*ds def testOptimization(self): element = FiniteElement("Lagrange", "triangle", 3) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) a = dot(grad(v), grad(u))*dx L = v*f*dx def testP5tet(self): element = FiniteElement("Lagrange", tetrahedron, 5) def testP5tri(self): element = FiniteElement("Lagrange", triangle, 5) def testPoissonDG(self): element = FiniteElement("Discontinuous Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) n = triangle.n # FFC notation: h = MeshSize("triangle"), not supported by UFL h = Constant(triangle) gN = Coefficient(element) alpha = 4.0 gamma = 8.0 # FFC notation #a = dot(grad(v), grad(u))*dx \ # - dot(avg(grad(v)), jump(u, n))*dS \ # - dot(jump(v, n), avg(grad(u)))*dS \ # + alpha/h('+')*dot(jump(v, n), jump(u, n))*dS \ # - dot(grad(v), mult(u,n))*ds \ # - dot(mult(v,n), grad(u))*ds \ # + gamma/h*v*u*ds a = inner(grad(v), grad(u))*dx \ - inner(avg(grad(v)), jump(u, n))*dS \ - inner(jump(v, n), avg(grad(u)))*dS \ + alpha/h('+')*dot(jump(v, n), jump(u, n))*dS \ - inner(grad(v), u*n)*ds \ - inner(u*n, grad(u))*ds \ + gamma/h*v*u*ds L = v*f*dx + v*gN*ds def testPoisson(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) # Note: inner() also works a = dot(grad(v), grad(u))*dx L = v*f*dx def testPoissonSystem(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) # FFC notation: a = dot(grad(v), grad(u))*dx a = inner(grad(v), grad(u))*dx # FFC notation: L = dot(v, f)*dx L = inner(v, f)*dx def testProjection(self): # Projections are not supported by UFL and have been broken # in FFC for a while. For DOLFIN, the current (global) L^2 # projection can be extended to handle also local projections. P0 = FiniteElement("Discontinuous Lagrange", "triangle", 0) P1 = FiniteElement("Lagrange", "triangle", 1) P2 = FiniteElement("Lagrange", "triangle", 2) v = TestFunction(P1) f = Coefficient(P1) #pi0 = Projection(P0) #pi1 = Projection(P1) #pi2 = Projection(P2) # #a = v*(pi0(f) + pi1(f) + pi2(f))*dx def testQuadratureElement(self): element = FiniteElement("Lagrange", "triangle", 2) # FFC notation: #QE = QuadratureElement("triangle", 3) #sig = VectorQuadratureElement("triangle", 3) QE = FiniteElement("Quadrature", "triangle", 3) sig = VectorElement("Quadrature", "triangle", 3) v = TestFunction(element) u = TrialFunction(element) u0= Coefficient(element) C = Coefficient(QE) sig0 = Coefficient(sig) f = Coefficient(element) a = v.dx(i)*C*u.dx(i)*dx + v.dx(i)*2*u0*u*u0.dx(i)*dx L = v*f*dx - dot(grad(v), sig0)*dx def testStokes(self): # UFLException: Shape mismatch in sum. P2 = VectorElement("Lagrange", "triangle", 2) P1 = FiniteElement("Lagrange", "triangle", 1) TH = P2 * P1 (v, q) = TestFunctions(TH) (u, p) = TrialFunctions(TH) f = Coefficient(P2) # FFC notation: # a = (dot(grad(v), grad(u)) - div(v)*p + q*div(u))*dx a = (inner(grad(v), grad(u)) - div(v)*p + q*div(u))*dx L = dot(v, f)*dx def testSubDomain(self): element = FiniteElement("CG", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) M = f*dx(2) + f*ds(5) def testSubDomains(self): element = FiniteElement("CG", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx(0) + 10.0*v*u*dx(1) + v*u*ds(0) + 2.0*v*u*ds(1) + v('+')*u('+')*dS(0) + 4.3*v('+')*u('+')*dS(1) def testTensorWeightedPoisson(self): # FFC notation: #P1 = FiniteElement("Lagrange", "triangle", 1) #P0 = FiniteElement("Discontinuous Lagrange", "triangle", 0) # #v = TestFunction(P1) #u = TrialFunction(P1) #f = Coefficient(P1) # #c00 = Coefficient(P0) #c01 = Coefficient(P0) #c10 = Coefficient(P0) #c11 = Coefficient(P0) # #C = [[c00, c01], [c10, c11]] # #a = dot(grad(v), mult(C, grad(u)))*dx P1 = FiniteElement("Lagrange", "triangle", 1) P0 = TensorElement("Discontinuous Lagrange", "triangle", 0, shape=(2, 2)) v = TestFunction(P1) u = TrialFunction(P1) C = Coefficient(P0) a = inner(grad(v), C*grad(u))*dx def testVectorLaplaceGradCurl(self): def HodgeLaplaceGradCurl(element, felement): (tau, v) = TestFunctions(element) (sigma, u) = TrialFunctions(element) f = Coefficient(felement) # FFC notation: a = (dot(tau, sigma) - dot(grad(tau), u) + dot(v, grad(sigma)) + dot(curl(v), curl(u)))*dx a = (inner(tau, sigma) - inner(grad(tau), u) + inner(v, grad(sigma)) + inner(curl(v), curl(u)))*dx # FFC notation: L = dot(v, f)*dx L = inner(v, f)*dx return [a, L] shape = "tetrahedron" order = 1 GRAD = FiniteElement("Lagrange", shape, order) # FFC notation: CURL = FiniteElement("Nedelec", shape, order-1) CURL = FiniteElement("N1curl", shape, order) VectorLagrange = VectorElement("Lagrange", shape, order+1) [a, L] = HodgeLaplaceGradCurl(GRAD * CURL, VectorLagrange) if __name__ == "__main__": main() ufl-1.3.0/test/test_forms.py000077500000000000000000000321321226300046600160220ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2008-03-12 -- 2008-12-02" # Modified by Anders Logg, 2008 from ufltestcase import UflTestCase, main from ufl import * from ufl.algorithms import * class TestMeasure(UflTestCase): def test_manually_constructed_measures(self): # Since we can't write 'dx = dx[data]' in a non-global scope, # because of corner cases in the python scope rules, # it may be convenient to construct measures directly: domain_data = ('Stokes', 'Darcy') dx = Measure('dx')[domain_data] ds = Measure('ds')[domain_data] dS = Measure('dS')[domain_data] # Possible PyDOLFIN syntax: #ds = boundaries.dx(3) # return Measure('dx')[self](3) def test_functionals_with_compiler_data(self): x, y, z = tetrahedron.x a0 = x*dx(0) + y*dx(0) + z*dx(1) a1 = x*dx(0, {'k': 'v'}) + y*dx(0, {'k': 'v'}) + z*dx(1, {'k': 'v'}) a2 = x*dx(0, {'k': 'x'}) + y*dx(0, {'k': 'y'}) + z*dx(1, {'k': 'z'}) b0 = x*dx(0) + z*dx(1) + y*dx(0) b1 = x*dx(0, {'k': 'v'}) + z*dx(1, {'k': 'v'}) + y*dx(0, {'k': 'v'}) b2 = x*dx(0, {'k': 'x'}) + z*dx(1, {'k': 'z'}) + y*dx(0, {'k': 'y'}) c0 = y*dx(0) + z*dx(1) + x*dx(0) c1 = y*dx(0, {'k': 'v'}) + z*dx(1, {'k': 'v'}) + x*dx(0, {'k': 'v'}) c2 = y*dx(0, {'k': 'y'}) + z*dx(1, {'k': 'z'}) + x*dx(0, {'k': 'x'}) d0 = (x*dx(0, {'k': 'xk', 'q':'xq'}) + y*dx(1, {'k': 'yk', 'q':'yq'}) ) d1 = (y*dx(1, {'k': 'yk', 'q':'yq'}) + x*dx(0, {'k': 'xk', 'q':'xq'})) a0s = a0.compute_form_data().signature a1s = a1.compute_form_data().signature a2s = a2.compute_form_data().signature b0s = b0.compute_form_data().signature b1s = b1.compute_form_data().signature b2s = b2.compute_form_data().signature c0s = c0.compute_form_data().signature c1s = c1.compute_form_data().signature c2s = c2.compute_form_data().signature d0s = d0.compute_form_data().signature d1s = d1.compute_form_data().signature # Check stability w.r.t. ordering of terms without compiler data self.assertEqual(a0s, b0s) self.assertEqual(a0s, c0s) # Check stability w.r.t. ordering of terms with equal compiler data self.assertEqual(a1s, b1s) self.assertEqual(a1s, c1s) # Check stability w.r.t. ordering of terms with different compiler data self.assertEqual(a2s, b2s) self.assertEqual(a2s, c2s) # Check stability w.r.t. ordering of terms with two-value compiler data dict self.assertEqual(d0s, d1s) def test_forms_with_compiler_data(self): element = FiniteElement("Lagrange", triangle, 1) u = TrialFunction(element) v = TestFunction(element) # Three terms on the same subdomain using different representations a_0 = (u*v*dx(0, {"representation":"tensor"}) + inner(grad(u), grad(v))*dx(0, {"representation": "quadrature"}) + inner(grad(u), grad(v))*dx(0, {"representation": "auto"})) # Three terms on different subdomains using different representations and order a_1 = (inner(grad(u), grad(v))*dx(0, {"representation":"tensor", "quadrature_degree":8}) + inner(grad(u), grad(v))*dx(1, {"representation":"quadrature", "quadrature_degree":4}) + inner(grad(u), grad(v))*dx(1, {"representation":"auto", "quadrature_degree":"auto"})) # Sum of the above a = a_0 + a_1 # Same forms with no compiler data: b_0 = (u*v*dx(0) + inner(grad(u), grad(v))*dx(0) + inner(grad(u), grad(v))*dx(0)) b_1 = (inner(grad(u), grad(v))*dx(0) + inner(grad(u), grad(v))*dx(1) + inner(grad(u), grad(v))*dx(1)) b = b_0 + b_1 # Same forms with same compiler data but different ordering of terms c_0 = (inner(grad(u), grad(v))*dx(0, {"representation": "auto"}) + inner(grad(u), grad(v))*dx(0, {"representation": "quadrature"}) + u*v*dx(0, {"representation":"tensor"})) c_1 = (inner(grad(u), grad(v))*dx(0, {"representation":"tensor", "quadrature_degree":8}) + inner(grad(u), grad(v))*dx(1, {"representation":"auto", "quadrature_degree":"auto"}) + inner(grad(u), grad(v))*dx(1, {"representation":"quadrature", "quadrature_degree":4})) c = c_0 + c_1 afd = a.compute_form_data() cfd = c.compute_form_data() bfd = b.compute_form_data() self.assertNotEqual(afd.signature, bfd.signature) self.assertEqual(afd.signature, cfd.signature) def test_measures_with_domain_data(self): # Configure measure with some arbitrary data object as domain_data domain_data = ('Stokes', 'Darcy') dX = dx[domain_data] # Build form with this domain_data element = FiniteElement("Lagrange", triangle, 1) f = Coefficient(element) a = f*dX(0) + f**2*dX(1) # Check that we get an UFL error when using dX without domain id self.assertRaises(UFLException, lambda: f*dX) # Check that we get a Python error when using unsupported type self.assertRaises(TypeError, lambda: "foo"*dX(1)) # Check that we get the right domain_data from the preprocessed form data fd = a.compute_form_data() self.assertIs(fd.domain_data['cell'], domain_data) self.assertIsNone(fd.domain_data.get('exterior_facet')) # Check that integral_data list is consistent as well f2 = f.reconstruct(count=0) self.assertIs(fd.domain_data['cell'], domain_data) for itd in fd.integral_data: self.assertEqual(itd.domain_type, 'cell') self.assertEqual(itd.metadata, {}) if isinstance(itd.domain_id, int): self.assertEqual(replace(itd.integrals[0].integrand(), fd.function_replace_map), f2**(itd.domain_id+1)) else: self.assertEqual(itd.domain_id, Measure.DOMAIN_ID_OTHERWISE) def test_measure_sums(self): element = FiniteElement("Lagrange", triangle, 1) f = Coefficient(element) a1 = f**2*dx(0) + f**2*dx(3) a2 = f**2*(dx(0) + dx(3)) self.assertEqual(a1, a2) a3 = f**2*dx(3) + f**2*dx(0) a4 = f**2*(dx(3) + dx(0)) self.assertEqual(a3, a4) # Shouldn't we have sorting of integrals? #self.assertEqual(a1, a4) class TestIntegrals(UflTestCase): def test_separated_dx(self): "Tests automatic summation of integrands over same domain." element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) f = Coefficient(element) a = f*v*dx(0) + 2*v*ds + 3*v*dx(0) + 7*v*ds + 3*v*dx(2) + 7*v*dx(2) b = (f*v + 3*v)*dx(0) + (2*v + 7*v)*ds + (3*v + 7*v)*dx(2) # Check that integrals are represented canonically after preprocessing # (these forms have no indices with icky numbering issues) self.assertEqual(a.compute_form_data().preprocessed_form.integrals(), b.compute_form_data().preprocessed_form.integrals()) # And therefore the signatures should be the same self.assertEqual(a.deprecated_signature(), b.deprecated_signature()) def test_adding_zero(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) a = v*dx b = a + 0 self.assertEqual(id(a), id(b)) b = 0 + a self.assertEqual(id(a), id(b)) b = sum([a, 2*a]) self.assertEqual(b, a+2*a) class TestFormScaling(UflTestCase): def test_scalar_mult_form(self): D = Domain(triangle) R = FiniteElement("Real", D, 0) element = FiniteElement("Lagrange", D, 1) v = TestFunction(element) f = Coefficient(element) c = Coefficient(R) # These should be acceptable: #self.assertEqual(0*(c*dx), (0*c)*dx) # TODO: Need argument annotation of zero to make this work self.assertEqual(0*(c*dx(D)), (0*c)*dx(D)) self.assertEqual(3*(v*dx), (3*v)*dx) self.assertEqual(3.14*(v*dx), (3.14*v)*dx) self.assertEqual(c*(v*dx), (c*v)*dx) self.assertEqual((c**c+c/3)*(v*dx), ((c**c+c/3)*v)*dx) # These should not be acceptable: self.assertRaises(TypeError, lambda: f*(v*dx)) self.assertRaises(TypeError, lambda: (f/2)*(v*dx)) self.assertRaises(TypeError, lambda: (c*f)*(v*dx)) def test_action_mult_form(self): V = FiniteElement("CG", triangle, 1) u = TrialFunction(V) v = TrialFunction(V) f = Coefficient(V) a = u*v*dx self.assertEqual(a*f, action(a,f)) self.assertRaises(TypeError, lambda: a*"foo") class TestExampleForms(UflTestCase): def test_source1(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) f = Coefficient(element) a = f*v*dx def test_source2(self): element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) f = Coefficient(element) a = dot(f,v)*dx def test_source3(self): element = TensorElement("Lagrange", triangle, 1) v = TestFunction(element) f = Coefficient(element) a = inner(f,v)*dx def test_source4(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) x = triangle.x f = sin(x[0]) a = f*v*dx def test_mass1(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = u*v*dx def test_mass2(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = u*v*dx def test_mass3(self): element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = dot(u,v)*dx def test_mass4(self): element = TensorElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = inner(u,v)*dx def test_point1(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = u*v*dP(0) def test_stiffness1(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = dot(grad(u), grad(v)) * dx def test_stiffness2(self): element = FiniteElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = inner(grad(u), grad(v)) * dx def test_stiffness3(self): element = VectorElement("Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) a = inner(grad(u), grad(v)) * dx def test_nonnabla_stiffness_with_conductivity(self): velement = VectorElement("Lagrange", triangle, 1) telement = TensorElement("Lagrange", triangle, 1) v = TestFunction(velement) u = TrialFunction(velement) M = Coefficient(telement) a = inner(grad(u)*M.T, grad(v)) * dx def test_nabla_stiffness_with_conductivity(self): velement = VectorElement("Lagrange", triangle, 1) telement = TensorElement("Lagrange", triangle, 1) v = TestFunction(velement) u = TrialFunction(velement) M = Coefficient(telement) a = inner(M*nabla_grad(u), nabla_grad(v)) * dx def test_nonnabla_navier_stokes(self): cell = triangle velement = VectorElement("Lagrange", cell, 2) pelement = FiniteElement("Lagrange", cell, 1) TH = velement * pelement v, q = TestFunctions(TH) u, p = TrialFunctions(TH) f = Coefficient(velement) w = Coefficient(velement) Re = Constant(cell) dt = Constant(cell) a = (dot(u, v) + dt*dot(grad(u)*w, v) - dt*Re*inner(grad(u), grad(v)) + dt*dot(grad(p), v))*dx L = dot(f, v)*dx b = dot(u, grad(q))*dx def test_nabla_navier_stokes(self): cell = triangle velement = VectorElement("Lagrange", cell, 2) pelement = FiniteElement("Lagrange", cell, 1) TH = velement * pelement v, q = TestFunctions(TH) u, p = TrialFunctions(TH) f = Coefficient(velement) w = Coefficient(velement) Re = Constant(cell) dt = Constant(cell) a = (dot(u, v) + dt*dot(dot(w,nabla_grad(u)), v) - dt*Re*inner(grad(u), grad(v)) + dt*dot(grad(p), v))*dx L = dot(f, v)*dx b = dot(u, grad(q))*dx if __name__ == "__main__": main() ufl-1.3.0/test/test_future_division.py000077500000000000000000000020731226300046600201130ustar00rootroot00000000000000#!/usr/bin/env python # This file must be separate from the other arithmetic # tests to test the effect of this future statment from __future__ import division from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import Division, FloatValue, IntValue class FutureDivisionTestCase(UflTestCase): def test_future_true_float_division(self): d = as_ufl(20.0) / 10.0 self.assertIsInstance(d, FloatValue) self.assertEqual(d, 2) def test_future_true_int_division(self): # UFL treats all divisions as true division d = as_ufl(40) / 7 self.assertIsInstance(d, FloatValue) self.assertEqual(d, 40.0 / 7.0) #self.assertAlmostEqual(d, 40 / 7.0, 15) def test_future_floor_division_fails(self): f = as_ufl(2.0) r = as_ufl(4) s = as_ufl(5) self.assertRaises(NotImplementedError, lambda: r // 4) self.assertRaises(NotImplementedError, lambda: r // s) self.assertRaises(NotImplementedError, lambda: f // s) if __name__ == "__main__": main() ufl-1.3.0/test/test_grad.py000077500000000000000000000112341226300046600156110ustar00rootroot00000000000000#!/usr/bin/env python """ Test use of grad in various situations. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * #from ufl.classes import ... #from ufl.algorithms import ... class GradTestCase(UflTestCase): def xtest_grad_div_curl_properties_in_1D(self): self._test_grad_div_curl_properties(cell1D) def test_grad_div_curl_properties_in_2D(self): self._test_grad_div_curl_properties(cell2D) def xtest_grad_div_curl_properties_in_3D(self): self._test_grad_div_curl_properties(cell3D) def _test_grad_div_curl_properties(self, cell): d = cell.d S = FiniteElement("CG", cell, 1) V = VectorElement("CG", cell, 1) T = TensorElement("CG", cell, 1) cs = Constant(cell) cv = VectorConstant(cell) ct = TensorConstant(cell) s = Coefficient(S) v = Coefficient(V) t = Coefficient(T) def eval_s(x, derivatives=()): return sum(derivatives) def eval_v(x, derivatives=()): return tuple(float(k)+sum(derivatives) for k in range(d)) def eval_t(x, derivatives=()): return tuple(tuple(float(i*j)+sum(derivatives) for i in range(d)) for j in range(d)) mapping = { cs: eval_s, s: eval_s, cv: eval_v, v: eval_v, ct: eval_t, t: eval_t, } x = tuple(1.0+float(k) for k in range(d)) self.assertEqual(s.shape(), ()) self.assertEqual(v.shape(), (d,)) self.assertEqual(t.shape(), (d,d)) self.assertEqual(cs.shape(), ()) self.assertEqual(cv.shape(), (d,)) self.assertEqual(ct.shape(), (d,d)) self.assertEqual(s(x, mapping=mapping), eval_s(x)) self.assertEqual(v(x, mapping=mapping), eval_v(x)) self.assertEqual(t(x, mapping=mapping), eval_t(x)) self.assertEqual(grad(s).shape(), (d,)) self.assertEqual(grad(v).shape(), (d,d)) self.assertEqual(grad(t).shape(), (d,d,d)) self.assertEqual(grad(cs).shape(), (d,)) self.assertEqual(grad(cv).shape(), (d,d)) self.assertEqual(grad(ct).shape(), (d,d,d)) self.assertEqual(grad(s)[0](x, mapping=mapping), eval_s(x, (0,))) self.assertEqual(grad(v)[d-1,d-1](x, mapping=mapping), eval_v(x, derivatives=(d-1,))[d-1]) self.assertEqual(grad(t)[d-1,d-1,d-1](x, mapping=mapping), eval_t(x, derivatives=(d-1,))[d-1][d-1]) self.assertEqual(div(grad(cs)).shape(), ()) self.assertEqual(div(grad(cv)).shape(), (d,)) self.assertEqual(div(grad(ct)).shape(), (d,d)) self.assertEqual(s.dx(0).shape(), ()) self.assertEqual(v.dx(0).shape(), (d,)) self.assertEqual(t.dx(0).shape(), (d,d)) self.assertEqual(s.dx(0,0).shape(), ()) self.assertEqual(v.dx(0,0).shape(), (d,)) self.assertEqual(t.dx(0,0).shape(), (d,d)) i,j = indices(2) self.assertEqual(s.dx(i).shape(), ()) self.assertEqual(v.dx(i).shape(), (d,)) self.assertEqual(t.dx(i).shape(), (d,d)) self.assertEqual(s.dx(i).free_indices(), (i,)) self.assertEqual(v.dx(i).free_indices(), (i,)) self.assertEqual(t.dx(i).free_indices(), (i,)) self.assertEqual(s.dx(i,j).shape(), ()) self.assertEqual(v.dx(i,j).shape(), (d,)) self.assertEqual(t.dx(i,j).shape(), (d,d)) # This comparison is unstable w.r.t. sorting of i,j self.assertTrue(s.dx(i,j).free_indices() in [(i,j), (j,i)]) self.assertTrue(v.dx(i,j).free_indices() in [(i,j), (j,i)]) self.assertTrue(t.dx(i,j).free_indices() in [(i,j), (j,i)]) a0 = s.dx(0)*dx a1 = s.dx(0)**2*dx a2 = v.dx(0)**2*dx a3 = t.dx(0)**2*dx a4 = inner(grad(s), grad(s))*dx a5 = inner(grad(v), grad(v))*dx a6 = inner(grad(t), grad(t))*dx a7 = inner(div(grad(s)), s)*dx a8 = inner(div(grad(v)), v)*dx a9 = inner(div(grad(t)), t)*dx fd0 = a0.compute_form_data() fd1 = a1.compute_form_data() fd2 = a2.compute_form_data() fd3 = a3.compute_form_data() fd4 = a4.compute_form_data() fd5 = a5.compute_form_data() fd6 = a6.compute_form_data() fd7 = a7.compute_form_data() fd8 = a8.compute_form_data() fd9 = a9.compute_form_data() #self.assertTrue(False) # Just to show it runs # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_illegal.py000077500000000000000000000137741226300046600163200ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl import * from ufl.algorithms import * # TODO: Add more illegal expressions to check! class IllegalExpressionsTestCase(UflTestCase): def setUp(self): super(IllegalExpressionsTestCase, self).setUp() self.selement = FiniteElement("Lagrange", "triangle", 1) self.velement = VectorElement("Lagrange", "triangle", 1) self.a = Argument(self.selement) self.b = Argument(self.selement) self.v = Argument(self.velement) self.u = Argument(self.velement) self.f = Coefficient(self.selement) self.g = Coefficient(self.selement) self.vf = Coefficient(self.velement) self.vg = Coefficient(self.velement) def tearDown(self): super(IllegalExpressionsTestCase, self).tearDown() def test_mul_v_u(self): self.assertRaises(UFLException, lambda: self.v * self.u) def test_mul_vf_u(self): self.assertRaises(UFLException, lambda: self.vf * self.u) def test_mul_vf_vg(self): self.assertRaises(UFLException, lambda: self.vf * self.vg) def test_add_a_v(self): self.assertRaises(UFLException, lambda: self.a + self.v) def test_add_vf_b(self): self.assertRaises(UFLException, lambda: self.vf + self.b) def test_add_vectorexpr_b(self): tmp = self.vg + self.v + self.u + self.vf self.assertRaises(UFLException, lambda: tmp + self.b) # TODO: Add more forms, covering all UFL operators class FormsTestCase(UflTestCase): def setUp(self): super(FormsTestCase, self).setUp() def tearDown(self): super(FormsTestCase, self).tearDown() def test_source1(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) f = Coefficient(element) self.assertRaises(UFLException, lambda: f*v*dx) def test_source2(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) f = Coefficient(element) self.assertRaises(UFLException, lambda: dot(f[0], v)) def test_source3(self): element = TensorElement("Lagrange", "triangle", 1) v = TestFunction(element) f = Coefficient(element) self.assertRaises(UFLException, lambda: inner(f, v[0])*dx) def test_mass1(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) self.assertRaises(UFLException, lambda: u[i]*v*dx) def test_mass2(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) self.assertRaises(UFLException, lambda: u[i][j]) def test_mass3(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) self.assertRaises(UFLException, lambda: dot(u[i], v[j])*dx) def test_mass4(self): element = TensorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) a = inner(u,v)*dx # TODO: Assert something? What are we testing here? def check_validate_raises(self, a): def store_if_nothrow(): validate_form(a) store_if_nothrow.nothrow = True store_if_nothrow.nothrow = False self.assertRaises(UFLException, store_if_nothrow) if store_if_nothrow.nothrow: print "in check_validate_raises:" print "repr =", repr(a) print "str =", str(a) def test_duplicated_args(self): element = FiniteElement("Lagrange", "triangle", 1) element2 = FiniteElement("Lagrange", "triangle", 2) v = TestFunction(element) u = TrialFunction(element) V = TestFunction(element2) U = TrialFunction(element2) a = inner(u,v)*dx + inner(V,U)*dx self.check_validate_raises(a) def test_duplicated_args2(self): element = FiniteElement("Lagrange", "triangle", 1) element2 = FiniteElement("Lagrange", "triangle", 2) f = Coefficient(element) g = Coefficient(element2, count=f.count()) a = (f+g)*dx self.check_validate_raises(a) def test_stiffness1(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) a = dot(grad(u), grad(v)) * dx # TODO: Assert something? What are we testing here? def test_stiffness2(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) a = inner(grad(u), grad(v)) * dx # TODO: Assert something? What are we testing here? def test_stiffness3(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) a = inner(grad(u), grad(v)) * dx # TODO: Assert something? What are we testing here? def test_stiffness_with_conductivity(self): velement = VectorElement("Lagrange", "triangle", 1) telement = TensorElement("Lagrange", "triangle", 1) v = TestFunction(velement) u = TrialFunction(velement) M = Coefficient(telement) a = inner(M*grad(u), grad(v)) * dx # TODO: Assert something? What are we testing here? def test_navier_stokes(self): polygon = "triangle" velement = VectorElement("Lagrange", polygon, 2) pelement = FiniteElement("Lagrange", polygon, 1) TH = velement * pelement v, q = TestFunctions(TH) u, p = TrialFunctions(TH) f = Coefficient(velement) w = Coefficient(velement) Re = Constant(polygon) dt = Constant(polygon) a = dot(u, v) + dt*dot(dot(w, grad(u)), v) - dt*Re*inner(grad(u), grad(v)) + dt*dot(grad(p), v) L = dot(f, v) b = dot(u, grad(q)) # TODO: Assert something? What are we testing here? if __name__ == "__main__": main() ufl-1.3.0/test/test_indices.py000077500000000000000000000211411226300046600163100ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl import * from ufl.indexutils import * from ufl.algorithms import * from ufl.classes import IndexSum # TODO: add more expressions to test as many possible combinations of index notation as feasible... class IndexTestCase(UflTestCase): def test_index_utils(self): shape = (1,2,None,4,None) self.assertEqual((1,2,3,4,3), complete_shape(shape, 3)) ii = indices(3) self.assertEqual(ii, unique_indices(ii) ) self.assertEqual(ii, unique_indices(ii+ii) ) self.assertEqual((), repeated_indices(ii) ) self.assertEqual(ii, repeated_indices(ii+ii) ) self.assertEqual(ii, shared_indices(ii, ii) ) self.assertEqual(ii, shared_indices(ii, ii+ii) ) self.assertEqual(ii, shared_indices(ii+ii, ii) ) self.assertEqual(ii, shared_indices(ii+ii, ii+ii) ) self.assertEqual(ii, single_indices(ii) ) self.assertEqual((), single_indices(ii+ii) ) def test_vector_indices(self): element = VectorElement("CG", "triangle", 1) u = Argument(element) f = Coefficient(element) a = u[i]*f[i]*dx b = u[j]*f[j]*dx def test_tensor_indices(self): element = TensorElement("CG", "triangle", 1) u = Argument(element) f = Coefficient(element) a = u[i,j]*f[i,j]*dx b = u[j,i]*f[i,j]*dx c = u[j,i]*f[j,i]*dx try: d = (u[i,i]+f[j,i])*dx self.fail() except (UFLException, e): pass def test_indexed_sum1(self): element = VectorElement("CG", "triangle", 1) u = Argument(element) f = Coefficient(element) a = u[i]+f[i] try: a*dx self.fail() except (UFLException, e): pass def test_indexed_sum2(self): element = VectorElement("CG", "triangle", 1) v = Argument(element) u = Argument(element) f = Coefficient(element) a = u[j]+f[j]+v[j]+2*v[j]+exp(u[i]*u[i])/2*f[j] try: a*dx self.fail() except (UFLException, e): pass def test_indexed_sum3(self): element = VectorElement("CG", "triangle", 1) u = Argument(element) f = Coefficient(element) try: a = u[i]+f[j] self.fail() except (UFLException, e): pass def test_indexed_function1(self): element = VectorElement("CG", "triangle", 1) v = Argument(element) u = Argument(element) f = Coefficient(element) aarg = (u[i]+f[i])*v[i] a = exp(aarg)*dx def test_indexed_function2(self): element = VectorElement("CG", "triangle", 1) v = Argument(element) u = Argument(element) f = Coefficient(element) bfun = cos(f[0]) left = u[i] + f[i] right = v[i] * bfun self.assertEqual(len(left.free_indices()), 1) self.assertEqual(left.free_indices()[0], i) self.assertEqual(len(right.free_indices()), 1) self.assertEqual(right.free_indices()[0], i) b = left * right * dx def test_indexed_function3(self): element = VectorElement("CG", "triangle", 1) v = Argument(element) u = Argument(element) f = Coefficient(element) try: c = sin(u[i] + f[i])*dx self.fail() except (UFLException, e): pass def test_vector_from_indices(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) # legal vv = as_vector(u[i], i) uu = as_vector(v[j], j) w = v + u ww = vv + uu self.assertEqual(vv.rank(), 1) self.assertEqual(uu.rank(), 1) self.assertEqual(w.rank(), 1) self.assertEqual(ww.rank(), 1) def test_matrix_from_indices(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) A = as_matrix(u[i]*v[j], (i,j)) B = as_matrix(v[k]*v[k]*u[i]*v[j], (j,i)) C = A + A C = B + B D = A + B self.assertEqual(A.rank(), 2) self.assertEqual(B.rank(), 2) self.assertEqual(C.rank(), 2) self.assertEqual(D.rank(), 2) def test_vector_from_list(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) # create vector from list vv = as_vector([u[0], v[0]]) ww = vv + vv self.assertEqual(vv.rank(), 1) self.assertEqual(ww.rank(), 1) def test_matrix_from_list(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) # create matrix from list A = as_matrix( [ [u[0], u[1]], [v[0], v[1]] ] ) # create matrix from indices B = as_matrix( (v[k]*v[k]) * u[i]*v[j], (j,i) ) # Test addition C = A + A C = B + B D = A + B self.assertEqual(A.rank(), 2) self.assertEqual(B.rank(), 2) self.assertEqual(C.rank(), 2) self.assertEqual(D.rank(), 2) def test_tensor(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) # define the components of a fourth order tensor Cijkl = u[i]*v[j]*f[k]*g[l] self.assertEqual(Cijkl.rank(), 0) self.assertEqual(set(Cijkl.free_indices()), set((i,j,k,l))) # make it a tensor C = as_tensor(Cijkl, (i,j,k,l)) self.assertEqual(C.rank(), 4) self.assertSameIndices(C, ()) # get sub-matrix A = C[:,:,0,0] self.assertEqual(A.rank(), 2) self.assertSameIndices(A, ()) A = C[:,:,i,j] self.assertEqual(A.rank(), 2) self.assertEqual(set(A.free_indices()), set((i,j))) # legal? vv = as_vector([u[i], v[i]]) ww = f[i]*vv # this is well defined: ww = sum_i # illegal try: vv = as_vector([u[i], v[j]]) self.fail() except (UFLException, e): pass # illegal try: A = as_matrix( [ [u[0], u[1]], [v[0],] ] ) self.fail() except (UFLException, e): pass # ... def test_indexed(self): element = VectorElement("CG", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) i, j, k, l = indices(4) a = v[i] self.assertSameIndices(a, (i,)) a = outer(v,u)[i,j] self.assertSameIndices(a, (i,j)) a = outer(v,u)[i,i] self.assertSameIndices(a, ()) self.assertIsInstance(a, IndexSum) def test_spatial_derivative(self): cell = triangle element = VectorElement("CG", cell, 1) v = TestFunction(element) u = TrialFunction(element) i, j, k, l = indices(4) d = cell.d a = v[i].dx(i) self.assertSameIndices(a, ()) self.assertIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) a = v[i].dx(j) self.assertSameIndices(a, (i,j)) self.assertNotIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) a = (v[i]*u[j]).dx(i,j) self.assertSameIndices(a, ()) self.assertIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) a = v.dx(i,j) #self.assertSameIndices(a, (i,j)) self.assertEqual(set(a.free_indices()), set((j,i))) self.assertNotIsInstance(a, IndexSum) self.assertEqual(a.shape(), (d,)) a = v[i].dx(0) self.assertSameIndices(a, (i,)) self.assertNotIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) a = (v[i]*u[j]).dx(0, 1) # indices change place because of sorting, I guess this may be ok self.assertEqual(set(a.free_indices()), set((i,j))) self.assertNotIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) a = v.dx(i)[i] self.assertSameIndices(a, ()) self.assertIsInstance(a, IndexSum) self.assertEqual(a.shape(), ()) def test_renumbering(self): pass if __name__ == "__main__": main() ufl-1.3.0/test/test_lhs_rhs.py000077500000000000000000000035241226300046600163410ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Marie E. Rognes" # First added: 2011-11-09 # Last changed: 2011-11-09 from ufltestcase import UflTestCase, main from ufl import * class FormOperations(UflTestCase): def test_lhs_rhs_simple(self): V = FiniteElement("CG", interval, 1) v = TestFunction(V) u = TrialFunction(V) w = Argument(V, 0) f = Coefficient(V) F0 = f*u*v*w*dx a, L = system(F0) assert(len(a.integrals()) == 0) assert(len(L.integrals()) == 0) F1 = derivative(F0, f) a, L = system(F1) assert(len(a.integrals()) == 0) assert(len(L.integrals()) == 0) F2 = action(F0, f) a, L = system(F2) assert(len(a.integrals()) == 1) assert(len(L.integrals()) == 0) F3 = action(F2, f) a, L = system(F3) assert(len(L.integrals()) == 1) def test_lhs_rhs_derivatives(self): V = FiniteElement("CG", interval, 1) v = TestFunction(V) u = TrialFunction(V) f = Coefficient(V) F0 = exp(f)*u*v*dx + v*dx + f*v*ds + exp(f)('+')*v*dS a, L = system(F0) assert(len(a.integrals()) == 1) assert(len(L.integrals()) == 3) F1 = derivative(F0, f) a, L = system(F0) def test_lhs_rhs_slightly_obscure(self): V = FiniteElement("CG", interval, 1) u = TrialFunction(V) w = Argument(V, 2) f = Constant(interval) # FIXME: # ufl.algorithsm.formtransformations.compute_form_with_arity # is not perfect, e.g. try # F = f*u*w*dx + f*w*dx F = f*u*w*dx a, L = system(F) assert(len(a.integrals()) == 1) assert(len(L.integrals()) == 0) F = f*w*dx a, L = system(F) assert(len(L.integrals()) == 1) if __name__ == "__main__": main() ufl-1.3.0/test/test_literals.py000077500000000000000000000077361226300046600165270ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Martin Sandve Alnes" __date__ = "2011-04-14 -- 2011-04-14" from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import Indexed from ufl.constantvalue import Zero, FloatValue, IntValue, as_ufl class TestLiterals(UflTestCase): def test_zero(self): z1 = Zero(()) z2 = Zero(()) z3 = as_ufl(0) z4 = as_ufl(0.0) z5 = FloatValue(0) z6 = FloatValue(0.0) #self.assertTrue(z1 is z2) #self.assertTrue(z1 is z3) #self.assertTrue(z1 is z4) #self.assertTrue(z1 is z5) #self.assertTrue(z1 is z6) self.assertEqual(z1, z1) self.assertEqual(z1, 0) self.assertEqual(z1, 0.0) self.assertNotEqual(z1, 1.0) self.assertFalse(z1) def test_float(self): f1 = as_ufl(1) f2 = as_ufl(1.0) f3 = FloatValue(1) f4 = FloatValue(1.0) f5 = 3 - FloatValue(1) - 1 f6 = 3 * FloatValue(2) / 6 self.assertEqual(f1, f1) self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations! self.assertEqual(f2, f3) self.assertEqual(f2, f4) self.assertEqual(f2, f5) self.assertEqual(f2, f6) def test_int(self): f1 = as_ufl(1) f2 = as_ufl(1.0) f3 = IntValue(1) f4 = IntValue(1.0) f5 = 3 - IntValue(1) - 1 f6 = 3 * IntValue(2) / 6 self.assertEqual(f1, f1) self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations! self.assertEqual(f1, f3) self.assertEqual(f1, f4) self.assertEqual(f1, f5) self.assertEqual(f2, f6) # Division produces a FloatValue def test_scalar_sums(self): n = 10 s = [as_ufl(i) for i in range(n)] for i in range(n): self.assertNotEqual(s[i], i+1) for i in range(n): self.assertEqual(s[i], i) for i in range(n): self.assertEqual(0 + s[i], i) for i in range(n): self.assertEqual(s[i] + 0, i) for i in range(n): self.assertEqual(0 + s[i] + 0, i) for i in range(n): self.assertEqual(1 + s[i] - 1, i) self.assertEqual(s[1] + s[1], 2) self.assertEqual(s[1] + s[2], 3) self.assertEqual(s[1] + s[2] + s[3], s[6]) self.assertEqual(s[5] - s[2], 3) self.assertEqual(1*s[5], 5) self.assertEqual(2*s[5], 10) self.assertEqual(s[6]/3, 2) def test_identity(self): pass # FIXME def test_permutation_symbol_3(self): e = PermutationSymbol(3) self.assertEqual(e.shape(), (3, 3, 3)) self.assertEqual(eval(repr(e)), e) for i in range(3): for j in range(3): for k in range(3): value = (j-i)*(k-i)*(k-j)/2 self.assertEqual(e[i, j, k], value) i, j, k = indices(3) self.assertIsInstance(e[i,j,k], Indexed) x = (0,0,0) self.assertEqual((e[i,j,k] * e[i,j,k])(x), 6) def test_permutation_symbol_n(self): for n in range(2,5): # tested with upper limit 7, but evaluation is a bit slow then e = PermutationSymbol(n) self.assertEqual(e.shape(), (n,)*n) self.assertEqual(eval(repr(e)), e) ii = indices(n) x = (0,)*n nfac = product(m for m in range(1,n+1)) self.assertEqual((e[ii] * e[ii])(x), nfac) def test_unit_dyads(self): from ufl.tensors import unit_vectors, unit_matrices ei, ej = unit_vectors(2) self.assertEqual(as_vector((1,0)), ei) self.assertEqual(as_vector((0,1)), ej) eii, eij, eji, ejj = unit_matrices(2) self.assertEqual(as_matrix(((1,0),(0,0))), eii) self.assertEqual(as_matrix(((0,1),(0,0))), eij) self.assertEqual(as_matrix(((0,0),(1,0))), eji) self.assertEqual(as_matrix(((0,0),(0,1))), ejj) if __name__ == "__main__": main() ufl-1.3.0/test/test_manualtest.py000077500000000000000000000005331226300046600170510ustar00rootroot00000000000000#!/usr/bin/env python __authors__ = "Automatically generated from .tex files" __date__ = "2009-02-07 -- 2009-02-07" from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import * from ufl.algorithms import * class ManualTestCase(UflTestCase): def setUp(self): pass if __name__ == "__main__": main() ufl-1.3.0/test/test_pickle.py000077500000000000000000000366301226300046600161520ustar00rootroot00000000000000#!/usr/bin/env python """Pickle all the unit test forms from FFC 0.5.0""" __author__ = "Anders Logg (logg@simula.no) et al." __date__ = "2008-04-09 -- 2008-09-26" __copyright__ = "Copyright (C) 2008 Anders Logg et al." __license__ = "GNU GPL version 3 or any later version" # Examples copied from the FFC demo directory, examples contributed # by Johan Jansson, Kristian Oelgaard, Marie Rognes, and Garth Wells. from ufltestcase import UflTestCase, main from ufl import * from ufl.algorithms import preprocess import pickle p = pickle.HIGHEST_PROTOCOL class PickleTestCase(UflTestCase): def testConstant(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) c = Constant("triangle") d = VectorConstant("triangle") a = c*dot(grad(v), grad(u))*dx # FFC notation: L = dot(d, grad(v))*dx L = inner(d, grad(v))*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testElasticity(self): element = VectorElement("Lagrange", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) def eps(v): # FFC notation: return grad(v) + transp(grad(v)) return grad(v) + (grad(v)).T # FFC notation: a = 0.25*dot(eps(v), eps(u))*dx a = 0.25*inner(eps(v), eps(u))*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testEnergyNorm(self): element = FiniteElement("Lagrange", "tetrahedron", 1) v = Coefficient(element) a = (v*v + dot(grad(v), grad(v)))*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testEquation(self): element = FiniteElement("Lagrange", "triangle", 1) k = 0.1 v = TestFunction(element) u = TrialFunction(element) u0 = Coefficient(element) F = v*(u - u0)*dx + k*dot(grad(v), grad(0.5*(u0 + u)))*dx a = lhs(F) L = rhs(F) a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testFunctionOperators(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) # FFC notation: a = sqrt(1/modulus(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx + v*u*sqrt(f*g)*g*dx a = sqrt(1/abs(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx + v*u*sqrt(f*g)*g*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testHeat(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u1 = TrialFunction(element) u0 = Coefficient(element) c = Coefficient(element) f = Coefficient(element) k = Constant("triangle") a = v*u1*dx + k*c*dot(grad(v), grad(u1))*dx L = v*u0*dx + k*v*f*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testMass(self): element = FiniteElement("Lagrange", "tetrahedron", 3) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testMixedMixedElement(self): P3 = FiniteElement("Lagrange", "triangle", 3) element = (P3 * P3) * (P3 * P3) element_pickle = pickle.dumps(element, p) element_restore = pickle.loads(element_pickle) assert(element == element_restore) def testMixedPoisson(self): q = 1 BDM = FiniteElement("Brezzi-Douglas-Marini", "triangle", q) DG = FiniteElement("Discontinuous Lagrange", "triangle", q - 1) mixed_element = BDM * DG (tau, w) = TestFunctions(mixed_element) (sigma, u) = TrialFunctions(mixed_element) f = Coefficient(DG) a = (dot(tau, sigma) - div(tau)*u + w*div(sigma))*dx L = w*f*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testNavierStokes(self): element = VectorElement("Lagrange", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) w = Coefficient(element) # FFC notation: a = v[i]*w[j]*D(u[i], j)*dx a = v[i]*w[j]*Dx(u[i], j)*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testNeumannProblem(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) # FFC notation: a = dot(grad(v), grad(u))*dx a = inner(grad(v), grad(u))*dx # FFC notation: L = dot(v, f)*dx + dot(v, g)*ds L = inner(v, f)*dx + inner(v, g)*ds a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testOptimization(self): element = FiniteElement("Lagrange", "triangle", 3) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) a = dot(grad(v), grad(u))*dx L = v*f*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testP5tet(self): element = FiniteElement("Lagrange", tetrahedron, 5) element_pickle = pickle.dumps(element, p) element_restore = pickle.loads(element_pickle) assert(element == element_restore) def testP5tri(self): element = FiniteElement("Lagrange", triangle, 5) element_pickle = pickle.dumps(element, p) element_restore = pickle.loads(element_pickle) def testPoissonDG(self): element = FiniteElement("Discontinuous Lagrange", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) n = triangle.n # FFC notation: h = MeshSize("triangle"), not supported by UFL h = Constant(triangle) gN = Coefficient(element) alpha = 4.0 gamma = 8.0 # FFC notation #a = dot(grad(v), grad(u))*dx \ # - dot(avg(grad(v)), jump(u, n))*dS \ # - dot(jump(v, n), avg(grad(u)))*dS \ # + alpha/h('+')*dot(jump(v, n), jump(u, n))*dS \ # - dot(grad(v), mult(u,n))*ds \ # - dot(mult(v,n), grad(u))*ds \ # + gamma/h*v*u*ds a = inner(grad(v), grad(u))*dx \ - inner(avg(grad(v)), jump(u, n))*dS \ - inner(jump(v, n), avg(grad(u)))*dS \ + alpha/h('+')*dot(jump(v, n), jump(u, n))*dS \ - inner(grad(v), u*n)*ds \ - inner(u*n, grad(u))*ds \ + gamma/h*v*u*ds L = v*f*dx + v*gN*ds a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testPoisson(self): element = FiniteElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) # Note: inner() also works a = dot(grad(v), grad(u))*dx L = v*f*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testPoissonSystem(self): element = VectorElement("Lagrange", "triangle", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) # FFC notation: a = dot(grad(v), grad(u))*dx a = inner(grad(v), grad(u))*dx # FFC notation: L = dot(v, f)*dx L = inner(v, f)*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testQuadratureElement(self): element = FiniteElement("Lagrange", "triangle", 2) # FFC notation: #QE = QuadratureElement("triangle", 3) #sig = VectorQuadratureElement("triangle", 3) QE = FiniteElement("Quadrature", "triangle", 3) sig = VectorElement("Quadrature", "triangle", 3) v = TestFunction(element) u = TrialFunction(element) u0= Coefficient(element) C = Coefficient(QE) sig0 = Coefficient(sig) f = Coefficient(element) a = v.dx(i)*C*u.dx(i)*dx + v.dx(i)*2*u0*u*u0.dx(i)*dx L = v*f*dx - dot(grad(v), sig0)*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testStokes(self): # UFLException: Shape mismatch in sum. P2 = VectorElement("Lagrange", "triangle", 2) P1 = FiniteElement("Lagrange", "triangle", 1) TH = P2 * P1 (v, q) = TestFunctions(TH) (u, r) = TrialFunctions(TH) f = Coefficient(P2) # FFC notation: # a = (dot(grad(v), grad(u)) - div(v)*p + q*div(u))*dx a = (inner(grad(v), grad(u)) - div(v)*r + q*div(u))*dx L = dot(v, f)*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testSubDomain(self): element = FiniteElement("CG", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) M = f*dx(2) + f*ds(5) M_pickle = pickle.dumps(M, p) M_restore = pickle.loads(M_pickle) assert(M.deprecated_signature() == M_restore.deprecated_signature()) def testSubDomains(self): element = FiniteElement("CG", "tetrahedron", 1) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx(0) + 10.0*v*u*dx(1) + v*u*ds(0) + 2.0*v*u*ds(1) + v('+')*u('+')*dS(0) + 4.3*v('+')*u('+')*dS(1) a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testTensorWeightedPoisson(self): # FFC notation: #P1 = FiniteElement("Lagrange", "triangle", 1) #P0 = FiniteElement("Discontinuous Lagrange", "triangle", 0) # #v = TestFunction(P1) #u = TrialFunction(P1) #f = Coefficient(P1) # #c00 = Coefficient(P0) #c01 = Coefficient(P0) #c10 = Coefficient(P0) #c11 = Coefficient(P0) # #C = [[c00, c01], [c10, c11]] # #a = dot(grad(v), mult(C, grad(u)))*dx P1 = FiniteElement("Lagrange", "triangle", 1) P0 = TensorElement("Discontinuous Lagrange", "triangle", 0, shape=(2, 2)) v = TestFunction(P1) u = TrialFunction(P1) C = Coefficient(P0) a = inner(grad(v), C*grad(u))*dx a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) def testVectorLaplaceGradCurl(self): def HodgeLaplaceGradCurl(element, felement): (tau, v) = TestFunctions(element) (sigma, u) = TrialFunctions(element) f = Coefficient(felement) # FFC notation: a = (dot(tau, sigma) - dot(grad(tau), u) + dot(v, grad(sigma)) + dot(curl(v), curl(u)))*dx a = (inner(tau, sigma) - inner(grad(tau), u) + inner(v, grad(sigma)) + inner(curl(v), curl(u)))*dx # FFC notation: L = dot(v, f)*dx L = inner(v, f)*dx return [a, L] shape = "tetrahedron" order = 1 GRAD = FiniteElement("Lagrange", shape, order) # FFC notation: CURL = FiniteElement("Nedelec", shape, order-1) CURL = FiniteElement("N1curl", shape, order) VectorLagrange = VectorElement("Lagrange", shape, order+1) [a, L] = HodgeLaplaceGradCurl(GRAD * CURL, VectorLagrange) a_pickle = pickle.dumps(a, p) a_restore = pickle.loads(a_pickle) L_pickle = pickle.dumps(L, p) L_restore = pickle.loads(L_pickle) assert(a.deprecated_signature() == a_restore.deprecated_signature()) assert(L.deprecated_signature() == L_restore.deprecated_signature()) def testIdentity(self): i = Identity(2) i_pickle = pickle.dumps(i, p) i_restore = pickle.loads(i_pickle) assert(i == i_restore) def testFormData(self): element = FiniteElement("Lagrange", "tetrahedron", 3) v = TestFunction(element) u = TrialFunction(element) a = v*u*dx form_data = preprocess(a) form_data_pickle = pickle.dumps(form_data, p) form_data_restore = pickle.loads(form_data_pickle) form_data_restore.validate() assert(str(form_data)==str(form_data_restore)) if __name__ == "__main__": main() ufl-1.3.0/test/test_scratch.py000077500000000000000000000400601226300046600163220ustar00rootroot00000000000000#!/usr/bin/env python """ This is a template file you can copy when making a new test case. Begin by copying this file to a filename matching test_*.py. The tests in the file will then automatically be run by ./test.py. Next look at the TODO markers below for places to edit. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main from itertools import izip # This imports everything external code will see from ufl from ufl import * from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.tensors import as_scalar, unit_indexed_tensor, unwrap_list_tensor # TODO: Import only what you need from classes and algorithms: from ufl.classes import Grad, FormArgument, Zero, Indexed, FixedIndex, ListTensor #from ufl.algorithms import ... class MockForwardAD: def __init__(self): self._w = () self._v = () class Obj: def __init__(self): self._data = {} self._cd = Obj() def grad(self, g): # If we hit this type, it has already been propagated # to a coefficient (or grad of a coefficient), # FIXME: Assert this! # so we need to take the gradient of the variation or return zero. # Complications occur when dealing with derivatives w.r.t. single components... # Figure out how many gradients are around the inner terminal ngrads = 0 o = g while isinstance(o, Grad): o, = o.operands() ngrads += 1 if not isinstance(o, FormArgument): error("Expecting gradient of a FormArgument, not %r" % (o,)) def apply_grads(f): if not isinstance(f, FormArgument): print ','*60 print f print o print g print ','*60 error("What?") for i in range(ngrads): f = Grad(f) return f # Find o among all w without any indexing, which makes this easy for (w, v) in izip(self._w, self._v): if o == w and isinstance(v, FormArgument): # Case: d/dt [w + t v] return (g, apply_grads(v)) # If o is not among coefficient derivatives, return do/dw=0 gprimesum = Zero(g.shape()) def analyse_variation_argument(v): # Analyse variation argument if isinstance(v, FormArgument): # Case: d/dt [w[...] + t v] vval, vcomp = v, () elif isinstance(v, Indexed): # Case: d/dt [w + t v[...]] # Case: d/dt [w[...] + t v[...]] vval, vcomp = v.operands() vcomp = tuple(vcomp) else: error("Expecting argument or component of argument.") ufl_assert(all(isinstance(k, FixedIndex) for k in vcomp), "Expecting only fixed indices in variation.") return vval, vcomp def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp): # Apply gradients directly to argument vval, # and get the right indexed scalar component(s) kk = indices(ngrads) Dvkk = apply_grads(vval)[vcomp+kk] # Place scalar component(s) Dvkk into the right tensor positions if wshape: Ejj, jj = unit_indexed_tensor(wshape, wcomp) else: Ejj, jj = 1, () gprimeterm = as_tensor(Ejj*Dvkk, jj+kk) return gprimeterm # Accumulate contributions from variations in different components for (w, v) in izip(self._w, self._v): # Analyse differentiation variable coefficient if isinstance(w, FormArgument): if not w == o: continue wshape = w.shape() if isinstance(v, FormArgument): # Case: d/dt [w + t v] return (g, apply_grads(v)) elif isinstance(v, ListTensor): # Case: d/dt [w + t <...,v,...>] for wcomp, vsub in unwrap_list_tensor(v): if not isinstance(vsub, Zero): vval, vcomp = analyse_variation_argument(vsub) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: ufl_assert(wshape == (), "Expecting scalar coefficient in this branch.") # Case: d/dt [w + t v[...]] wval, wcomp = w, () vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) elif isinstance(w, Indexed): # This path is tested in unit tests, but not actually used? # Case: d/dt [w[...] + t v[...]] # Case: d/dt [w[...] + t v] wval, wcomp = w.operands() if not wval == o: continue assert isinstance(wval, FormArgument) ufl_assert(all(isinstance(k, FixedIndex) for k in wcomp), "Expecting only fixed indices in differentiation variable.") wshape = wval.shape() vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: error("Expecting coefficient or component of coefficient.") # FIXME: Handle other coefficient derivatives: oprimes = self._cd._data.get(o) if 0: oprimes = self._cd._data.get(o) if oprimes is None: if self._cd._data: # TODO: Make it possible to silence this message in particular? # It may be good to have for debugging... warning("Assuming d{%s}/d{%s} = 0." % (o, self._w)) else: # Make sure we have a tuple to match the self._v tuple if not isinstance(oprimes, tuple): oprimes = (oprimes,) ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\ "expecting a matching tuple of coefficient derivatives.") # Compute dg/dw_j = dg/dw_h : v. # Since we may actually have a tuple of oprimes and vs in a # 'mixed' space, sum over them all to get the complete inner # product. Using indices to define a non-compound inner product. for (oprime, v) in izip(oprimes, self._v): error("FIXME: Figure out how to do this with ngrads") so, oi = as_scalar(oprime) rv = len(v.shape()) oi1 = oi[:-rv] oi2 = oi[-rv:] prod = so*v[oi2] if oi1: gprimesum += as_tensor(prod, oi1) else: gprimesum += prod return (g, gprimesum) class ScratchTestCase(UflTestCase): def setUp(self): super(ScratchTestCase, self).setUp() def tearDown(self): super(ScratchTestCase, self).tearDown() def test_something(self): self.assertTrue(42) def test_unit_tensor(self): E2_1,ii = unit_indexed_tensor((2,), (1,)) E3_1,ii = unit_indexed_tensor((3,), (1,)) E22_10,ii = unit_indexed_tensor((2,2), (1,0)) # TODO: Evaluate and assert values def test_unwrap_list_tensor(self): lt = as_tensor((1,2)) expected = [((0,), 1), ((1,), 2),] comp = unwrap_list_tensor(lt) self.assertEqual(comp, expected) lt = as_tensor(((1,2),(3,4))) expected = [((0,0), 1), ((0,1), 2), ((1,0), 3), ((1,1), 4),] comp = unwrap_list_tensor(lt) self.assertEqual(comp, expected) lt = as_tensor((((1,2),(3,4)), ((11,12),(13,14)))) expected = [((0,0,0), 1), ((0,0,1), 2), ((0,1,0), 3), ((0,1,1), 4), ((1,0,0), 11), ((1,0,1), 12), ((1,1,0), 13), ((1,1,1), 14),] comp = unwrap_list_tensor(lt) self.assertEqual(comp, expected) def test__forward_coefficient_ad__grad_of_scalar_coefficient(self): U = FiniteElement("CG", cell2D, 1) u = Coefficient(U) du = TestFunction(U) mad = MockForwardAD() mad._w = (u,) mad._v = (du,) # Simple grad(coefficient) -> grad(variation) f = grad(u) df = grad(du) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) # Simple grad(grad(coefficient)) -> grad(grad(variation)) f = grad(grad(u)) df = grad(grad(du)) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) def test__forward_coefficient_ad__grad_of_vector_coefficient(self): V = VectorElement("CG", cell2D, 1) v = Coefficient(V) dv = TestFunction(V) mad = MockForwardAD() mad._w = (v,) mad._v = (dv,) # Simple grad(coefficient) -> grad(variation) f = grad(v) df = grad(dv) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) # Simple grad(grad(coefficient)) -> grad(grad(variation)) f = grad(grad(v)) df = grad(grad(dv)) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation(self): V = VectorElement("CG", cell2D, 1) v = Coefficient(V) dv = TestFunction(V) mad = MockForwardAD() # Component of variation: # grad(grad(c))[0,...] -> grad(grad(dc))[1,...] mad._w = (v[0],) mad._v = (dv[1],) f = grad(v) df = grad(as_vector((dv[1],0))) # Mathematically this would be the natural result j,k = indices(2) df = as_tensor(Identity(2)[0,j]*grad(dv)[1,k], (j,k)) # Actual representation should have grad right next to dv g, dg = mad.grad(f) if 0: print '\nf ', f print 'df ', df print 'g ', g print 'dg ', dg self.assertEqual(f.shape(), df.shape()) self.assertEqual(g.shape(), f.shape()) self.assertEqual(dg.shape(), df.shape()) self.assertEqual(g, f) self.assertEqual((inner(dg,dg)*dx).deprecated_signature(), (inner(df,df)*dx).deprecated_signature()) #self.assertEqual(dg, df) # Expected to fail because of different index numbering # Multiple components of variation: # grad(grad(c))[0,1,:,:] -> grad(grad(dc))[1,0,:,:] mad._w = (v[0], v[1]) mad._v = (dv[1], dv[0]) f = grad(v) # Mathematically this would be the natural result: df = grad(as_vector((dv[1],dv[0]))) # Actual representation should have grad right next to dv: j0,k0 = indices(2) j1,k1 = indices(2) # Using j0,k0 for both terms gives different signature df = (as_tensor(Identity(2)[0,j0]*grad(dv)[1,k0], (j0,k0)) + as_tensor(Identity(2)[1,j1]*grad(dv)[0,k1], (j1,k1))) g, dg = mad.grad(f) print '\nf ', f print 'df ', df print 'g ', g print 'dg ', dg self.assertEqual(f.shape(), df.shape()) self.assertEqual(g.shape(), f.shape()) self.assertEqual(dg.shape(), df.shape()) self.assertEqual(g, f) self.assertEqual((inner(dg,dg)*dx).deprecated_signature(), (inner(df,df)*dx).deprecated_signature()) #self.assertEqual(dg, df) # Expected to fail because of different index numbering def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation_in_list(self): V = VectorElement("CG", cell2D, 1) v = Coefficient(V) dv = TestFunction(V) mad = MockForwardAD() # Component of variation: # grad(grad(c))[0,...] -> grad(grad(dc))[1,...] mad._w = (v,) mad._v = (as_vector((dv[1],0)),) f = grad(v) df = grad(as_vector((dv[1],0))) # Mathematically this would be the natural result j,k = indices(2) df = as_tensor(Identity(2)[0,j]*grad(dv)[1,k], (j,k)) # Actual representation should have grad right next to dv g, dg = mad.grad(f) if 0: print '\nf ', f print 'df ', df print 'g ', g print 'dg ', dg self.assertEqual(f.shape(), df.shape()) self.assertEqual(g.shape(), f.shape()) self.assertEqual(dg.shape(), df.shape()) self.assertEqual(g, f) self.assertEqual((inner(dg,dg)*dx).deprecated_signature(), (inner(df,df)*dx).deprecated_signature()) #self.assertEqual(dg, df) # Expected to fail because of different index numbering # Multiple components of variation: # grad(grad(c))[0,1,:,:] -> grad(grad(dc))[1,0,:,:] mad._w = (v, ) mad._v = (as_vector((dv[1], dv[0])),) f = grad(v) # Mathematically this would be the natural result: df = grad(as_vector((dv[1],dv[0]))) # Actual representation should have grad right next to dv: j0,k0 = indices(2) j1,k1 = indices(2) # Using j0,k0 for both terms gives different signature df = (as_tensor(Identity(2)[0,j0]*grad(dv)[1,k0], (j0,k0)) + as_tensor(Identity(2)[1,j1]*grad(dv)[0,k1], (j1,k1))) g, dg = mad.grad(f) print '\nf ', f print 'df ', df print 'g ', g print 'dg ', dg self.assertEqual(f.shape(), df.shape()) self.assertEqual(g.shape(), f.shape()) self.assertEqual(dg.shape(), df.shape()) self.assertEqual(g, f) self.assertEqual((inner(dg,dg)*dx).deprecated_signature(), (inner(df,df)*dx).deprecated_signature()) #self.assertEqual(dg, df) # Expected to fail because of different index numbering def test__forward_coefficient_ad__grad_of_tensor_coefficient(self): W = TensorElement("CG", cell2D, 1) w = Coefficient(W) dw = TestFunction(W) mad = MockForwardAD() mad._w = (w,) mad._v = (dw,) # Simple grad(coefficient) -> grad(variation) f = grad(w) df = grad(dw) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) # Simple grad(grad(coefficient)) -> grad(grad(variation)) f = grad(grad(w)) df = grad(grad(dw)) g, dg = mad.grad(f) self.assertEqual(g, f) self.assertEqual(dg, df) def test__forward_coefficient_ad__grad_of_tensor_coefficient__with_component_variation(self): W = TensorElement("CG", cell2D, 1) w = Coefficient(W) dw = TestFunction(W) mad = MockForwardAD() # Component of variation: # grad(grad(c))[0,...] -> grad(grad(dc))[1,...] wc = (1,0) dwc = (0,1) mad._w = (w[wc],) mad._v = (dw[dwc],) f = grad(w) df = grad(as_matrix(((0,0),(dw[dwc],0)))) # Mathematically this is it. i,j,k = indices(3) E = outer(Identity(2)[wc[0],i],Identity(2)[wc[1],j]) Ddw = grad(dw)[dwc + (k,)] df = as_tensor(E*Ddw, (i,j,k)) # Actual representation should have grad next to dv g, dg = mad.grad(f) if 0: print '\nf ', f print 'df ', df print 'g ', g print 'dg ', dg self.assertEqual(f.shape(), df.shape()) self.assertEqual(g.shape(), f.shape()) self.assertEqual(dg.shape(), df.shape()) self.assertEqual(g, f) self.assertEqual((inner(dg,dg)*dx).deprecated_signature(), (inner(df,df)*dx).deprecated_signature()) #self.assertEqual(dg, df) # Expected to fail because of different index numbering # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_signature.py000077500000000000000000000354021226300046600167000ustar00rootroot00000000000000#!/usr/bin/env python """ Test the computation of form signatures. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * from ufl.common import EmptyDictType from ufl.classes import MultiIndex from ufl.algorithms.signature import compute_multiindex_hashdata, \ compute_terminal_hashdata, compute_form_signature from itertools import chain # TODO: Test compute_terminal_hashdata # TODO: Check that form argument counts only affect the sig by their relative ordering # TODO: Check that all other relevant terminal propeties affect the terminal_hashdata # TODO: Test that operator types affect the sig # TODO: Test that we do not get collisions for some large sets of generated forms # TODO: How do we know that we have tested the signature reliably enough? class TerminalHashDataTestCase(UflTestCase): def compute_unique_hashdatas(self, hashdatas): count = 0 data = set() hashes = set() reprs = set() for d in hashdatas: if isinstance(d, dict): t = str(d.items()) else: t = str(d) data.add(t) hashes.add(hash(t)) reprs.add(repr(d)) count += 1 return count, len(data), len(reprs), len(hashes) def check_unique_hashdatas(self, hashdatas): c, d, r, h = self.compute_unique_hashdatas(hashdatas) self.assertEqual(d, c) self.assertEqual(r, c) self.assertEqual(h, c) def test_terminal_hashdata_depends_on_literals(self): reprs = set() hashes = set() def forms(): x = triangle.x i, j = indices(2) for d in (2, 3): I = Identity(d) for fv in (1.1, 2.2): for iv in (5, 7): expr = (I[0,j]*(fv*x[j]))**iv reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_terminal_hashdata(expr) c, d, r, h = self.compute_unique_hashdatas(forms()) self.assertEqual(c, 8) self.assertEqual(d, c) self.assertEqual(r, c) self.assertEqual(h, c) self.assertEqual(len(reprs), c) self.assertEqual(len(hashes), c) def test_terminal_hashdata_depends_on_geometry(self): reprs = set() hashes = set() def forms(): i, j = indices(2) for cell in (triangle, tetrahedron, cell2D, cell3D): d = cell.d x = cell.x n = cell.n r = cell.circumradius a = cell.facet_area s = cell.surface_area v = cell.volume I = Identity(d) for w in (x, n): for q in (r, a, s, v): expr = (I[0,j]*(q*w[j])) reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_terminal_hashdata(expr) c, d, r, h = self.compute_unique_hashdatas(forms()) self.assertEqual(c, 4*2*4) self.assertEqual(d, c) self.assertEqual(r, c) self.assertEqual(h, c) self.assertEqual(len(reprs), c) self.assertEqual(len(hashes), c) def test_terminal_hashdata_depends_on_form_argument_properties(self): reprs = set() hashes = set() nelm = 6 nreps = 2 def forms(): for rep in range(nreps): for cell in (triangle, tetrahedron, cell2D, cell3D): d = cell.d for degree in (1, 2): for family in ("CG", "Lagrange", "DG"): V = FiniteElement(family, cell, degree) W = VectorElement(family, cell, degree) W2 = VectorElement(family, cell, degree, dim=d+1) T = TensorElement(family, cell, degree) S = TensorElement(family, cell, degree, symmetry=True) S2 = TensorElement(family, cell, degree, shape=(d,d), symmetry={(0,0):(1,1)}) elements = [V, W, W2, T, S, S2] assert len(elements) == nelm for H in elements[:nelm]: a = Argument(H, count=1) c = Coefficient(H, count=1) for f in (a,c): expr = inner(f,f) reprs.add(repr(expr)) hashes.add(hash(expr)) data = compute_terminal_hashdata(expr) yield data c, d, r, h = self.compute_unique_hashdatas(forms()) c1 = nreps* 4*2* 3 *nelm*2 c2 = 4*2* (3-1) *nelm*2 self.assertEqual(c, c1) self.assertEqual(d, c2) self.assertEqual(r, c2) self.assertEqual(h, c2) self.assertEqual(len(reprs), c2) self.assertEqual(len(hashes), c2) def test_terminal_hashdata_does_not_depend_on_form_argument_counts(self): reprs = set() hashes = set() counts = list(range(-3,4)) nreps = 2 def forms(): for rep in range(nreps): for cell in (triangle, hexahedron): for k in counts: V = FiniteElement("CG", cell, 2) a1 = Argument(V, count=k) a2 = Argument(V, count=k+2) c1 = Coefficient(V, count=k) c2 = Coefficient(V, count=k+2) for f,g in ((a1,a2), (c1,c2)): expr = inner(f,g) reprs.add(repr(expr)) hashes.add(hash(expr)) data = compute_terminal_hashdata(expr) keys = sorted(data.keys(), key=lambda x: x.count()) values = [data[k] for k in keys] yield values c, d, r, h = self.compute_unique_hashdatas(forms()) c1 = len(counts) * 4 self.assertEqual(c, nreps * c1) self.assertEqual(d, 4) self.assertEqual(r, 4) self.assertEqual(h, 4) self.assertEqual(len(reprs), c1) self.assertEqual(len(hashes), c1) class MultiIndexHashDataTestCase(UflTestCase): def compute_unique_hashdatas(self, hashdatas): count = 0 data = set() hashes = set() reprs = set() for d in hashdatas: data.add(tuple(d)) hashes.add(hash(tuple(d))) reprs.add(repr(d)) count += 1 return count, len(data), len(reprs), len(hashes) def check_unique_hashdatas(self, hashdatas): c, d, r, h = self.compute_unique_hashdatas(hashdatas) self.assertEqual(d, c) self.assertEqual(r, c) self.assertEqual(h, c) def test_multiindex_hashdata_depends_on_fixed_index_values(self): reprs = set() hashes = set() def hashdatas(): for i in range(3): for ii in ((i,), (i,0), (1,i)): expr = MultiIndex(ii, {}) self.assertTrue(type(expr.index_dimensions()) is EmptyDictType) # Just a side check reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_multiindex_hashdata(expr, {}) c, d, r, h = self.compute_unique_hashdatas(hashdatas()) self.assertEqual(c, 9) self.assertEqual(d, 9-1) # (1,0) is repeated, therefore -1 self.assertEqual(len(reprs), 9-1) self.assertEqual(len(hashes), 9-1) def test_multiindex_hashdata_does_not_depend_on_counts(self): reprs = set() hashes = set() def hashdatas(): ijs = [] iind = indices(3) jind = indices(3) for i in iind: ijs.append((i,)) for j in jind: ijs.append((i,j)) ijs.append((j,i)) for ij in ijs: expr = MultiIndex(ij, {i:2,j:3}) d = compute_multiindex_hashdata(expr, {}) reprs.add(repr(expr)) hashes.add(hash(expr)) yield d c, d, r, h = self.compute_unique_hashdatas(hashdatas()) self.assertEqual(c, 3+9+9) self.assertEqual(d, 1+1) self.assertEqual(len(reprs), 3+9+9) self.assertEqual(len(hashes), 3+9+9) def test_multiindex_hashdata_depends_on_the_order_indices_are_observed(self): reprs = set() hashes = set() nrep = 3 def hashdatas(): for rep in range(nrep): # Resetting index_numbering for each repetition, # resulting in hashdata staying the same for # each repetition but repr and hashes changing # because new indices are created each repetition. index_numbering = {} i, j, k, l = indices(4) idims = {i:2,j:3,k:4,l:5} for expr in (MultiIndex((i,), idims), MultiIndex((i,), idims), # r MultiIndex((i,j), idims), MultiIndex((j,i), idims), MultiIndex((i,j), idims), # r MultiIndex((i,j,k), idims), MultiIndex((k,j,i), idims), MultiIndex((j,i), idims)): # r d = compute_multiindex_hashdata(expr, index_numbering) reprs.add(repr(expr)) hashes.add(hash(expr)) yield d c, d, r, h = self.compute_unique_hashdatas(hashdatas()) self.assertEqual(c, nrep*8) self.assertEqual(d, 5) self.assertEqual(len(reprs), nrep*5) self.assertEqual(len(hashes), nrep*5) def test_multiindex_hashdata_does_not_depend_on_index_dimension(self): # The index dimensions are always inferred from the # surrounding expression, and therefore don't need # to be included in the form signature. reprs = set() hashes = set() nrep = 3 def hashdatas(): for rep in range(nrep): index_numbering = {} i, j = indices(2) idims1 = {i:1,j:2} idims2 = {i:3,j:4} for expr in (MultiIndex((i,), idims1), MultiIndex((i,), idims2), MultiIndex((i,j), idims1), MultiIndex((i,j), idims2)): d = compute_multiindex_hashdata(expr, index_numbering) reprs.add(repr(expr)) hashes.add(hash(expr)) yield d c, d, r, h = self.compute_unique_hashdatas(hashdatas()) self.assertEqual(c, nrep*4) self.assertEqual(d, 2) self.assertEqual(len(reprs), nrep*4) self.assertEqual(len(hashes), nrep*4) class FormSignatureTestCase(UflTestCase): def check_unique_signatures(self, forms): count = 0 sigs = set() hashes = set() reprs = set() for a in forms: #sig = a.deprecated_signature() sig = compute_form_signature(a) sigs.add(sig) self.assertTrue(sig) hashes.add(hash(a)) reprs.add(repr(a)) count += 1 self.assertEqual(len(sigs), count) self.assertEqual(len(reprs), count) self.assertEqual(len(hashes), count) def test_signature_is_affected_by_element_properties(self): def forms(): for family in ("CG", "DG"): for cell in (triangle, tetrahedron, quadrilateral): for degree in (1,2): V = FiniteElement(family, cell, degree) u = Coefficient(V) v = TestFunction(V) x = cell.x w = as_vector([v]*x.shape()[0]) f = dot(w, u*x) a = f*dx yield a self.check_unique_signatures(forms()) def test_signature_is_affected_by_domains(self): def forms(): for cell in (cell2D, cell3D): for di in (1, 2): for dj in (1, 2): for dk in (1, 2): V = FiniteElement("CG", cell, 1) u = Coefficient(V) a = u*dx(di) + 2*u*dx(dj) + 3*u*ds(dk) yield a self.check_unique_signatures(forms()) def test_signature_of_forms_with_diff(self): def forms(): for cell in (cell2D, cell3D): for k in (1, 2, 3): V = FiniteElement("CG", cell, 1) W = VectorElement("CG", cell, 1) u = Coefficient(V) w = Coefficient(W) vu = variable(u) vw = variable(w) f = vu*dot(vw,vu**k*vw) g = diff(f, vu) h = dot(diff(f, vw), cell.n) a = f*dx(1) + g*dx(2) + h*ds(0) yield a self.check_unique_signatures(forms()) def test_signature_of_form_depend_on_coefficient_numbering_across_integrals(self): cell = cell2D V = FiniteElement("CG", cell, 1) f = Coefficient(V) g = Coefficient(V) M1 = f*dx(0) + g*dx(1) M2 = g*dx(0) + f*dx(1) M3 = g*dx(0) + g*dx(1) self.assertTrue(M1.deprecated_signature() != M2.deprecated_signature()) self.assertTrue(M1.deprecated_signature() != M3.deprecated_signature()) self.assertTrue(M2.deprecated_signature() != M3.deprecated_signature()) def test_signature_of_forms_change_with_operators(self): def forms(): for cell in (cell2D, cell3D): V = FiniteElement("CG", cell, 1) u = Coefficient(V) v = Coefficient(V) fs = [(u*v)+(u/v), (u+v)+(u/v), (u+v)*(u/v), (u*v)*(u*v), (u+v)*(u*v), # (!) same #(u*v)*(u+v), # (!) same (u*v)+(u+v), ] for f in fs: a = f*dx yield a self.check_unique_signatures(forms()) # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_simplify.py000077500000000000000000000102211226300046600165230ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl.classes import Sum, Product import math from ufl import * class SimplificationTestCase(UflTestCase): def xtest_zero_times_argument(self): # FIXME: Allow zero forms element = FiniteElement("CG", triangle, 1) v = TestFunction(element) u = TrialFunction(element) L = 0*v*dx a = 0*(u*v)*dx b = (0*u)*v*dx self.assertEqual(len(L.compute_form_data().arguments), 1) self.assertEqual(len(a.compute_form_data().arguments), 2) self.assertEqual(len(b.compute_form_data().arguments), 2) def test_divisions(self): element = FiniteElement("CG", triangle, 1) f = Coefficient(element) g = Coefficient(element) # Test simplification of division by 1 a = f b = f/1 self.assertEqual(a, b) # Test simplification of division by 1.0 a = f b = f/1.0 self.assertEqual(a, b) # Test simplification of division by of zero by something a = 0/f b = 0*f self.assertEqual(a, b) # Test simplification of division by self a = f/f b = 1 self.assertEqual(a, b) def test_products(self): element = FiniteElement("CG", triangle, 1) f = Coefficient(element) g = Coefficient(element) # Test simplification of basic multiplication a = f b = 1*f self.assertEqual(a, b) # Test simplification of self-multiplication a = f*f b = f**2 self.assertEqual(a, b) # Test simplification of flattened self-multiplication (may occur in algorithms) a = Product(f,f,f) b = f**3 self.assertEqual(a, b) # Test simplification of flattened self-multiplication (may occur in algorithms) a = Product(f,f,f,f) b = f**4 self.assertEqual(a, b) def test_sums(self): element = FiniteElement("CG", triangle, 1) f = Coefficient(element) g = Coefficient(element) # Test collapsing of basic sum a = f + f b = 2*f self.assertEqual(a, b) # Test collapsing of flattened sum (may occur in algorithms) a = Sum(f, f, f) b = 3*f self.assertEqual(a, b) a = Sum(f, f, f, f) b = 4*f self.assertEqual(a, b) # Test reordering of operands a = f + g b = g + f self.assertEqual(a, b) # Test reordering of operands and collapsing sum a = f + g + f # not collapsed, but ordered b = g + f + f # not collapsed, but ordered c = (g + f) + f # not collapsed, but ordered d = f + (f + g) # not collapsed, but ordered self.assertEqual(a, b) self.assertEqual(a, c) self.assertEqual(a, d) # Test reordering of operands and collapsing sum a = f + f + g # collapsed b = g + (f + f) # collapsed self.assertEqual(a, b) def test_mathfunctions(self): for i in (0.1, 0.3, 0.9): self.assertEqual(math.sin(i), sin(i)) self.assertEqual(math.cos(i), cos(i)) self.assertEqual(math.tan(i), tan(i)) self.assertEqual(math.sinh(i), sinh(i)) self.assertEqual(math.cosh(i), cosh(i)) self.assertEqual(math.tanh(i), tanh(i)) self.assertEqual(math.asin(i), asin(i)) self.assertEqual(math.acos(i), acos(i)) self.assertEqual(math.atan(i), atan(i)) self.assertEqual(math.exp(i), exp(i)) self.assertEqual(math.log(i), ln(i)) self.assertEqual(i, float(Max(i,i-1))) # TODO: Implement automatic simplification of conditionals? self.assertEqual(i, float(Min(i,i+1))) # TODO: Implement automatic simplification of conditionals? def test_indexing(self): u = VectorConstant(triangle) v = VectorConstant(triangle) A = outer(u,v) A2 = as_tensor(A[i,j], (i,j)) self.assertEqual(A2, A) Bij = u[i]*v[j] Bij2 = as_tensor(Bij, (i,j))[i,j] self.assertEqual(Bij2, Bij) if __name__ == "__main__": main() ufl-1.3.0/test/test_split.py000077500000000000000000000034261226300046600160330ustar00rootroot00000000000000#!/usr/bin/env python from ufl import * __authors__ = "Martin Sandve Alnes" __date__ = "2009-03-14 -- 2009-03-14" from ufltestcase import UflTestCase, main from ufl import * class SplitTestCase(UflTestCase): def test_split(self): cell = triangle d = cell.d f = FiniteElement("CG", cell, 1) v = VectorElement("CG", cell, 1) w = VectorElement("CG", cell, 1, dim=d+1) t = TensorElement("CG", cell, 1) s = TensorElement("CG", cell, 1, symmetry=True) r = TensorElement("CG", cell, 1, symmetry={(1,0): (0,1)}, shape=(d,d)) m = MixedElement(f, v, w, t, s, r) # Shapes of all these functions are correct: self.assertEqual((), Coefficient(f).shape()) self.assertEqual((d,), Coefficient(v).shape()) self.assertEqual((d+1,), Coefficient(w).shape()) self.assertEqual((d,d), Coefficient(t).shape()) self.assertEqual((d,d), Coefficient(s).shape()) self.assertEqual((d,d), Coefficient(r).shape()) self.assertEqual((3*d*d + 2*d + 2,), Coefficient(m).shape()) # sum of value sizes, not accounting for symmetries # Shapes of subelements are reproduced: g = Coefficient(m) s, = g.shape() for g2 in split(g): s -= product(g2.shape()) self.assertEqual(s, 0) # TODO: Should functions on mixed elements (vector+vector) be able to have tensor shape instead of vector shape? Think Marie wants this for BDM+BDM? v2 = MixedElement(v, v) m2 = MixedElement(t, t) #self.assertEqual(d, 2) #self.assertEqual((2,2), Coefficient(v2).shape()) self.assertEqual((d+d,), Coefficient(v2).shape()) self.assertEqual((2*d*d,), Coefficient(m2).shape()) if __name__ == "__main__": main() ufl-1.3.0/test/test_str.py000077500000000000000000000063041226300046600155060ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl import * from ufl.classes import * class TestStrOfLiterals(UflTestCase): def test_str_int_value(self): self.assertEqual(str(as_ufl(3)), "3") def test_str_float_value(self): self.assertEqual(str(as_ufl(3.14)), "3.14") def test_str_zero(self): x = triangle.x self.assertEqual(str(as_ufl(0)), "0") self.assertEqual(str(0*x), "(0<(2,), ()>)") # TODO: Not very nice... self.assertEqual(str(0*x*x[Index(42)]), "(0<(2,), (Index(42),)>)") # TODO: Not very nice... def test_str_index(self): self.assertEqual(str(Index(3)), "i_3") self.assertEqual(str(Index(42)), "i_{42}") class TestStrOfGeometricQuantities(UflTestCase): def test_str_coordinate(self): self.assertEqual(str(triangle.x), "x") self.assertEqual(str(triangle.x[0]), "(x)[0]") # FIXME: Get rid of extra () def test_str_normal(self): self.assertEqual(str(triangle.n), "n") self.assertEqual(str(triangle.n[0]), "(n)[0]") # FIXME: Get rid of extra () def test_str_circumradius(self): self.assertEqual(str(triangle.circumradius), "circumradius") # TODO: Use a shorter name? def test_str_cellsurfacearea(self): self.assertEqual(str(triangle.surface_area), "surfacearea") # TODO: Use a shorter name? def test_str_facetarea(self): self.assertEqual(str(triangle.facet_area), "facetarea") # TODO: Use a shorter name? def test_str_volume(self): self.assertEqual(str(triangle.volume), "volume") # TODO: Use a shorter name? class TestStrOfArguments(UflTestCase): def test_str_scalar_argument(self): v = TestFunction(FiniteElement("CG", triangle, 1)) u = TrialFunction(FiniteElement("CG", triangle, 1)) self.assertEqual(str(v), "v_{-2}") # FIXME self.assertEqual(str(u), "v_{-1}") # FIXME #def test_str_vector_argument(self): # FIXME #def test_str_scalar_coefficient(self): # FIXME #def test_str_vector_coefficient(self): # FIXME #def test_str_scalar_constant(self): # FIXME #def test_str_vector_constant(self): # FIXME class TestStrOfTensors(UflTestCase): def test_str_list_vector(self): x, y, z = tetrahedron.x v = as_vector((x, y, z)) self.assertEqual(str(v), "[%s, %s, %s]" % (x, y, z)) def test_str_list_vector_with_zero(self): x, y, z = tetrahedron.x v = as_vector((x, 0, 0)) self.assertEqual(str(v), "[%s, 0, 0]" % (x,)) def test_str_list_matrix(self): x, y = triangle.x v = as_matrix(((2*x, 3*y), (4*x, 5*y))) a = str(2*x) b = str(3*y) c = str(4*x) d = str(5*y) self.assertEqual(str(v), "[\n [%s, %s],\n [%s, %s]\n]" % (a, b, c, d)) def test_str_list_matrix_with_zero(self): x, y = triangle.x v = as_matrix(((2*x, 3*y), (0, 0))) a = str(2*x) b = str(3*y) c = str(as_vector((0,0))) self.assertEqual(str(v), "[\n [%s, %s],\n%s\n]" % (a, b, c)) # FIXME: Add more tests for tensors collapsing # partly or completely into Zero! if __name__ == "__main__": main() ufl-1.3.0/test/test_tensoralgebra.py000077500000000000000000000110101226300046600175140ustar00rootroot00000000000000#!/usr/bin/env python """ Test tensor algebra operators. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main # This imports everything external code will see from ufl from ufl import * class TensorAlgebraTestCase(UflTestCase): def setUp(self): super(TensorAlgebraTestCase, self).setUp() self.A = as_matrix([[2, 3], [4, 5]]) self.B = as_matrix([[6, 7], [8, 9]]) self.u = as_vector([10, 20]) self.v = as_vector([30, 40]) def assertEqualValues(self, A, B): B = as_ufl(B) self.assertEqual(A.shape(), B.shape()) self.assertEqual(inner(A-B, A-B)(None), 0) def test_repeated_as_tensor(self): A = as_tensor(self.A) B = as_matrix(self.B) u = as_tensor(self.u) v = as_vector(self.v) self.assertEqual(A, self.A) self.assertEqual(B, self.B) self.assertEqual(u, self.u) self.assertEqual(v, self.v) def test_outer(self): C = outer(self.u, self.v) D = as_matrix([[10*30, 10*40], [20*30, 20*40]]) self.assertEqualValues(C, D) C = outer(self.A, self.v) A, v = self.A, self.v dims = (0,1) D = as_tensor([[[self.A[i,j]*v[k] for k in dims] for j in dims] for i in dims]) self.assertEqualValues(C, D) # TODO: Test other ranks def test_inner(self): C = inner(self.A, self.B) D = 2*6 + 3*7 + 4*8 + 5*9 self.assertEqualValues(C, D) C = inner(self.u, self.v) D = 10*30 + 20*40 self.assertEqualValues(C, D) def test_pow2_inner(self): f = triangle.n[0] f2 = f**2 self.assertEqual(f2, inner(f, f)) u2 = self.u**2 self.assertEqual(u2, inner(self.u, self.u)) A2 = self.A**2 self.assertEqual(A2, inner(self.A, self.A)) # Only tensor**2 notation is supported: self.assertRaises(UFLException, lambda: self.A**3) def test_dot(self): C = dot(self.u, self.v) D = 10*30 + 20*40 self.assertEqualValues(C, D) C = dot(self.A, self.B) dims = (0,1) D = as_matrix([[sum(self.A[i,k]*self.B[k,j] for k in dims) \ for j in dims] for i in dims]) self.assertEqualValues(C, D) def test_cross(self): u = as_vector([3,3,3]) v = as_vector([2,2,2]) C = cross(u, v) D = zero(3) self.assertEqualValues(C, D) u = as_vector([3,3,0]) v = as_vector([-2,2,0]) C = cross(u, v) z = det(as_matrix([[3,3],[-2,2]])) D = as_vector([0,0,z]) self.assertEqualValues(C, D) def xtest_dev(self): C = dev(self.A) D = 0*C # FIXME: Add expected value here self.assertEqualValues(C, D) def test_skew(self): C = skew(self.A) A, dims = self.A, (0,1) D = 0.5*as_matrix([[A[i,j] - A[j,i] for j in dims] for i in dims]) self.assertEqualValues(C, D) def test_sym(self): C = sym(self.A) A, dims = self.A, (0,1) D = 0.5*as_matrix([[A[i,j] + A[j,i] for j in dims] for i in dims]) self.assertEqualValues(C, D) def test_transpose(self): C = transpose(self.A) dims = (0,1) D = as_matrix([[self.A[j,i] for j in dims] for i in dims]) self.assertEqualValues(C, D) def test_diag(self): dims = (0,1) C = diag(self.A) D = as_matrix([[(0 if i != j else self.A[i,i]) for j in dims] for i in dims]) self.assertEqualValues(C, D) C = diag(self.u) D = as_matrix([[(0 if i != j else self.u[i]) for j in dims] for i in dims]) self.assertEqualValues(C, D) def test_diag_vector(self): dims = (0,1) C = diag_vector(self.A) D = as_vector([self.A[i,i] for i in dims]) self.assertEqualValues(C, D) def test_tr(self): C = tr(self.A) A, dims = self.A, (0,1) D = sum(A[i,i] for i in dims) self.assertEqualValues(C, D) def xtest_det(self): C = det(self.A) D = zero() # FIXME: Add expected value here self.assertEqualValues(C, D) def xtest_cofac(self): C = cofac(self.A) D = 0*C # FIXME: Add expected value here self.assertEqualValues(C, D) def xtest_inv(self): C = inv(self.A) D = 0*C # FIXME: Add expected value here self.assertEqualValues(C, D) # Don't touch these lines, they allow you to run this file directly if __name__ == "__main__": main() ufl-1.3.0/test/test_transformations.py000077500000000000000000000030131226300046600201210ustar00rootroot00000000000000#!/usr/bin/env python from ufltestcase import UflTestCase, main from ufl import * #from ufl.classes import ... from ufl.algorithms import replace class TestTransformations(UflTestCase): def test_replace(self): V1 = FiniteElement("CG", triangle, 1) f1 = Coefficient(V1) g1 = Coefficient(V1) v1 = TestFunction(V1) u1 = TrialFunction(V1) a1 = f1 * g1 * u1 * v1 * dx V2 = FiniteElement("CG", triangle, 2) f2 = Coefficient(V2) g2 = Coefficient(V2) v2 = TestFunction(V2) u2 = TrialFunction(V2) a2 = f2 * g2 * u2 * v2 * dx mapping = { f1: f2, g1: g2, v1: v2, u1: u2, } b = replace(a1, mapping) self.assertEqual(b, a2) def test_replace_with_derivatives(self): V1 = FiniteElement("CG", triangle, 1) f1 = Coefficient(V1) g1 = Coefficient(V1) v1 = TestFunction(V1) u1 = TrialFunction(V1) a1 = u1.dx(0) * v1.dx(1) * (1 + dot(grad(f1), grad(g1))) * dx V2 = FiniteElement("CG", triangle, 2) f2 = Coefficient(V2) g2 = Coefficient(V2) v2 = TestFunction(V2) u2 = TrialFunction(V2) a2 = u2.dx(0) * v2.dx(1) * (1 + dot(0*grad(f2), grad(g2))) * dx mapping = { f1: 0, # zero! g1: g2, v1: v2, u1: u2, } b = replace(a1, mapping) self.assertEqual(b, a2) if __name__ == "__main__": main() ufl-1.3.0/test/test_utilities.py000077500000000000000000000102451226300046600167100ustar00rootroot00000000000000#!/usr/bin/env python """ Test internal utility functions. """ # These are thin wrappers on top of unittest.TestCase and unittest.main from ufltestcase import UflTestCase, main class UtilityTestCase(UflTestCase): def test_component_numbering(self): from ufl.permutation import build_component_numbering sh = (2,2) sm = { (1,0): (0,1) } v, s = build_component_numbering(sh, sm) assert v == {(0, 1): 1, (1, 0): 1, (0, 0): 0, (1, 1): 2} assert s == [(0, 0), (0, 1), (1, 1)] sh = (3,3) sm = { (1,0): (0,1), (2,0): (0,2), (2,1): (1,2) } v, s = build_component_numbering(sh, sm) assert v == {(0, 1): 1, (1, 2): 4, (0, 0): 0, (2, 1): 4, (1, 1): 3, (2, 0): 2, (2, 2): 5, (1, 0): 1, (0, 2): 2} assert s == [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)] def test_component_indexing(self): from ufl.common import strides, component_to_index, index_to_component # Scalar shape s = () self.assertEqual(strides(s), ()) c = () q = component_to_index(c, s) c2 = index_to_component(q, s) self.assertEqual(q, 0) self.assertEqual(c2, ()) # Vector shape s = (2,) self.assertEqual(strides(s), (1,)) for i in range(s[0]): c = (i,) q = component_to_index(c, s) c2 = index_to_component(q, s) #print c, q, c2 #self.assertEqual(FIXME) # Tensor shape s = (2,3) self.assertEqual(strides(s), (3,1)) for i in range(s[0]): for j in range(s[1]): c = (i,j) q = component_to_index(c, s) c2 = index_to_component(q, s) #print c, q, c2 #self.assertEqual(FIXME) # Rank 3 tensor shape s = (2,3,4) self.assertEqual(strides(s), (12,4,1)) for i in range(s[0]): for j in range(s[1]): for k in range(s[2]): c = (i,j,k) q = component_to_index(c, s) c2 = index_to_component(q, s) #print c, q, c2 #self.assertEqual(FIXME) # Taylor-Hood example: # pressure element is index 3: c = (3,) # get flat index: i = component_to_index(c, (4,)) # remove offset: i -= 3 # map back to component: c = index_to_component(i, ()) #print c #self.assertEqual(FIXME) # vector element y-component is index 1: c = (1,) # get flat index: i = component_to_index(c, (4,)) # remove offset: i -= 0 # map back to component: c = index_to_component(i, (3,)) #print c #self.assertEqual(FIXME) # Try a tensor/vector element: mixed_shape = (6,) ts = (2,2) vs = (2,) offset = 4 # vector element y-component is index offset+1: c = (offset+1,) # get flat index: i = component_to_index(c, mixed_shape) # remove offset: i -= offset # map back to vector component: c = index_to_component(i, vs) #print c #self.assertEqual(FIXME) for k in range(4): # tensor element (1,1)-component is index 3: c = (k,) # get flat index: i = component_to_index(c, mixed_shape) # remove offset: i -= 0 # map back to vector component: c = index_to_component(i, ts) #print c #self.assertEqual(FIXME) def test_stackdict(self): from ufl.common import StackDict d = StackDict(a=1) self.assertEqual(d["a"], 1) d.push("a", 2) self.assertEqual(d["a"], 2) d.push("a", 3) d.push("b", 9) self.assertEqual(d["a"], 3) self.assertEqual(d["b"], 9) d.pop() self.assertEqual(d["a"], 3) self.assertTrue("b" not in d) d.pop() self.assertEqual(d["a"], 2) d.pop() self.assertEqual(d["a"], 1) if __name__ == "__main__": main() ufl-1.3.0/test/ufltestcase.py000066400000000000000000000053001226300046600161510ustar00rootroot00000000000000#!/usr/bin/env python import unittest class CompatibilityTestCase(unittest.TestCase): def __init__(self, *args, **kwargs): super(CompatibilityTestCase, self).__init__(*args, **kwargs) def setUp(self): super(CompatibilityTestCase, self).setUp() def tearDown(self): super(CompatibilityTestCase, self).tearDown() ### Asserts available in TestCase from python 2.7: def _assertIsInstance(self, obj, cl, msg=None): self.assertTrue(isinstance(obj, cl), msg=None) def _assertNotIsInstance(self, obj, cl, msg=None): self.assertFalse(isinstance(obj, cl), msg=msg) def _assertIs(self, obj, cl, msg=None): self.assertTrue(obj is cl, msg=None) def _assertIsNot(self, obj, cl, msg=None): self.assertTrue(obj is not cl, msg=msg) def _assertIsNone(self, obj, msg=None): self.assertTrue(obj is None, msg=msg) def _assertGreater(self, lhs, rhs, msg=None): self.assertTrue(lhs > rhs, msg=msg) def _assertLess(self, lhs, rhs, msg=None): self.assertTrue(lhs < rhs, msg=msg) # Hack for different versions of python unittest: for func in ('assertIsInstance', 'assertNotIsInstance', 'assertIs', 'assertIsNot', 'assertIsNone', 'assertGreater', 'assertLess'): if not hasattr(CompatibilityTestCase, func): setattr(CompatibilityTestCase, func, getattr(CompatibilityTestCase, '_'+func)) class UflTestCase(CompatibilityTestCase): def __init__(self, *args, **kwargs): super(UflTestCase, self).__init__(*args, **kwargs) def setUp(self): super(UflTestCase, self).setUp() def tearDown(self): super(UflTestCase, self).tearDown() ### UFL specific asserts def assertSameIndices(self, expr, free_indices, msg=None): self.assertEqual(expr.free_indices(), free_indices, msg=msg) def assertSameShape(self, expr, shape, msg=None): self.assertEqual(expr.shape(), shape, msg=msg) def assertSameExprProps(self, expr, shape=None, free_indices=None, terminal=None, msg=None): if shape is not None: self.assertSameShape(expr, shape, msg=msg) if free_indices is not None: self.assertSameIndices(expr, free_indices, msg=msg) if terminal is not None: if terminal: self.assertIsInstance(expr, Terminal, msg=msg) else: self.assertIsInstance(expr, Operator, msg=msg) def main(*args, **kwargs): "Hook to do something before running single file tests." return unittest.main(*args, **kwargs) if __name__ == "__main__": print "Not to be run directly." print "Call main function from this module" print "in modules with test cases." ufl-1.3.0/ufl/000077500000000000000000000000001226300046600130665ustar00rootroot00000000000000ufl-1.3.0/ufl/__init__.py000066400000000000000000000231051226300046600152000ustar00rootroot00000000000000"""The Unified Form Language is an embedded domain specific language for definition of variational forms intended for finite element discretization. More precisely, it defines a fixed interface for choosing finite element spaces and defining expressions for weak forms in a notation close to mathematical notation. This Python module contains the language as well as algorithms to work with it. * To import the language, type:: from ufl import * * To import the underlying classes an UFL expression tree is built from, type:: from ufl.classes import * * Various algorithms for working with UFL expression trees can be found in:: from ufl.algorithms import * The classes and algorithms are considered implementation details and should not be used in form definitions. For more details on the language, see http://www.fenicsproject.org and http://arxiv.org/abs/1211.4047 The development version can be found in the repository at https://www.bitbucket.org/fenics-project/ufl A very brief overview of the language contents follows: * Domains:: Domain, Region * Cells:: Cell, interval, triangle, tetrahedron, quadrilateral, hexahedron, cell1D, cell2D, cell3D, * Elements:: FiniteElement, MixedElement, VectorElement, TensorElement EnrichedElement, RestrictedElement, TensorProductElement * Arguments:: Argument, TestFunction, TrialFunction * Coefficients:: Coefficient, Constant, VectorConstant, TensorConstant * Splitting form arguments in mixed spaces:: split * Literal constants:: Identity, PermutationSymbol * Geometric quantities:: SpatialCoordinate, FacetNormal, CellVolume, Circumradius, CellSurfaceArea, FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength, FacetDiameter, LocalCoordinate, GeometryJacobi, GeometryJacobiDeterminant, InverseGeometryJacobi * Indices:: Index, indices, i, j, k, l, p, q, r, s * Scalar to tensor expression conversion:: as_tensor, as_vector, as_matrix * Unit vectors and matrices:: unit_vector, unit_vectors, unit_matrix, unit_matrices * Tensor algebra operators:: outer, inner, dot, cross, perp, det, inv, cofac, transpose, tr, diag, diag_vector, dev, skew, sym * Elementwise tensor operators:: elem_mult, elem_div, elem_pow, elem_op * Differential operators:: variable, diff, grad, div, nabla_grad, nabla_div, Dx, Dn, curl, rot * Nonlinear functions:: Max, Min, abs, sign, sqrt, exp, ln, erf, cos, sin, tan, acos, asin, atan, atan_2, cosh, sinh, tanh, bessel_J, bessel_Y, bessel_I, bessel_K * Discontinuous Galerkin operators: jump, avg, v('+'), v('-'), cell_avg, facet_avg * Conditional operators:: eq, ne, le, ge, lt, gt, <, >, <=, >=, And, Or, Not, conditional * Integral measures:: dx, ds, dS, dP, dE, dc * Form transformations:: rhs, lhs, system, functional, replace, adjoint, action, energy_norm, sensitivity_rhs, derivative """ # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard, 2009, 2011 # Modified by Anders Logg, 2009. # Modified by Johannes Ring, 2014. # # Last changed: 2014-01-07 __version__ = "1.3.0" ########## README # Imports here should be what the user sees when doing "from ufl import *", # which means we should _not_ import f.ex. "Grad", but "grad". # This way we expose the language, the operation "grad", but less # of the implementation, the particular class "Grad". ########## # Utility functions (product is the counterpart of the built-in # python function sum, can be useful for users as well?) from ufl.common import product # Output control from ufl.log import get_handler, get_logger, set_handler, set_level, add_logfile, \ UFLException, DEBUG, INFO, WARNING, ERROR, CRITICAL # Types for geometric quantities from ufl.geometry import (Cell, ProductCell, SpatialCoordinate, FacetNormal, CellVolume, Circumradius, CellSurfaceArea, FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength, FacetDiameter, LocalCoordinate, GeometryJacobi, GeometryJacobiDeterminant, InverseGeometryJacobi) # Types for domain description from ufl.domains import Domain, Region # Finite elements classes from ufl.finiteelement import FiniteElementBase, FiniteElement, \ MixedElement, VectorElement, TensorElement, EnrichedElement, \ RestrictedElement, TensorProductElement # Hook to extend predefined element families from ufl.finiteelement.elementlist import register_element, show_elements #, ufl_elements # Arguments from ufl.argument import Argument, TestFunction, TrialFunction, \ Arguments, TestFunctions, TrialFunctions # Coefficients from ufl.coefficient import Coefficient, Coefficients, \ Constant, VectorConstant, TensorConstant # Split function from ufl.split_functions import split # Literal constants from ufl.constantvalue import PermutationSymbol, Identity, zero, as_ufl # Indexing of tensor expressions from ufl.indexing import Index, indices # Special functions for expression base classes # (ensure this is imported, since it attaches operators to Expr) import ufl.exproperators as __exproperators # Containers for expressions with value rank > 0 from ufl.tensors import as_tensor, as_vector, as_matrix, relabel from ufl.tensors import unit_vector, unit_vectors, unit_matrix, unit_matrices # Operators from ufl.operators import rank, shape, \ outer, inner, dot, cross, perp, \ det, inv, cofac, \ transpose, tr, diag, diag_vector, \ dev, skew, sym, \ sqrt, exp, ln, erf, \ cos, sin, tan, \ acos, asin, atan, atan_2, \ cosh, sinh, tanh, \ bessel_J, bessel_Y, bessel_I, bessel_K, \ eq, ne, le, ge, lt, gt, And, Or, Not, \ conditional, sign, Max, Min, \ variable, diff, \ Dx, grad, div, curl, rot, nabla_grad, nabla_div, Dn, exterior_derivative, \ jump, avg, cell_avg, facet_avg, \ elem_mult, elem_div, elem_pow, elem_op # Form class from ufl.form import Form # Integral classes from ufl.integral import Integral, Measure, register_domain_type, ProductMeasure # Representations of transformed forms from ufl.formoperators import replace, derivative, action, energy_norm, rhs, lhs,\ system, functional, adjoint, sensitivity_rhs #, dirichlet_functional # Predefined convenience objects from ufl.objects import \ vertex, interval, triangle, tetrahedron, \ quadrilateral, hexahedron, facet, cell1D, cell2D, cell3D, \ i, j, k, l, p, q, r, s, \ dx, ds, dS, dP, dE, dc # Useful constants from math import e, pi __all__ = [ 'product', 'get_handler', 'get_logger', 'set_handler', 'set_level', 'add_logfile', 'UFLException', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'Cell', 'ProductCell', 'SpatialCoordinate', 'FacetNormal', 'CellVolume', 'Circumradius', 'CellSurfaceArea', 'FacetArea', 'MinFacetEdgeLength', 'MaxFacetEdgeLength', 'FacetDiameter', 'LocalCoordinate', 'GeometryJacobi', 'GeometryJacobiDeterminant', 'InverseGeometryJacobi', 'Domain', 'Region', 'FiniteElementBase', 'FiniteElement', 'MixedElement', 'VectorElement', 'TensorElement', 'EnrichedElement', 'RestrictedElement', 'TensorProductElement', 'register_element', 'show_elements', 'Argument', 'TestFunction', 'TrialFunction', 'Arguments', 'TestFunctions', 'TrialFunctions', 'Coefficient', 'Coefficients', 'Constant', 'VectorConstant', 'TensorConstant', 'split', 'PermutationSymbol', 'Identity', 'zero', 'as_ufl', 'Index', 'indices', 'as_tensor', 'as_vector', 'as_matrix', 'relabel', 'unit_vector', 'unit_vectors', 'unit_matrix', 'unit_matrices', 'rank', 'shape', 'outer', 'inner', 'dot', 'cross', 'perp', 'det', 'inv', 'cofac', 'transpose', 'tr', 'diag', 'diag_vector', 'dev', 'skew', 'sym', 'sqrt', 'exp', 'ln', 'erf', 'cos', 'sin', 'tan', 'acos', 'asin', 'atan', 'atan_2', 'cosh', 'sinh', 'tanh', 'bessel_J', 'bessel_Y', 'bessel_I', 'bessel_K', 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', 'conditional', 'sign', 'Max', 'Min', 'variable', 'diff', 'Dx', 'grad', 'div', 'curl', 'rot', 'nabla_grad', 'nabla_div', 'Dn', 'exterior_derivative', 'jump', 'avg', 'cell_avg', 'facet_avg', 'elem_mult', 'elem_div', 'elem_pow', 'elem_op', 'Form', 'Integral', 'Measure', 'register_domain_type', 'ProductMeasure', 'replace', 'derivative', 'action', 'energy_norm', 'rhs', 'lhs', 'system', 'functional', 'adjoint', 'sensitivity_rhs', 'dx', 'ds', 'dS', 'dP', 'dE', 'dc', 'vertex', 'interval', 'triangle', 'tetrahedron', 'quadrilateral', 'hexahedron', 'facet', 'cell1D', 'cell2D', 'cell3D', 'i', 'j', 'k', 'l', 'p', 'q', 'r', 's', 'e', 'pi', ] ufl-1.3.0/ufl/algebra.py000066400000000000000000000363331226300046600150450ustar00rootroot00000000000000"Basic algebra operations." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-05-20 # Last changed: 2013-01-02 from itertools import chain from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.common import product, mergedicts, subdict, EmptyDict from ufl.expr import Expr from ufl.operatorbase import AlgebraOperator from ufl.constantvalue import Zero, zero, ScalarValue, IntValue, is_ufl_scalar, is_true_ufl_scalar, as_ufl from ufl.indexutils import unique_indices from ufl.sorting import sorted_expr from ufl.precedence import parstr #--- Algebraic operators --- class Sum(AlgebraOperator): __slots__ = ("_operands",) def __new__(cls, *operands): # TODO: This whole thing seems a bit complicated... Can it be simplified? Maybe we can merge some loops for efficiency? ufl_assert(operands, "Can't take sum of nothing.") #if not operands: # return Zero() # Allowing this leads to zeros with invalid type information in other places, need indices and shape # make sure everything is an Expr operands = [as_ufl(o) for o in operands] # Got one operand only? Do nothing then. if len(operands) == 1: return operands[0] # assert consistent tensor properties sh = operands[0].shape() fi = operands[0].free_indices() fid = operands[0].index_dimensions() #ufl_assert(all(sh == o.shape() for o in operands[1:]), # "Shape mismatch in Sum.") #ufl_assert(not any((set(fi) ^ set(o.free_indices())) for o in operands[1:]), # "Can't add expressions with different free indices.") if any(sh != o.shape() for o in operands[1:]): error("Shape mismatch in Sum.") if any((set(fi) ^ set(o.free_indices())) for o in operands[1:]): error("Can't add expressions with different free indices.") # sort operands in a canonical order operands = sorted_expr(operands) # purge zeros operands = [o for o in operands if not isinstance(o, Zero)] # sort scalars to beginning and merge them scalars = [o for o in operands if isinstance(o, ScalarValue)] if scalars: # exploiting Pythons built-in coersion rules f = as_ufl(sum(f._value for f in scalars)) nonscalars = [o for o in operands if not isinstance(o, ScalarValue)] if not nonscalars: return f if isinstance(f, Zero): operands = nonscalars else: operands = [f] + nonscalars # have we purged everything? if not operands: return Zero(sh, fi, fid) # left with one operand only? if len(operands) == 1: return operands[0] # Replace n-repeated operands foo with n*foo newoperands = [] op = operands[0] n = 1 for o in operands[1:] + [None]: if o == op: n += 1 else: newoperands.append(op if n == 1 else n*op) op = o n = 1 operands = newoperands # left with one operand only? if len(operands) == 1: return operands[0] # construct and initialize a new Sum object self = AlgebraOperator.__new__(cls) self._init(*operands) return self def _init(self, *operands): self._operands = operands def __init__(self, *operands): AlgebraOperator.__init__(self) def operands(self): return self._operands def free_indices(self): return self._operands[0].free_indices() def index_dimensions(self): return self._operands[0].index_dimensions() def shape(self): return self._operands[0].shape() def evaluate(self, x, mapping, component, index_values): return sum(o.evaluate(x, mapping, component, index_values) for o in self.operands()) def __str__(self): ops = [parstr(o, self) for o in self._operands] if False: # Implementation with line splitting: limit = 70 delimop = " + \\\n + " op = " + " s = ops[0] n = len(s) for o in ops[1:]: m = len(o) if n+m > limit: s += delimop n = m else: s += op n += m s += o return s # Implementation with no line splitting: return "%s" % " + ".join(ops) def __repr__(self): return "Sum(%s)" % ", ".join(repr(o) for o in self._operands) class Product(AlgebraOperator): """The product of two or more UFL objects.""" __slots__ = ("_operands", "_free_indices", "_index_dimensions",) def __new__(cls, *operands): # Make sure everything is an Expr operands = [as_ufl(o) for o in operands] # Make sure everything is scalar #ufl_assert(not any(o.shape() for o in operands), # "Product can only represent products of scalars.") if any(o.shape() for o in operands): error("Product can only represent products of scalars.") # No operands? Return one. if not operands: return IntValue(1) # Got one operand only? Just return it. if len(operands) == 1: return operands[0] # Got any zeros? Return zero. if any(isinstance(o, Zero) for o in operands): free_indices = unique_indices(tuple(chain(*(o.free_indices() for o in operands)))) index_dimensions = subdict(mergedicts([o.index_dimensions() for o in operands]), free_indices) return Zero((), free_indices, index_dimensions) # Merge scalars, but keep nonscalars sorted scalars = [] nonscalars = [] for o in operands: if isinstance(o, ScalarValue): scalars.append(o) else: nonscalars.append(o) if scalars: # merge scalars p = as_ufl(product(s._value for s in scalars)) # only scalars? if not nonscalars: return p # merged scalar is unity? if p == 1: scalars = [] # Left with one nonscalar operand only after merging scalars? if len(nonscalars) == 1: return nonscalars[0] else: scalars = [p] # Sort operands in a canonical order (NB! This is fragile! Small changes here can have large effects.) operands = scalars + sorted_expr(nonscalars) # Replace n-repeated operands foo with foo**n newoperands = [] op, nop = operands[0], 1 for o in operands[1:] + [None]: if o == op: # op is repeated, count number of repetitions nop += 1 else: if nop == 1: # op is not repeated newoperands.append(op) elif op.free_indices(): # We can't simplify products to powers if the operands has # free indices, because of complications in differentiation. # op repeated, but has free indices, so we don't simplify newoperands.extend([op]*nop) else: # op repeated, make it a power newoperands.append(op**nop) # Reset op as o op, nop = o, 1 operands = newoperands # Left with one operand only after simplifications? if len(operands) == 1: return operands[0] # Construct and initialize a new Product object self = AlgebraOperator.__new__(cls) self._init(*operands) return self def _init(self, *operands): "Constructor, called by __new__ with already checked arguments." # Store basic properties self._operands = operands # Extract indices self._free_indices = unique_indices(tuple(chain(*(o.free_indices() for o in operands)))) self._index_dimensions = mergedicts([o.index_dimensions() for o in operands]) or EmptyDict def __init__(self, *operands): AlgebraOperator.__init__(self) def operands(self): return self._operands def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return () def evaluate(self, x, mapping, component, index_values): ops = self.operands() sh = self.shape() if sh: ufl_assert(sh == ops[-1].shape(), "Expecting nonscalar product operand to be the last by convention.") tmp = ops[-1].evaluate(x, mapping, component, index_values) ops = ops[:-1] else: tmp = 1 for o in ops: tmp *= o.evaluate(x, mapping, (), index_values) return tmp def __str__(self): ops = [parstr(o, self) for o in self._operands] if False: # Implementation with line splitting: limit = 70 delimop = " * \\\n * " op = " * " s = ops[0] n = len(s) for o in ops[1:]: m = len(o) if n+m > limit: s += delimop n = m else: s += op n += m s += o return s # Implementation with no line splitting: return "%s" % " * ".join(ops) def __repr__(self): return "Product(%s)" % ", ".join(repr(o) for o in self._operands) class Division(AlgebraOperator): __slots__ = ("_a", "_b",) def __new__(cls, a, b): a = as_ufl(a) b = as_ufl(b) # Assertions # TODO: Enabled workaround for nonscalar division in __div__, # so maybe we can keep this assertion. Some algorithms may need updating. if not is_ufl_scalar(a): error("Expecting scalar nominator in Division.") if not is_true_ufl_scalar(b): error("Division by non-scalar is undefined.") if isinstance(b, Zero): error("Division by zero!") # Simplification a/b -> a if isinstance(a, Zero) or b == 1: return a # Simplification "literal a / literal b" -> "literal value of a/b" # Avoiding integer division by casting to float if isinstance(a, ScalarValue) and isinstance(b, ScalarValue): return as_ufl(float(a._value) / float(b._value)) # Simplification "a / a" -> "1" if not a.free_indices() and not a.shape() and a == b: return as_ufl(1) # construct and initialize a new Division object self = AlgebraOperator.__new__(cls) self._init(a, b) return self def _init(self, a, b): #ufl_assert(isinstance(a, Expr) and isinstance(b, Expr), "Expecting Expr instances.") if not (isinstance(a, Expr) and isinstance(b, Expr)): error("Expecting Expr instances.") self._a = a self._b = b def __init__(self, a, b): AlgebraOperator.__init__(self) def operands(self): return (self._a, self._b) def free_indices(self): return self._a.free_indices() def index_dimensions(self): return self._a.index_dimensions() def shape(self): return () # self._a.shape() def evaluate(self, x, mapping, component, index_values): a, b = self.operands() a = a.evaluate(x, mapping, component, index_values) b = b.evaluate(x, mapping, component, index_values) # Avoiding integer division by casting to float return float(a) / float(b) def __str__(self): return "%s / %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Division(%r, %r)" % (self._a, self._b) class Power(AlgebraOperator): __slots__ = ("_a", "_b",) def __new__(cls, a, b): a = as_ufl(a) b = as_ufl(b) if not is_true_ufl_scalar(a): error("Cannot take the power of a non-scalar expression.") if not is_true_ufl_scalar(b): error("Cannot raise an expression to a non-scalar power.") if isinstance(a, ScalarValue) and isinstance(b, ScalarValue): return as_ufl(a._value ** b._value) if a == 0 and isinstance(b, ScalarValue): bf = float(b) if bf < 0: error("Division by zero, annot raise 0 to a negative power.") else: return zero() if b == 1: return a if b == 0: return IntValue(1) # construct and initialize a new Power object self = AlgebraOperator.__new__(cls) self._init(a, b) return self def _init(self, a, b): #ufl_assert(isinstance(a, Expr) and isinstance(b, Expr), "Expecting Expr instances.") if not (isinstance(a, Expr) and isinstance(b, Expr)): error("Expecting Expr instances.") self._a = a self._b = b def __init__(self, a, b): AlgebraOperator.__init__(self) def operands(self): return (self._a, self._b) def free_indices(self): return self._a.free_indices() def index_dimensions(self): return self._a.index_dimensions() def shape(self): return () def evaluate(self, x, mapping, component, index_values): a, b = self.operands() a = a.evaluate(x, mapping, component, index_values) b = b.evaluate(x, mapping, component, index_values) return a**b def __str__(self): return "%s ** %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Power(%r, %r)" % (self._a, self._b) class Abs(AlgebraOperator): __slots__ = ("_a",) def __init__(self, a): AlgebraOperator.__init__(self) ufl_assert(isinstance(a, Expr), "Expecting Expr instance.") if not isinstance(a, Expr): error("Expecting Expr instances.") self._a = a def operands(self): return (self._a, ) def free_indices(self): return self._a.free_indices() def index_dimensions(self): return self._a.index_dimensions() def shape(self): return self._a.shape() def evaluate(self, x, mapping, component, index_values): a = self._a.evaluate(x, mapping, component, index_values) return abs(a) def __str__(self): return "| %s |" % parstr(self._a, self) def __repr__(self): return "Abs(%r)" % self._a ufl-1.3.0/ufl/algorithms/000077500000000000000000000000001226300046600152375ustar00rootroot00000000000000ufl-1.3.0/ufl/algorithms/__init__.py000066400000000000000000000130361226300046600173530ustar00rootroot00000000000000"This module collects algorithms and utility functions operating on UFL objects." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # # First added: 2008-08-14 # Last changed: 2011-10-11 # Function for preprocessing a form from ufl.algorithms.preprocess import preprocess, preprocess_expression, extract_common_cell # Class for simple extraction of form meta data from ufl.algorithms.formdata import FormData # Utilities for traversing over expression trees in different ways from ufl.algorithms.traversal import iter_expressions, traverse_terminals, \ post_traversal, pre_traversal, \ post_walk, pre_walk, walk # Utilities for extracting information from forms and expressions from ufl.algorithms.analysis import extract_classes, extract_type, has_type, \ extract_arguments, extract_coefficients, extract_arguments_and_coefficients, \ extract_elements, extract_unique_elements, \ extract_sub_elements, extract_unique_sub_elements, \ extract_variables, extract_duplications, \ extract_max_quadrature_element_degree, estimate_quadrature_degree, \ sort_elements # Utilities for checking properties of forms from ufl.algorithms.predicates import is_multilinear # Utilities for error checking of forms from ufl.algorithms.checks import validate_form # Utilites for modifying expressions and forms from ufl.algorithms.multifunction import MultiFunction from ufl.algorithms.transformer import Transformer, is_post_handler, \ transform, transform_integrands, apply_transformer, \ ReuseTransformer, ufl2ufl, \ CopyTransformer, ufl2uflcopy, \ VariableStripper, strip_variables from ufl.algorithms.replace import Replacer, replace from ufl.algorithms.expand_compounds import CompoundExpander, expand_compounds, \ CompoundExpanderPreDiff, expand_compounds_prediff, \ CompoundExpanderPostDiff, expand_compounds_postdiff from ufl.algorithms.estimate_degrees import SumDegreeEstimator, estimate_total_polynomial_degree from ufl.algorithms.argument_dependencies import ArgumentDependencyExtracter, extract_argument_dependencies, NotMultiLinearException from ufl.algorithms.deprecated import TreeFlattener, flatten, \ DuplicationMarker, mark_duplications, \ DuplicationPurger, purge_duplications from ufl.algorithms.renumbering import renumber_indices from ufl.algorithms.expand_indices import expand_indices, purge_list_tensors from ufl.algorithms.propagate_restrictions import propagate_restrictions # Utilities for transforming complete Forms into other Forms from ufl.algorithms.formtransformations import compute_form_adjoint, compute_form_action, compute_energy_norm, \ compute_form_lhs, compute_form_rhs, compute_form_functional, \ compute_form_arities # Utilities for Automatic Functional Differentiation from ufl.algorithms.ad import expand_derivatives #, compute_diff, propagate_spatial_derivatives, compute_form_derivative # Utilities for working with linearized computational graphs from ufl.algorithms.graph import Graph, format_graph, rebuild_tree, partition # TODO: Add more imports here # Utilities for tuple notation from ufl.algorithms.tuplenotation import as_form # Utilities for UFL object printing from ufl.algorithms.printing import integral_info, form_info, tree_format from ufl.algorithms.ufl2latex import ufl2latex, ufl2tex, ufl2pdf, forms2latexdocument from ufl.algorithms.ufl2dot import ufl2dot # Utilities for form file handling from ufl.algorithms.formfiles import read_ufl_file, load_ufl_file, load_forms # State of files (in the opinion of Martin): # traversal.py - Ok. # analysis.py - Ok, some unused stuff. # formdata.py - Probably don't need both self.unique_elements and self.sub_elements? # Need to improve quadrature order estimation. # graph.py - Work in progress, works ok so far. # predicates.py - is_multilinear seems ok but needs testing. # checks.py - Ok, more checks are welcome. # formfiles.py - Ok. # transformations.py - Ok. # formtransformations.py - Ok? Needs testing. # ad.py - Ok? # printing.py - Ok. # latextools.py - Ok. # ufl2latex.py - Fix precedence stuff. # ufl2dot.py - Rework with graph tools. ufl-1.3.0/ufl/algorithms/ad.py000066400000000000000000000102151226300046600161740ustar00rootroot00000000000000"""Front-end for AD routines.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # # First added: 2008-12-28 # Last changed: 2012-04-12 from ufl.log import debug, error from ufl.assertions import ufl_assert from ufl.classes import Terminal, Derivative from ufl.algorithms.transformer import transform_integrands, Transformer from ufl.algorithms.expand_compounds import expand_compounds, expand_compounds_postdiff from ufl.algorithms.forward_ad import apply_nested_forward_ad #class ADApplyer(Transformer): # def __init__(self, ad_routine): # Transformer.__init__(self) # self.ad_routine = ad_routine # # def terminal(self, e): # return e # # def expr(self, e, *ops): # return self.reuse_if_possible(e, *ops) # # def derivative(self, e, *ops): # return self.ad_routine(self.expr(e, *ops)) # #def apply_ad(e, ad_routine): # if isinstance(e, Terminal): # #print 'T apply_ad', e # return e # else: # #print 'O apply_ad', e # ops1 = e.operands() # ops2 = tuple(apply_ad(o, ad_routine) for o in ops1) # if not (ops1 == ops2): # e = e.reconstruct(*ops2) # if isinstance(e, Derivative): # #print 'apply_ad calling ad_routine', e # e = ad_routine(e) # return e def expand_derivatives(form, dim=None, apply_expand_compounds_before=True, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=False): """Expand all derivatives of expr. In the returned expression g which is mathematically equivalent to expr, there are no VariableDerivative or CoefficientDerivative objects left, and Grad objects have been propagated to Terminal nodes.""" # Find geometric dimension. This is messy because of PyDOLFIN integration issues. cell = form.cell() gdim = None if cell is None else cell.geometric_dimension() if dim is None: dim = gdim if gdim is not None: ufl_assert(dim == gdim, "Expecting dim to match the geometric dimension, "+\ "got dim=%r and gdim=%r." % (dim, gdim)) def _expand_derivatives(expression): #print '_expand_derivatives:', expression # Expand compound expressions or not, in the future this # should be removed from here and applied on the outside. if apply_expand_compounds_before: expression = expand_compounds(expression, dim) #print 'after expand_compounds', expression # Apply recursive forward mode AD expression = apply_nested_forward_ad(expression, dim) # FIXME: Form compilers assume expand_compounds have been applied. # This means quite a bit of work to handle all compounds # through the entire jit chain. For now, just test if we # can apply compounds afterwards, to focus on fixing issues # in the AD algorithm for compounds. Since this is optional, # alternative form compilers can then disable expand_compounds alltogether. if apply_expand_compounds_after: # FIXME: Test expand_compounds_postdiff, it should make this algorithm viable for existing FFC code #expression = expand_compounds(expression, dim) expression = expand_compounds_postdiff(expression, dim) return expression # Apply chosen algorithm to all integrands return transform_integrands(form, _expand_derivatives) ufl-1.3.0/ufl/algorithms/analysis.py000066400000000000000000000334511226300046600174420ustar00rootroot00000000000000"""Utility algorithms for inspection of and information extraction from UFL objects in various ways.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010. # Modified by Johan Hake, 2010. # # First added: 2008-03-14 # Last changed: 2013-01-02 from itertools import izip, chain from collections import namedtuple from ufl.log import error, warning, info from ufl.assertions import ufl_assert from ufl.sorting import topological_sorting from ufl.common import sorted_by_count from ufl.expr import Expr from ufl.terminal import Terminal, FormArgument from ufl.finiteelement import MixedElement, RestrictedElement from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.variable import Variable from ufl.indexing import Index, MultiIndex from ufl.domains import Region, Domain from ufl.integral import Measure, Integral from ufl.form import Form from ufl.algorithms.traversal import iter_expressions, post_traversal, post_walk, traverse_terminals # Domain types (should probably be listed somewhere else) _domain_types = Measure._domain_types_tuple #--- Utilities to extract information from an expression --- def extract_classes(a): """Build a set of all unique Expr subclasses used in a. The argument a can be a Form, Integral or Expr.""" return set(o._uflclass for e in iter_expressions(a) \ for o in post_traversal(e)) def extract_type(a, ufl_type): """Build a set of all objects of class ufl_type found in a. The argument a can be a Form, Integral or Expr.""" if issubclass(ufl_type, Terminal): return set(o for e in iter_expressions(a) \ for o in traverse_terminals(e) \ if isinstance(o, ufl_type)) return set(o for e in iter_expressions(a) \ for o in post_traversal(e) \ if isinstance(o, ufl_type)) def has_type(a, ufl_types): """Check if any class from ufl_types is found in a. The argument a can be a Form, Integral or Expr.""" if issubclass(ufl_types, Expr): ufl_types = (ufl_types,) if all(issubclass(ufl_type, Terminal) for ufl_type in ufl_types): return any(isinstance(o, ufl_types) \ for e in iter_expressions(a) \ for o in traverse_terminals(e)) return any(isinstance(o, ufl_types) \ for e in iter_expressions(a) \ for o in post_traversal(e)) def extract_terminals(a): "Build a set of all Terminal objects in a." return set(o for e in iter_expressions(a) \ for o in post_traversal(e) \ if isinstance(o, Terminal)) def extract_arguments(a): """Build a sorted list of all arguments in a, which can be a Form, Integral or Expr.""" return sorted_by_count(extract_type(a, Argument)) def extract_coefficients(a): """Build a sorted list of all coefficients in a, which can be a Form, Integral or Expr.""" return sorted_by_count(extract_type(a, Coefficient)) def extract_arguments_and_coefficients(a): """Build two sorted lists of all arguments and coefficients in a, which can be a Form, Integral or Expr.""" # This function is faster than extract_arguments + extract_coefficients # for large forms, and has more validation built in. # Extract lists of all form argument instances terminals = extract_type(a, FormArgument) arguments = [f for f in terminals if isinstance(f, Argument)] coefficients = [f for f in terminals if isinstance(f, Coefficient)] # Build count: instance mappings, should be one to one bfcounts = dict((f, f.count()) for f in arguments) fcounts = dict((f, f.count()) for f in coefficients) if len(bfcounts) != len(set(bfcounts.values())): msg = """\ Found different Arguments with same counts. Did you combine test or trial functions from different spaces? The Arguments found are:\n%s""" % "\n".join(" %s" % f for f in arguments) error(msg) if len(fcounts) != len(set(fcounts.values())): msg = """\ Found different coefficients with same counts. The arguments found are:\n%s""" % "\n".join(" %s" % f for f in coefficients) error(msg) # Passed checks, so we can safely sort the instances by count arguments = sorted_by_count(arguments) coefficients = sorted_by_count(coefficients) return arguments, coefficients def build_argument_replace_map(arguments, coefficients, element_mapping=None): """Create new Argument and Coefficient objects with count starting at 0. Return mapping from old to new objects, and lists of the new objects.""" if element_mapping is None: element_mapping = {} def remap(args): for (i, f) in enumerate(args): old_e = f.element() new_e = element_mapping.get(old_e, old_e) yield f.reconstruct(element=new_e, count=i) new_arguments = list(remap(arguments)) new_coefficients = list(remap(coefficients)) replace_map = dict(izip(chain(arguments, coefficients), chain(new_arguments, new_coefficients))) return replace_map, new_arguments, new_coefficients # alternative implementation, kept as an example: def _extract_coefficients(a): """Build a sorted list of all coefficients in a, which can be a Form, Integral or Expr.""" # build set of all unique coefficients s = set() def func(o): if isinstance(o, Coefficient): s.add(o) post_walk(a, func) # sort by count return sorted_by_count(s) def extract_elements(form): "Build sorted tuple of all elements used in form." args = chain(extract_arguments(form), extract_coefficients(form)) return tuple(f.element() for f in args) def extract_unique_elements(form): "Build sorted tuple of all unique elements used in form." return unique_tuple(extract_elements(form)) def extract_sub_elements(elements): "Build sorted tuple of all sub elements (including parent element)." sub_elements = tuple(chain(*[e.sub_elements() for e in elements])) if not sub_elements: return tuple(elements) return tuple(elements) + extract_sub_elements(sub_elements) def extract_unique_sub_elements(elements): "Build sorted tuple of all unique sub elements (including parent element)." return unique_tuple(extract_sub_elements(elements)) def extract_element_map(elements): "Build map from elements to element index in ordered tuple." element_map = {} unique_elements = unique_tuple(elements) for element in elements: indices = [i for (i, e) in enumerate(unique_elements) if e == element] ufl_assert(len(indices) == 1, "Unable to find unique index for element.") element_map[element] = i return element_map def extract_indices(expression): "Build a set of all Index objects used in expression." info("Is this used for anything? Doesn't make much sense.") multi_indices = extract_type(expression, MultiIndex) indices = set() for mi in multi_indices: indices.update(i for i in mi if isinstance(i, Index)) return indices def extract_variables(a): """Build a list of all Variable objects in a, which can be a Form, Integral or Expr. The ordering in the list obeys dependency order.""" handled = set() variables = [] for e in iter_expressions(a): for o in post_traversal(e): if isinstance(o, Variable): expr, label = o.operands() if not label in handled: variables.append(o) handled.add(label) return variables def extract_duplications(expression): "Build a set of all repeated expressions in expression." # TODO: Handle indices in a canonical way, maybe create a transformation that does this to apply before extract_duplications? ufl_assert(isinstance(expression, Expr), "Expecting UFL expression.") handled = set() duplicated = set() for o in post_traversal(expression): if o in handled: duplicated.add(o) handled.add(o) return duplicated def count_nodes(expr, ids=None): "Count the number of unique Expr instances in expression." i = id(expr) if ids is None: ids = set() elif i in ids: # Skip already visited subtrees return # Extend set with children recursively for o in expr.operands(): count_nodes(o, ids) ids.add(i) return len(ids) def extract_max_quadrature_element_degree(integral): """Extract quadrature integration order from quadrature elements in integral. Returns None if not found.""" quadrature_elements = [e for e in extract_elements(integral) if "Quadrature" in e.family()] degrees = [element.degree() for element in quadrature_elements] degrees = [q for q in degrees if not q is None] if not degrees: return None max_degree = quadrature_elements[0].degree() ufl_assert(all(max_degree == q for q in degrees), "Incompatible quadrature elements specified (orders must be equal).") return max_degree def estimate_quadrature_degree(integral): "Estimate the necessary quadrature order for integral using the sum of argument degrees." arguments = extract_arguments(integral) degrees = [v.element().degree() for v in arguments] if len(arguments) == 0: return None if len(arguments) == 1: return 2*degrees[0] return sum(degrees) def unique_tuple(objects): "Return sorted tuple of unique objects." unique_objects = [] for object in objects: if not object in unique_objects: unique_objects.append(object) return tuple(unique_objects) def extract_domain_data(form): "Extract the domain_data attached to integrals of each domain type in form." domain_data = {} for integral in form.integrals(): domain_type = integral.domain_type() data = integral.domain_data() # Check that there is only one domain_data object for each integral type existing_data = domain_data.get(domain_type) if existing_data is None: # Got no data before, store this one. May be None, that's fine. domain_data[domain_type] = data elif data is None: # Already got data, getting no data is ok but don't overwrite. pass elif existing_data is not data: # NB! Using 'is' because we're not assuming anything about domain_data type, not even an equals operator! error("Found two domain data objects for same domain type '%s', only one is allowed." % str(domain_type)) return domain_data def extract_num_sub_domains(form): "Extract the upper limit of sub domain ids for each domain type." num_domains = {} for integral in form.integrals(): domain_type = integral.domain_type() domain_id = integral.domain_id() # TODO: This may need some redesign max_domain_id = None if isinstance(domain_id, int): max_domain_id = domain_id elif isinstance(domain_id, Region): max_domain_id = max(domain_id.subdomain_ids()) elif isinstance(domain_id, Domain): max_domain_id = None if max_domain_id is not None: num_domains[domain_type] = max(num_domains.get(domain_type, 0), max_domain_id + 1) return num_domains class IntegralData(object): """Utility class with the members (domain_type, domain_id, integrals, metadata) where metadata is an empty dictionary that may be used for associating metadata with each object. """ __slots__ = ('domain_type', 'domain_id', 'integrals', 'metadata') def __init__(self, domain_type, domain_id, integrals, metadata): self.domain_type = domain_type self.domain_id = domain_id self.integrals = integrals self.metadata = metadata def __lt__(self, other): # To preserve behaviour of extract_integral_data: return ((self.domain_type, self.domain_id, self.integrals, self.metadata) < (other.domain_type, other.domain_id, other.integrals, other.metadata)) def __eq__(self, other): # Currently only used for tests: return (self.domain_type == other.domain_type and self.domain_id == other.domain_id and self.integrals == other.integrals and self.metadata == other.metadata) def __str__(self): return "IntegralData object over domain (%s, %s), with integrals:\n%s\nand metadata:\n%s" % ( self.domain_type, self.domain_id, '\n\n'.join(map(str,self.integrals)), self.metadata) def sort_elements(elements): """ Sort elements so that any sub elements appear before the corresponding mixed elements. This is useful when sub elements need to be defined before the corresponding mixed elements. The ordering is based on sorting a directed acyclic graph. """ # Set nodes nodes = elements # Set edges edges = dict((node, []) for node in nodes) for element in elements: for sub_element in element.sub_elements(): edges[element].append(sub_element) # Sort graph sorted_elements = topological_sorting(nodes, edges) # Reverse list of elements sorted_elements.reverse() return sorted_elements ufl-1.3.0/ufl/algorithms/argument_dependencies.py000066400000000000000000000124541226300046600221470ustar00rootroot00000000000000"""Algorithms for analysing argument dependencies in expressions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # # First added: 2008-05-07 # Last changed: 2012-04-12 from ufl.assertions import ufl_assert from ufl.classes import Expr from ufl.algorithms.transformer import Transformer class NotMultiLinearException(Exception): def __init__(self, *args, **kwargs): Exception.__init__(self, *args, **kwargs) class ArgumentDependencyExtracter(Transformer): def __init__(self): Transformer.__init__(self) self._empty = frozenset() def expr(self, o, *opdeps): "Default for nonterminals: nonlinear in all operands." for d in opdeps: if d: raise NotMultiLinearException, repr(o) return self._empty def terminal(self, o): "Default for terminals: no dependency on Arguments." return self._empty def variable(self, o): # Check variable cache to reuse previously transformed variable if possible e, l = o.operands() d = self._variable_cache.get(l) if d is None: # Visit the expression our variable represents d = self.visit(e) self._variable_cache[l] = d return d def argument(self, o): d = frozenset((o,)) return frozenset((d,)) def linear(self, o, a): "Nonterminals that are linear with a single argument." return a grad = linear div = linear curl = linear transposed = linear trace = linear skew = linear positive_restricted = linear negative_restricted = linear cell_avg = linear facet_avg = linear def indexed(self, o, f, i): return f def spatial_derivative(self, o, a, b): return a def variable_derivative(self, o, a, b): if b: raise NotMultiLinearException, repr(o) return a def component_tensor(self, o, f, i): return f def list_tensor(self, o, *opdeps): "Require same dependencies for all listtensor entries." d = opdeps[0] for d2 in opdeps[1:]: if not d == d2: raise NotMultiLinearException, repr(o) return d def conditional(self, o, cond, t, f): "Considering EQ, NE, LE, GE, LT, GT nonlinear in this context." if cond or (not t == f): raise NotMultiLinearException, repr(o) return t def division(self, o, a, b): "Arguments cannot be in the denominator." if b: raise NotMultiLinearException, repr(o) return a def index_sum(self, o, f, i): "Index sums inherit the dependencies of their summand." return f def sum(self, o, *opdeps): """Sums can contain both linear and bilinear terms (we could change this to require that all operands have the same dependencies).""" # convert frozenset to a mutable set deps = set(opdeps[0]) for d in opdeps[1:]: # d is a frozenset of frozensets deps.update(d) return frozenset(deps) def product(self, o, *opdeps): # Product operands should not depend on the same Arguments c = [] adeps, bdeps = opdeps # TODO: Generalize to any number of operands using permutations # for each frozenset ad in the frozenset adeps ufl_assert(isinstance(adeps, frozenset), "Type error") ufl_assert(isinstance(bdeps, frozenset), "Type error") ufl_assert(all(isinstance(ad, frozenset) for ad in adeps), "Type error") ufl_assert(all(isinstance(bd, frozenset) for bd in bdeps), "Type error") none = frozenset((None,)) noneset = frozenset((none,)) if not adeps: adeps = noneset if not bdeps: bdeps = noneset for ad in adeps: # for each frozenset bd in the frozenset bdeps for bd in bdeps: # build frozenset cd with the combined Argument dependencies from ad and bd cd = (ad | bd) - none # build frozenset cd with the combined Argument dependencies from ad and bd if not len(cd) == len(ad - none) + len(bd - none): raise NotMultiLinearException, repr(o) # remember this dependency combination if cd: c.append(cd) return frozenset(c) inner = product outer = product dot = product cross = product def extract_argument_dependencies(e): "Extract a set of sets of Arguments." ufl_assert(isinstance(e, Expr), "Expecting an Expr.") return ArgumentDependencyExtracter().visit(e) ufl-1.3.0/ufl/algorithms/checks.py000066400000000000000000000114011226300046600170460ustar00rootroot00000000000000"""Functions to check the validity of forms.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # Modified by Mehdi Nikbakht, 2010. # # First added: 2008-03-14 # Last changed: 2012-04-12 from ufl.log import warning, error # UFL classes from ufl.form import Form from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.constantvalue import is_true_ufl_scalar from ufl.integral import Measure # UFL algorithms from ufl.algorithms.traversal import iter_expressions, traverse_terminals from ufl.algorithms.propagate_restrictions import check_restrictions def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception? """Performs all implemented validations on a form. Raises exception if something fails.""" errors = [] warnings = [] if not isinstance(form, Form): msg = "Validation failed, not a Form:\n%s" % repr(form) error(msg) #errors.append(msg) #return errors # FIXME: Add back check for multilinearity # Check that form is multilinear #if not is_multilinear(form): # errors.append("Form is not multilinear in arguments.") # FIXME DOMAIN: Add check for consistency between domains somehow domains = set(t.domain() for e in iter_expressions(form) for t in traverse_terminals(e)) - set((None,)) if not domains: errors.append("Missing domain definition in form.") top_domains = set(dom.top_domain() for dom in domains if dom is not None) if not top_domains: errors.append("Missing domain definition in form.") elif len(top_domains) > 1: warnings.append("Multiple top domain definitions in form: %s" % str(top_domains)) # Check that cell is the same everywhere cells = set(dom.cell() for dom in top_domains) - set((None,)) if not cells: errors.append("Missing cell definition in form.") elif len(cells) > 1: errors.append("Multiple cell definitions in form: %s" % str(cells)) # Check that no Coefficient or Argument instance # have the same count unless they are the same coefficients = {} arguments = {} for e in iter_expressions(form): for f in traverse_terminals(e): if isinstance(f, Coefficient): c = f.count() if c in coefficients: g = coefficients[c] if not f is g: errors.append("Found different Coefficients with " + \ "same count: %s and %s." % (repr(f), repr(g))) else: coefficients[c] = f elif isinstance(f, Argument): c = f.count() if c in arguments: g = arguments[c] if not f is g: if c == -2: msg = "TestFunctions" elif c == -1: msg = "TrialFunctions" else: msg = "Arguments with same count" msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g)) errors.append(msg) else: arguments[c] = f # Check that all integrands are scalar for expression in iter_expressions(form): if not is_true_ufl_scalar(expression): errors.append("Found non-scalar integrand expression:\n%s\n%s" % \ (str(expression), repr(expression))) # Check that restrictions are permissible for integral in form.integrals(): # Only allow restricitions on interior facet integrals and surface measures if integral.measure().domain_type() in (Measure.INTERIOR_FACET, Measure.SURFACE): check_restrictions(integral.integrand(), True) else: check_restrictions(integral.integrand(), False) # Raise exception with all error messages # TODO: Return errors list instead, need to collect messages from all validations above first. if errors: final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join(errors) error(final_msg) ufl-1.3.0/ufl/algorithms/deprecated.py000066400000000000000000000124051226300046600177130ustar00rootroot00000000000000""" Stuff in this file will probably be removed. """ from ufl.log import error, warning from ufl.algorithms.transformer import ReuseTransformer, apply_transformer from ufl.classes import Variable class TreeFlattener(ReuseTransformer): def __init__(self): ReuseTransformer.__init__(self) def sum_or_product(self, o, *ops): c = o._uflclass operands = [] for b in ops: if isinstance(b, c): operands.extend(b.operands()) else: operands.append(b) return o.reconstruct(*operands) sum = sum_or_product product = sum_or_product def flatten(e): # TODO: Fix or remove! Maybe this works better now with IndexSum marking implicit summations. """Convert an UFL expression to a new UFL expression, with sums and products flattened from binary tree nodes to n-ary tree nodes.""" warning("flatten doesn't work correctly for some indexed products, like (u[i]*v[i])*(q[i]*r[i])") return apply_transformer(e, TreeFlattener()) #class OperatorApplier(ReuseTransformer): # "Implements mappings that can be defined through Python operators." # def __init__(self): # ReuseTransformer.__init__(self) # # def abs(self, o, a): # return abs(a) # # def sum(self, o, *ops): # return sum(ops) # # def division(self, o, a, b): # return a / b # # def power(self, o, a, b): # return a ** b # # def product(self, o, *ops): # return product(ops) # # def indexed(self, o, a, b): # return a[*b] if isinstance(b, tuple) else a[b] # TODO: Indices will often mess up extract_duplications / mark_duplications. # Can we renumber indices consistently from the leaves to avoid that problem? # This may introduce many ComponentTensor/Indexed objects for relabeling of indices though. # We probably need some kind of pattern matching to make this effective. # That's another step towards a complete symbolic library... # # What this does do well is insert Variables around subexpressions that the # user actually identified manually in his code like in "a = ...; b = a*(1+a)", # and expressions without indices (prior to expand_compounds). class DuplicationMarker(ReuseTransformer): def __init__(self, duplications): ReuseTransformer.__init__(self) self._duplications = duplications self._expr2variable = {} def expr(self, o, *ops): v = self._expr2variable.get(o) if v is None: oo = o # reconstruct if necessary if not ops == o.operands(): o = o._uflclass(*ops) if (oo in self._duplications) or (o in self._duplications): v = Variable(o) self._expr2variable[o] = v self._expr2variable[oo] = v else: v = o return v def wrap_terminal(self, o): v = self._expr2variable.get(o) if v is None: if o in self._duplications: v = Variable(o) self._expr2variable[o] = v else: v = o return v argument = wrap_terminal coefficient = wrap_terminal constant = wrap_terminal facet_normal = wrap_terminal def variable(self, o): e, l = o.operands() v = self._expr2variable.get(e) if v is None: e2 = self.visit(e) # Unwrap expression from the newly created Variable wrapper # unless the original expression was a Variable, in which # case we possibly need to keep the label for correctness. if (not isinstance(e, Variable)) and isinstance(e2, Variable): e2 = e2._expression v = self._expr2variable.get(e2) if v is None: v = Variable(e2, l) self._expr2variable[e] = v self._expr2variable[e2] = v return v from ufl.algorithms.analysis import extract_duplications def mark_duplications(e): """Wrap subexpressions that are equal (completely equal, not mathematically equivalent) in Variable objects to facilitate subexpression reuse.""" duplications = extract_duplications(e) return apply_transformer(e, DuplicationMarker(duplications)) class DuplicationPurger(ReuseTransformer): "Replace all duplicated nodes from an UFL Expr." def __init__(self): ReuseTransformer.__init__(self) self._handled = {} #self._duplications = set() def expr(self, x, *ops): # Check cache e = self._handled.get(x) if e is None: # Reuse or reconstruct if ops == x.operands(): e = x else: e = x._uflclass(*ops) # Update cache self._handled[x] = e #else: # self._duplications.add(e) assert repr(x) == repr(e) return e def terminal(self, x): e = self._handled.get(x) if e is None: # Reuse e = x # Update cache self._handled[x] = e #else: # self._duplications.add(e) return e def purge_duplications(e): """Replace any subexpressions in expression that occur more than once with a single instance.""" return apply_transformer(e, DuplicationPurger()) ufl-1.3.0/ufl/algorithms/domain_analysis.py000066400000000000000000000172001226300046600207630ustar00rootroot00000000000000"""Algorithm sketch to build canonical data structure for integrals over subdomains.""" #from ufl import * from ufl import Domain, Region, Measure, Form # Transitional helper constructor from ufl.integral import Integral from ufl.common import sorted_items def integral_domain_ids(integral): did = integral.measure().domain_id() if isinstance(did, int): return (did,) elif isinstance(did, tuple): return did elif isinstance(did, Region): return did.subdomain_ids() elif isinstance(did, Domain): return Measure.DOMAIN_ID_EVERYWHERE elif did in Measure.DOMAIN_ID_CONSTANTS: return did else: error("Invalid domain id %s." % did) # Tuple comparison helper from collections import defaultdict from ufl.sorting import cmp_expr from ufl.sorting import sorted_expr class ExprTupleKey(object): __slots__ = ('x',) def __init__(self, x): self.x = x def __lt__(self, other): c = cmp_expr(self.x[0], other.x[0]) if c < 0: return True elif c > 0: return False else: # NB! Comparing form compiler data here! Assuming this is an ok operation. return self.x[1] < other.x[1] def expr_tuple_key(expr): return ExprTupleKey(expr) def extract_domain_data_from_integral_list(integrals): # Keep track of domain data objects, want only one ddids = set() domain_data = None for itg in integrals: dd = itg.domain_data() if dd is not None: domain_data = dd ddids.add(id(dd)) assert len(ddids) <= 1, ("Found multiple domain data objects in form for domain type %s" % dt) return domain_data def extract_domain_data_from_integral_dict(integrals): # TODO: Is this really any better than the existing extract_domain_data? domain_data = {} # Iterate over domain types in order for dt in Measure._domain_types_tuple: # Get integrals list for this domain type if any if dt in integrals: domain_data[dt] = extract_domain_data_from_integral_list(integrals[dt]) return domain_data def integral_dict_to_sub_integral_data(integrals): # Data structures to return sub_integral_data = {} # Iterate over domain types in order for dt in Measure._domain_types_tuple: # Get integrals list for this domain type if any itgs = integrals.get(dt) if itgs is not None: # Make dict for this domain type with mapping (subdomain id -> integral list) sub_integrals = build_sub_integral_list(itgs) # Build a canonical representation of integrals for this type of domain, # with only one integrand for each compiler_data on each subdomain sub_integral_data[dt] = canonicalize_sub_integral_data(sub_integrals) # Return result: #sub_integral_data[dt][did][:] = [(integrand0, compiler_data0), (integrand1, compiler_data1), ...] return sub_integral_data def reconstruct_form_from_sub_integral_data(sub_integral_data, domain_data=None): domain_data = domain_data or {} integrals = [] # Iterate over domain types in order for dt in Measure._domain_types_tuple: dd = domain_data.get(dt) # Get integrals list for this domain type if any metaintegrands = sub_integral_data.get(dt) if metaintegrands is not None: for k in sorted(metaintegrands.keys()): for integrand, compiler_data in metaintegrands[k]: integrals.append(Integral(integrand, dt, k, compiler_data, dd)) return Form(integrals) def build_sub_integral_list(itgs): sub_integrals = defaultdict(list) # Fill sub_integrals with lists of integrals sorted by and restricted to subdomain ids for itg in itgs: dids = integral_domain_ids(itg) assert dids != Measure.DOMAIN_ID_OTHERWISE if dids == Measure.DOMAIN_ID_EVERYWHERE: # Everywhere integral sub_integrals[Measure.DOMAIN_ID_EVERYWHERE].append(itg) else: # Region or single subdomain id for did in dids: # Restrict integral to this subdomain! sub_integrals[did].append(itg.reconstruct(domain_description=did)) # Add everywhere integrals to each single subdomain id integral list if Measure.DOMAIN_ID_EVERYWHERE in sub_integrals: # We'll consume everywhere integrals... ei = sub_integrals[Measure.DOMAIN_ID_EVERYWHERE] del sub_integrals[Measure.DOMAIN_ID_EVERYWHERE] # ... and produce otherwise integrals instead assert Measure.DOMAIN_ID_OTHERWISE not in sub_integrals sub_integrals[Measure.DOMAIN_ID_OTHERWISE] = [] # Restrict everywhere integral to each subdomain and append to each integral list for did, itglist in sorted_items(sub_integrals): for itg in ei: # Restrict integral to this subdomain! itglist.append(itg.reconstruct(domain_description=did)) return sub_integrals def canonicalize_sub_integral_data(sub_integrals): for did in sub_integrals: # Group integrals by compiler data object id by_cdid = {} for itg in sub_integrals[did]: cd = itg.compiler_data() cdid = id(cd) if cdid in by_cdid: by_cdid[cdid][0].append(itg) else: by_cdid[cdid] = ([itg], cd) # Accumulate integrands separately for each compiler data object id for cdid in by_cdid: integrals, cd = by_cdid[cdid] # Ensure canonical sorting of more than two integrands integrands = sorted_expr((itg.integrand() for itg in integrals)) integrands_sum = sum(integrands[1:], integrands[0]) by_cdid[cdid] = (integrands_sum, cd) # Sort integrands canonically by integrand first then compiler data sub_integrals[did] = sorted(by_cdid.values(), key=expr_tuple_key) # i.e. the result is on the form: #sub_integrals[did][:] = [(integrand0, compiler_data0), (integrand1, compiler_data1), ...] # where integrand0 < integrand1 by the canonical ufl expression ordering criteria. return sub_integrals # Convert to integral_data format during transitional period: from ufl.algorithms.analysis import IntegralData def convert_sub_integral_data_to_integral_data(sub_integral_data): integral_data = [] for domain_type, domain_type_data in sorted_items(sub_integral_data): for domain_id, sub_domain_integrands in sorted_items(domain_type_data): integrals = [Integral(integrand, domain_type, domain_id, compiler_data, None) for integrand, compiler_data in sub_domain_integrands] ida = IntegralData(domain_type, domain_id, integrals, {}) integral_data.append(ida) return integral_data # Print output for inspection: def print_sub_integral_data(sub_integral_data): print for domain_type, domain_type_data in sorted_items(sub_integral_data): print "======", domain_type for domain_id, sub_domain_integrands in sorted_items(domain_type_data): print '---', domain_id, for integrand, compiler_data in sub_domain_integrands: print print "integrand: ", integrand print "compiler data:", compiler_data def extract_integral_data_from_integral_dict(integrals): sub_integral_data = integral_dict_to_sub_integral_data(integrals) if 0: print_sub_integral_data(sub_integral_data) # TODO: Replace integral_data with this through ufl and ffc integral_data = convert_sub_integral_data_to_integral_data(sub_integral_data) return integral_data ufl-1.3.0/ufl/algorithms/elementtransformations.py000066400000000000000000000042161226300046600224170ustar00rootroot00000000000000# Copyright (C) 2012 Marie E. Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2011-01-17 # Last changed: 2011-06-02 from ufl.assertions import ufl_assert from ufl.finiteelement import FiniteElement, MixedElement def change_regularity(element, family): """ For a given finite element, return the corresponding space specified by 'family'. """ n = element.num_sub_elements() if n > 0: subs = element.sub_elements() return MixedElement([change_regularity(subs[i], family) for i in range(n)]) shape = element.value_shape() if not shape: return FiniteElement(family, element.domain(), element.degree()) ufl_assert(len(shape) == 1, "TODO: Update this code to handle tensor elements.") return MixedElement([FiniteElement(family, element.domain(i), element.degree(i)) for i in range(shape[0])]) def tear(V): "For a finite element, return the corresponding discontinuous element." W = change_regularity(V, "DG") return W def increase_order(element): "Return element of same family, but a polynomial degree higher." ufl_assert(len(element.value_shape()) <= 1, "TODO: Update this code to handle tensor elements.") n = element.num_sub_elements() if n > 0: subs = element.sub_elements() return MixedElement([increase_order(subs[i]) for i in range(n)]) if element.family() == "Real": return element return FiniteElement(element.family(), element.domain(), element.degree()+1) ufl-1.3.0/ufl/algorithms/estimate_degrees.py000066400000000000000000000175151226300046600211330ustar00rootroot00000000000000"""Algorithms for estimating polynomial degrees of expressions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # Modified by Jan Blechta, 2012 # # First added: 2008-05-07 # Last changed: 2012-11-04 from ufl.assertions import ufl_assert from ufl.log import warning from ufl.form import Form from ufl.integral import Integral from ufl.algorithms.transformer import Transformer class SumDegreeEstimator(Transformer): "This algorithm is exact for a few operators and heuristic for many." def __init__(self, default_degree, element_replace_map): Transformer.__init__(self) self.default_degree = default_degree self.element_replace_map = element_replace_map def constant_value(self, v): "Constant values are constant. Duh." return 0 def geometric_quantity(self, v): "Most geometric quantities are cellwise constant." ufl_assert(v.is_cellwise_constant(), "Missing handler for non-constant geometry type %s." % v._uflclass.__name__) return 0 def spatial_coordinate(self, v): "A coordinate provides one additional degree." return 1 def local_coordinate(self, v): "A coordinate provides one additional degree." return 1 def form_argument(self, v): """A form argument provides a degree depending on the element, or the default degree if the element has no degree.""" e = v.element() e = self.element_replace_map.get(e,e) d = e.degree() # FIXME: Use component to improve accuracy return self.default_degree if d is None else d def _reduce_degree(self, v, f): return max(f - 1, 0) def _add_degrees(self, v, *ops): return sum(ops) def _max_degrees(self, v, *ops): return max(ops + (0,)) def _not_handled(self, v, *args): error("Missing degree handler for type %s" % v._uflclass.__name__) def expr(self, v, *ops): "For most operators we take the max degree of its operands." warning("Missing degree estimation handler for type %s" % v._uflclass.__name__) return self._add_degrees(v, *ops) # Utility types with no degree concept def multi_index(self, v): return None def label(self, v): return None # Fall-through, indexing and similar types def variable(self, v, e, l): return e def transposed(self, v, A): return A def index_sum(self, v, A, ii): return A def indexed(self, v, A, ii): return A def component_tensor(self, v, A, ii): return A list_tensor = _max_degrees def positive_restricted(self, v, a): return a def negative_restricted(self, v, a): return a # A sum takes the max degree of its operands: sum = _max_degrees # A spatial derivative reduces the degree with one grad = _reduce_degree # Handling these types although they should not occur... please apply preprocessing before using this algorithm: nabla_grad = _reduce_degree div = _reduce_degree nabla_div = _reduce_degree curl = _reduce_degree def cell_avg(self, v, a): "Cell average of a function is always cellwise constant." return 0 def facet_avg(self, v, a): "Facet average of a function is always cellwise constant." return 0 # A product accumulates the degrees of its operands: product = _add_degrees # Handling these types although they should not occur... please apply preprocessing before using this algorithm: inner = _add_degrees dot = _add_degrees outer = _add_degrees cross = _add_degrees # Explicitly not handling these types, please apply preprocessing before using this algorithm: derivative = _not_handled # base type compound_derivative = _not_handled # base type compound_tensor_operator = _not_handled # base class variable_derivative = _not_handled trace = _not_handled determinant = _not_handled cofactor = _not_handled inverse = _not_handled deviatoric = _not_handled skew = _not_handled sym = _not_handled def abs(self, v, a): "This is a heuristic, correct if there is no " if a == 0: return a else: return a def division(self, v, *ops): "Using the sum here is a heuristic. Consider e.g. (x+1)/(x-1)." return sum(ops) def power(self, v, a, b): """If b is an integer: degree(a**b) == degree(a)*b otherwise use the heuristic degree(a**b) == degree(a)*2""" f, g = v.operands() try: gi = abs(int(g)) return a*gi except: pass # Something to a non-integer power, this is just a heuristic with no background return a*2 def atan_2(self, v, a, b): """Using the heuristic degree(atan2(const,const)) == 0 degree(atan2(a,b)) == max(degree(a),degree(b))+2 which can be wildly inaccurate but at least gives a somewhat high integration degree. """ #print "estimate",a,b if a or b: return max(a,b)+2 else: return max(a,b) def math_function(self, v, a): """Using the heuristic degree(sin(const)) == 0 degree(sin(a)) == degree(a)+2 which can be wildly inaccurate but at least gives a somewhat high integration degree. """ if a: return a+2 else: return a def bessel_function(self, v, nu, x): """Using the heuristic degree(bessel_*(const)) == 0 degree(bessel_*(x)) == degree(x)+2 which can be wildly inaccurate but at least gives a somewhat high integration degree. """ if x: return x+2 else: return x def condition(self, v, *args): return None def conditional(self, v, c, t, f): """Degree of condition does not influence degree of values which conditional takes. So heuristicaly taking max of true degree and false degree. This will be exact in cells where condition takes single value. For improving accuracy of quadrature near condition transition surface quadrature order must be adjusted manually.""" return max(t, f) def estimate_total_polynomial_degree(e, default_degree=1, element_replace_map={}): """Estimate total polynomial degree of integrand. NB! Although some compound types are supported here, some derivatives and compounds must be preprocessed prior to degree estimation. In generic code, this algorithm should only be applied after preprocessing. For coefficients defined on an element with unspecified degree (None), the degree is set to the given default degree. """ de = SumDegreeEstimator(default_degree, element_replace_map) if isinstance(e, Form): ufl_assert(e.integrals(), "Got form with no integrals!") degrees = [de.visit(integral.integrand()) for integral in e.integrals()] elif isinstance(e, Integral): degrees = [de.visit(e.integrand())] else: degrees = [de.visit(e)] return max(degrees + [0]) ufl-1.3.0/ufl/algorithms/expand_compounds.py000066400000000000000000000365101226300046600211640ustar00rootroot00000000000000"""Algorithm for expanding compound expressions into equivalent representations using basic operators.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # # First added: 2008-05-07 # Last changed: 2012-04-12 from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.classes import Product, Index, Zero, FormArgument, Grad from ufl.indexing import indices, complete_shape from ufl.tensors import as_tensor, as_matrix, as_vector from ufl.algorithms.transformer import Transformer, ReuseTransformer, apply_transformer # Note: To avoid typing errors, the expressions for cofactor and # deviatoric parts below were created with the script # tensoralgebrastrings.py under sandbox/scripts/ class CompoundExpander(ReuseTransformer): "Expands compound expressions to equivalent representations using basic operators." def __init__(self, geometric_dimension): ReuseTransformer.__init__(self) self._dim = geometric_dimension #if self._dim is None: # warning("Got None for dimension, some compounds cannot be expanded.") # ------------ Compound tensor operators def trace(self, o, A): i = Index() return A[i,i] def transposed(self, o, A): i, j = indices(2) return as_tensor(A[i, j], (j, i)) def _square_matrix_shape(self, A): sh = A.shape() if self._dim is not None: sh = complete_shape(sh, self._dim) # FIXME: Does this ever happen? Pretty sure other places assume shapes are always "complete". ufl_assert(sh[0] == sh[1], "Expecting square matrix.") ufl_assert(sh[0] is not None, "Unknown dimension.") return sh def deviatoric(self, o, A): sh = self._square_matrix_shape(A) if sh[0] == 2: return as_matrix([[-1./2*A[1,1]+1./2*A[0,0],A[0,1]],[A[1,0],1./2*A[1,1]-1./2*A[0,0]]]) elif sh[0] == 3: return as_matrix([[-1./3*A[1,1]-1./3*A[2,2]+2./3*A[0,0],A[0,1],A[0,2]],[A[1,0],2./3*A[1,1]-1./3*A[2,2]-1./3*A[0,0],A[1,2]],[A[2,0],A[2,1],-1./3*A[1,1]+2./3*A[2,2]-1./3*A[0,0]]]) error("dev(A) not implemented for dimension %s." % sh[0]) def skew(self, o, A): i, j = indices(2) return as_matrix( (A[i,j] - A[j,i]) / 2, (i,j) ) def sym(self, o, A): i, j = indices(2) return as_matrix( (A[i,j] + A[j,i]) / 2, (i,j) ) def cross(self, o, a, b): def c(i, j): return Product(a[i],b[j]) - Product(a[j],b[i]) return as_vector((c(1,2), c(2,0), c(0,1))) def dot(self, o, a, b): ai = indices(a.rank()-1) bi = indices(b.rank()-1) k = indices(1) # Create an IndexSum over a Product s = a[ai+k]*b[k+bi] return as_tensor(s, ai+bi) def inner(self, o, a, b): ufl_assert(a.rank() == b.rank()) ii = indices(a.rank()) # Create multiple IndexSums over a Product s = a[ii]*b[ii] return s def outer(self, o, a, b): ii = indices(a.rank()) jj = indices(b.rank()) # Create a Product with no shared indices s = a[ii]*b[jj] return as_tensor(s, ii+jj) def determinant(self, o, A): sh = self._square_matrix_shape(A) def det2D(B, i, j, k, l): return B[i,k]*B[j,l]-B[i,l]*B[j,k] if len(sh) == 0: return A if sh[0] == 2: return det2D(A, 0, 1, 0, 1) if sh[0] == 3: return A[0,0]*det2D(A, 1, 2, 1, 2) + \ A[0,1]*det2D(A, 1, 2, 2, 0) + \ A[0,2]*det2D(A, 1, 2, 0, 1) # TODO: Implement generally for all dimensions? error("Determinant not implemented for dimension %d." % self._dim) def cofactor(self, o, A): # TODO: Find common subexpressions here. # TODO: Better implementation? sh = self._square_matrix_shape(A) if sh[0] == 2: return as_matrix([[A[1,1],-A[1,0]],[-A[0,1],A[0,0]]]) elif sh[0] == 3: return as_matrix([ [A[1,1]*A[2,2] - A[2,1]*A[1,2],A[2,0]*A[1,2] - A[1,0]*A[2,2], - A[2,0]*A[1,1] + A[1,0]*A[2,1]], [A[2,1]*A[0,2] - A[0,1]*A[2,2],A[0,0]*A[2,2] - A[2,0]*A[0,2], - A[0,0]*A[2,1] + A[2,0]*A[0,1]], [A[0,1]*A[1,2] - A[1,1]*A[0,2],A[1,0]*A[0,2] - A[0,0]*A[1,2], - A[1,0]*A[0,1] + A[0,0]*A[1,1]] ]) elif sh[0] == 4: return as_matrix([ [-A[3,1]*A[2,2]*A[1,3] - A[3,2]*A[2,3]*A[1,1] + A[1,3]*A[3,2]*A[2,1] + A[3,1]*A[2,3]*A[1,2] + A[2,2]*A[1,1]*A[3,3] - A[3,3]*A[2,1]*A[1,2], -A[1,0]*A[2,2]*A[3,3] + A[2,0]*A[3,3]*A[1,2] + A[2,2]*A[1,3]*A[3,0] - A[2,3]*A[3,0]*A[1,2] + A[1,0]*A[3,2]*A[2,3] - A[1,3]*A[3,2]*A[2,0], A[1,0]*A[3,3]*A[2,1] + A[2,3]*A[1,1]*A[3,0] - A[2,0]*A[1,1]*A[3,3] - A[1,3]*A[3,0]*A[2,1] - A[1,0]*A[3,1]*A[2,3] + A[3,1]*A[1,3]*A[2,0], A[3,0]*A[2,1]*A[1,2] + A[1,0]*A[3,1]*A[2,2] + A[3,2]*A[2,0]*A[1,1] - A[2,2]*A[1,1]*A[3,0] - A[3,1]*A[2,0]*A[1,2] - A[1,0]*A[3,2]*A[2,1]], [ A[3,1]*A[2,2]*A[0,3] + A[0,2]*A[3,3]*A[2,1] + A[0,1]*A[3,2]*A[2,3] - A[3,1]*A[0,2]*A[2,3] - A[0,1]*A[2,2]*A[3,3] - A[3,2]*A[0,3]*A[2,1], -A[2,2]*A[0,3]*A[3,0] - A[0,2]*A[2,0]*A[3,3] - A[3,2]*A[2,3]*A[0,0] + A[2,2]*A[3,3]*A[0,0] + A[0,2]*A[2,3]*A[3,0] + A[3,2]*A[2,0]*A[0,3], A[3,1]*A[2,3]*A[0,0] - A[0,1]*A[2,3]*A[3,0] - A[3,1]*A[2,0]*A[0,3] - A[3,3]*A[0,0]*A[2,1] + A[0,3]*A[3,0]*A[2,1] + A[0,1]*A[2,0]*A[3,3], A[3,2]*A[0,0]*A[2,1] - A[0,2]*A[3,0]*A[2,1] + A[0,1]*A[2,2]*A[3,0] + A[3,1]*A[0,2]*A[2,0] - A[0,1]*A[3,2]*A[2,0] - A[3,1]*A[2,2]*A[0,0]], [ A[3,1]*A[1,3]*A[0,2] - A[0,2]*A[1,1]*A[3,3] - A[3,1]*A[0,3]*A[1,2] + A[3,2]*A[1,1]*A[0,3] + A[0,1]*A[3,3]*A[1,2] - A[0,1]*A[1,3]*A[3,2], A[1,3]*A[3,2]*A[0,0] - A[1,0]*A[3,2]*A[0,3] - A[1,3]*A[0,2]*A[3,0] + A[0,3]*A[3,0]*A[1,2] + A[1,0]*A[0,2]*A[3,3] - A[3,3]*A[0,0]*A[1,2], -A[1,0]*A[0,1]*A[3,3] + A[0,1]*A[1,3]*A[3,0] - A[3,1]*A[1,3]*A[0,0] - A[1,1]*A[0,3]*A[3,0] + A[1,0]*A[3,1]*A[0,3] + A[1,1]*A[3,3]*A[0,0], A[0,2]*A[1,1]*A[3,0] - A[3,2]*A[1,1]*A[0,0] - A[0,1]*A[3,0]*A[1,2] - A[1,0]*A[3,1]*A[0,2] + A[3,1]*A[0,0]*A[1,2] + A[1,0]*A[0,1]*A[3,2]], [ A[0,3]*A[2,1]*A[1,2] + A[0,2]*A[2,3]*A[1,1] + A[0,1]*A[2,2]*A[1,3] - A[2,2]*A[1,1]*A[0,3] - A[1,3]*A[0,2]*A[2,1] - A[0,1]*A[2,3]*A[1,2], A[1,0]*A[2,2]*A[0,3] + A[1,3]*A[0,2]*A[2,0] - A[1,0]*A[0,2]*A[2,3] - A[2,0]*A[0,3]*A[1,2] - A[2,2]*A[1,3]*A[0,0] + A[2,3]*A[0,0]*A[1,2], -A[0,1]*A[1,3]*A[2,0] + A[2,0]*A[1,1]*A[0,3] + A[1,3]*A[0,0]*A[2,1] - A[1,0]*A[0,3]*A[2,1] + A[1,0]*A[0,1]*A[2,3] - A[2,3]*A[1,1]*A[0,0], A[1,0]*A[0,2]*A[2,1] - A[0,2]*A[2,0]*A[1,1] + A[0,1]*A[2,0]*A[1,2] + A[2,2]*A[1,1]*A[0,0] - A[1,0]*A[0,1]*A[2,2] - A[0,0]*A[2,1]*A[1,2]] ]) error("Cofactor not implemented for dimension %s." % sh[0]) def _cofactor_transposed(self, o, A): sh = self._square_matrix_shape(A) if sh[0] == 2: return as_matrix([[A[1,1], -A[0,1]], [-A[1,0], A[0,0]]]) elif sh[0] == 3: return as_matrix([ \ [ A[2,2]*A[1,1] - A[1,2]*A[2,1], -A[0,1]*A[2,2] + A[0,2]*A[2,1], A[0,1]*A[1,2] - A[0,2]*A[1,1]], [-A[2,2]*A[1,0] + A[1,2]*A[2,0], -A[0,2]*A[2,0] + A[2,2]*A[0,0], A[0,2]*A[1,0] - A[1,2]*A[0,0]], [ A[1,0]*A[2,1] - A[2,0]*A[1,1], A[0,1]*A[2,0] - A[0,0]*A[2,1], A[0,0]*A[1,1] - A[0,1]*A[1,0]] \ ]) elif sh[0] == 4: # TODO: Find common subexpressions here. # TODO: Better implementation? return as_matrix([ \ [-A[3,3]*A[2,1]*A[1,2] + A[1,2]*A[3,1]*A[2,3] + A[1,1]*A[3,3]*A[2,2] - A[3,1]*A[2,2]*A[1,3] + A[2,1]*A[1,3]*A[3,2] - A[1,1]*A[3,2]*A[2,3], -A[3,1]*A[0,2]*A[2,3] + A[0,1]*A[3,2]*A[2,3] - A[0,3]*A[2,1]*A[3,2] + A[3,3]*A[2,1]*A[0,2] - A[3,3]*A[0,1]*A[2,2] + A[0,3]*A[3,1]*A[2,2], A[3,1]*A[1,3]*A[0,2] + A[1,1]*A[0,3]*A[3,2] - A[0,3]*A[1,2]*A[3,1] - A[0,1]*A[1,3]*A[3,2] + A[3,3]*A[1,2]*A[0,1] - A[1,1]*A[3,3]*A[0,2], A[1,1]*A[0,2]*A[2,3] - A[2,1]*A[1,3]*A[0,2] + A[0,3]*A[2,1]*A[1,2] - A[1,2]*A[0,1]*A[2,3] - A[1,1]*A[0,3]*A[2,2] + A[0,1]*A[2,2]*A[1,3]], [ A[3,3]*A[1,2]*A[2,0] - A[3,0]*A[1,2]*A[2,3] + A[1,0]*A[3,2]*A[2,3] - A[3,3]*A[1,0]*A[2,2] - A[1,3]*A[3,2]*A[2,0] + A[3,0]*A[2,2]*A[1,3], A[0,3]*A[3,2]*A[2,0] - A[0,3]*A[3,0]*A[2,2] + A[3,3]*A[0,0]*A[2,2] + A[3,0]*A[0,2]*A[2,3] - A[0,0]*A[3,2]*A[2,3] - A[3,3]*A[0,2]*A[2,0], -A[3,3]*A[0,0]*A[1,2] + A[0,0]*A[1,3]*A[3,2] - A[3,0]*A[1,3]*A[0,2] + A[3,3]*A[1,0]*A[0,2] + A[0,3]*A[3,0]*A[1,2] - A[0,3]*A[1,0]*A[3,2], A[0,3]*A[1,0]*A[2,2] + A[1,3]*A[0,2]*A[2,0] - A[0,0]*A[2,2]*A[1,3] - A[0,3]*A[1,2]*A[2,0] + A[0,0]*A[1,2]*A[2,3] - A[1,0]*A[0,2]*A[2,3]], [ A[3,1]*A[1,3]*A[2,0] + A[3,3]*A[2,1]*A[1,0] + A[1,1]*A[3,0]*A[2,3] - A[1,0]*A[3,1]*A[2,3] - A[3,0]*A[2,1]*A[1,3] - A[1,1]*A[3,3]*A[2,0], A[3,3]*A[0,1]*A[2,0] - A[3,3]*A[0,0]*A[2,1] - A[0,3]*A[3,1]*A[2,0] - A[3,0]*A[0,1]*A[2,3] + A[0,0]*A[3,1]*A[2,3] + A[0,3]*A[3,0]*A[2,1], -A[0,0]*A[3,1]*A[1,3] + A[0,3]*A[1,0]*A[3,1] - A[3,3]*A[1,0]*A[0,1] + A[1,1]*A[3,3]*A[0,0] - A[1,1]*A[0,3]*A[3,0] + A[3,0]*A[0,1]*A[1,3], A[0,0]*A[2,1]*A[1,3] + A[1,0]*A[0,1]*A[2,3] - A[0,3]*A[2,1]*A[1,0] + A[1,1]*A[0,3]*A[2,0] - A[1,1]*A[0,0]*A[2,3] - A[0,1]*A[1,3]*A[2,0]], [-A[1,2]*A[3,1]*A[2,0] - A[2,1]*A[1,0]*A[3,2] + A[3,0]*A[2,1]*A[1,2] - A[1,1]*A[3,0]*A[2,2] + A[1,0]*A[3,1]*A[2,2] + A[1,1]*A[3,2]*A[2,0], -A[3,0]*A[2,1]*A[0,2] - A[0,1]*A[3,2]*A[2,0] + A[3,1]*A[0,2]*A[2,0] - A[0,0]*A[3,1]*A[2,2] + A[3,0]*A[0,1]*A[2,2] + A[0,0]*A[2,1]*A[3,2], A[0,0]*A[1,2]*A[3,1] - A[1,0]*A[3,1]*A[0,2] + A[1,1]*A[3,0]*A[0,2] + A[1,0]*A[0,1]*A[3,2] - A[3,0]*A[1,2]*A[0,1] - A[1,1]*A[0,0]*A[3,2], -A[1,1]*A[0,2]*A[2,0] + A[2,1]*A[1,0]*A[0,2] + A[1,2]*A[0,1]*A[2,0] + A[1,1]*A[0,0]*A[2,2] - A[1,0]*A[0,1]*A[2,2] - A[0,0]*A[2,1]*A[1,2]] \ ]) error("Cofactor not implemented for dimension %s." % sh[0]) def inverse(self, o, A): if A.rank() == 0: return 1.0 / A return self._cofactor_transposed(None, A) / self.determinant(None, A) # ------------ Compound differential operators def div(self, o, a): i = Index() return a[...,i].dx(i) def grad(self, o, a): return self.reuse_if_possible(o, a) def nabla_div(self, o, a): i = Index() return a[i,...].dx(i) def nabla_grad(self, o, a): j = Index() if a.rank() > 0: ii = tuple(indices(a.rank())) return as_tensor(a[ii].dx(j), (j,) + ii) else: return as_tensor(a.dx(j), (j,)) def curl(self, o, a): # o = curl a = "[a.dx(1), -a.dx(0)]" if a.shape() == () # o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.shape() == (2,) # o = curl a = "cross(nabla, a)" if a.shape() == (3,) def c(i, j): return a[j].dx(i) - a[i].dx(j) sh = a.shape() if sh == (): return as_vector((a.dx(1), -a.dx(0))) if sh == (2,): return c(0,1) if sh == (3,): return as_vector((c(1,2), c(2,0), c(0,1))) error("Invalid shape %s of curl argument." % (sh,)) """ FIXME: Make expand_compounds_prediff skip types that we make work in expand_derivatives, one by one, and optionally use it instead of expand_compounds from expand_derivatives. """ class CompoundExpanderPreDiff(CompoundExpander): def __init__(self, dim): CompoundExpander.__init__(self, dim) #inner = Transformer.reuse_if_possible #dot = Transformer.reuse_if_possible def grad(self, o, a): return self.reuse_if_possible(o, a) def nabla_grad(self, o, a): r = o.rank() ii = indices(r) jj = ii[-1:] + ii[:-1] return as_tensor(Grad(a)[ii], jj) def div(self, o, a): i = Index() return Grad(a)[...,i,i] def nabla_div(self, o, a): i = Index() return Grad(a)[i,...,i] def curl(self, o, a): # o = curl a = "[a.dx(1), -a.dx(0)]" if a.shape() == () # o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.shape() == (2,) # o = curl a = "cross(nabla, a)" if a.shape() == (3,) Da = Grad(a) def c(i, j): #return a[j].dx(i) - a[i].dx(j) return Da[j,i] - Da[i,j] sh = a.shape() if sh == (): #return as_vector((a.dx(1), -a.dx(0))) return as_vector((Da[1], -Da[0])) if sh == (2,): return c(0,1) if sh == (3,): return as_vector((c(1,2), c(2,0), c(0,1))) error("Invalid shape %s of curl argument." % (sh,)) class CompoundExpanderPostDiff(CompoundExpander): def __init__(self, dim): CompoundExpander.__init__(self, dim) def nabla_grad(self, o, a, i): error("This should not happen.") def div(self, o, a, i): error("This should not happen.") def nabla_div(self, o, a, i): error("This should not happen.") def curl(self, o, a, i): error("This should not happen.") def expand_compounds1(e, dim=None): """Expand compound objects into basic operators. Requires e to have a well defined geometric dimension.""" if dim is None: cell = e.cell() dim = None if cell is None else cell.geometric_dimension() return apply_transformer(e, CompoundExpander(dim)) def expand_compounds2(e, dim=None): """Expand compound objects into basic operators. Requires e to have a well defined geometric dimension.""" if dim is None: cell = e.cell() dim = None if cell is None else cell.geometric_dimension() return expand_compounds_postdiff(expand_compounds_prediff(e, dim), dim) def expand_compounds_prediff(e, dim=None): """Expand compound objects into basic operators. Requires e to have a well defined geometric dimension.""" if dim is None: cell = e.cell() dim = None if cell is None else cell.geometric_dimension() return apply_transformer(e, CompoundExpanderPreDiff(dim)) def expand_compounds_postdiff(e, dim=None): """Expand compound objects into basic operators. Requires e to have a well defined geometric dimension.""" if dim is None: cell = e.cell() dim = None if cell is None else cell.geometric_dimension() return apply_transformer(e, CompoundExpanderPostDiff(dim)) expand_compounds = expand_compounds1 ufl-1.3.0/ufl/algorithms/expand_indices.py000066400000000000000000000161011226300046600205650ustar00rootroot00000000000000"""This module defines expression transformation utilities, for expanding free indices in expressions to explicit fixed indices only.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # # First added: 2008-04-19 # Last changed: 2012-04-12 from itertools import izip from ufl.log import error from ufl.common import Stack, StackDict from ufl.assertions import ufl_assert from ufl.finiteelement import TensorElement from ufl.classes import Expr, Terminal, ListTensor, IndexSum, Indexed, FormArgument from ufl.tensors import as_tensor, ComponentTensor from ufl.permutation import compute_indices from ufl.constantvalue import Zero from ufl.indexing import Index, FixedIndex, MultiIndex from ufl.differentiation import Grad from ufl.algorithms.graph import Graph from ufl.algorithms.transformer import ReuseTransformer, apply_transformer, transform_integrands from ufl.algorithms.analysis import has_type class IndexExpander(ReuseTransformer): """...""" def __init__(self): ReuseTransformer.__init__(self) self._components = Stack() self._index2value = StackDict() def component(self): "Return current component tuple." if self._components: return self._components.peek() return () def terminal(self, x): if x.shape(): c = self.component() ufl_assert(len(x.shape()) == len(c), "Component size mismatch.") return x[c] return x def form_argument(self, x): sh = x.shape() if sh == (): return x else: e = x.element() r = len(sh) # Get component c = self.component() ufl_assert(r == len(c), "Component size mismatch.") # Map it through an eventual symmetry mapping s = e.symmetry() c = s.get(c, c) ufl_assert(r == len(c), "Component size mismatch after symmetry mapping.") return x[c] def zero(self, x): ufl_assert(len(x.shape()) == len(self.component()), "Component size mismatch.") s = set(x.free_indices()) - set(self._index2value.keys()) if s: error("Free index set mismatch, these indices have no value assigned: %s." % str(s)) # There is no index/shape info in this zero because that is asserted above return Zero() def scalar_value(self, x): if len(x.shape()) != len(self.component()): self.print_visit_stack() ufl_assert(len(x.shape()) == len(self.component()), "Component size mismatch.") s = set(x.free_indices()) - set(self._index2value.keys()) if s: error("Free index set mismatch, these indices have no value assigned: %s." % str(s)) return x._uflclass(x.value()) def division(self, x): a, b = x.operands() # Not accepting nonscalars in division anymore ufl_assert(a.shape() == (), "Not expecting tensor in division.") ufl_assert(self.component() == (), "Not expecting component in division.") ufl_assert(b.shape() == (), "Not expecting division by tensor.") a = self.visit(a) #self._components.push(()) b = self.visit(b) #self._components.pop() return self.reuse_if_possible(x, a, b) def index_sum(self, x): ops = [] summand, multiindex = x.operands() index, = multiindex # TODO: For the list tensor purging algorithm, do something like: # if index not in self._to_expand: # return self.expr(x, *[self.visit(o) for o in x.operands()]) for value in range(x.dimension()): self._index2value.push(index, value) ops.append(self.visit(summand)) self._index2value.pop() return sum(ops) def _multi_index(self, x): comp = [] for i in x: if isinstance(i, FixedIndex): comp.append(i._value) elif isinstance(i, Index): comp.append(self._index2value[i]) return tuple(comp) def multi_index(self, x): return MultiIndex(self._multi_index(x), {}) def indexed(self, x): A, ii = x.operands() # Push new component built from index value map self._components.push(self._multi_index(ii)) # Hide index values (doing this is not correct behaviour) #for i in ii: # if isinstance(i, Index): # self._index2value.push(i, None) result = self.visit(A) # Un-hide index values #for i in ii: # if isinstance(i, Index): # self._index2value.pop() # Reset component self._components.pop() return result def component_tensor(self, x): # This function evaluates the tensor expression # with indices equal to the current component tuple expression, indices = x.operands() ufl_assert(expression.shape() == (), "Expecting scalar base expression.") # Update index map with component tuple values comp = self.component() ufl_assert(len(indices) == len(comp), "Index/component mismatch.") for i, v in izip(indices._indices, comp): self._index2value.push(i, v) self._components.push(()) # Evaluate with these indices result = self.visit(expression) # Revert index map for _ in comp: self._index2value.pop() self._components.pop() return result def list_tensor(self, x): # Pick the right subtensor and subcomponent c = self.component() c0, c1 = c[0], c[1:] op = x.operands()[c0] # Evaluate subtensor with this subcomponent self._components.push(c1) r = self.visit(op) self._components.pop() return r def grad(self, x): f, = x.operands() ufl_assert(isinstance(f, (Terminal, Grad)), "Expecting expand_derivatives to have been applied.") # No need to visit child as long as it is on the form [Grad]([Grad](terminal)) return x[self.component()] def expand_indices(e): return apply_transformer(e, IndexExpander()) def purge_list_tensors(e): """Get rid of all ListTensor instances by expanding expressions to use their components directly. Will usually increase the size of the expression.""" if has_type(e, ListTensor): return expand_indices(e) # TODO: Only expand what's necessary to get rid of list tensors return e ufl-1.3.0/ufl/algorithms/formdata.py000066400000000000000000000114631226300046600174130ustar00rootroot00000000000000"""FormData class easy for collecting of various data about a form.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008. # # First added: 2008-09-13 # Last changed: 2013-01-09 from ufl.common import lstr, tstr, estr from ufl.assertions import ufl_assert class FormData(object): """ Class collecting various information extracted from a Form by calling preprocess. """ def __init__(self): "Create empty form data for given form." def __str__(self): "Return formatted summary of form data" types = sorted(self.num_sub_domains.keys()) domains = tuple(("Number of %s domains" % domain_type, self.num_sub_domains[domain_type]) for domain_type in types) return tstr((("Name", self.name), ("Cell", self.cell), ("Topological dimension", self.topological_dimension), ("Geometric dimension", self.geometric_dimension), ) + domains + ( ("Rank", self.rank), ("Number of coefficients", self.num_coefficients), ("Arguments", lstr(self.original_arguments)), ("Coefficients", lstr(self.original_coefficients)), ("Argument names", lstr(self.argument_names)), ("Coefficient names", lstr(self.coefficient_names)), ("Unique elements", estr(self.unique_elements)), ("Unique sub elements", estr(self.unique_sub_elements)), # FIXME DOMAINS what is "the domain(s)" for a form? ("Domains", self.domains), ("Top level domains", self.top_domains), )) def validate(self, object_names=None, common_cell=None, element_mapping=None): "Validate that the form data was built from the same inputs." ufl_assert((object_names or {}) == self._input_object_names, "Found non-matching object_names in form data validation.") ufl_assert(common_cell in (None, self.cell), "Found non-matching cells in form data validation.") ufl_assert((element_mapping or {}) == self._input_element_mapping, "Found non-matching element mappings in form data validation.") class ExprData(object): """ Class collecting various information extracted from a Expr by calling preprocess. """ def __init__(self): "Create empty expr data for given expr." def __str__(self): "Return formatted summary of expr data" return tstr((("Name", self.name), ("Cell", self.cell), ("Topological dimension", self.topological_dimension), ("Geometric dimension", self.geometric_dimension), ("Rank", self.rank), ("Number of coefficients", self.num_coefficients), ("Arguments", lstr(self.arguments)), ("Coefficients", lstr(self.coefficients)), ("Argument names", lstr(self.argument_names)), ("Coefficient names", lstr(self.coefficient_names)), ("Unique elements", estr(self.unique_elements)), ("Unique sub elements", estr(self.unique_sub_elements)), # FIXME DOMAINS what is "the domain(s)" for an expression? ("Domains", self.domains), ("Top level domains", self.top_domains), )) ufl-1.3.0/ufl/algorithms/formfiles.py000066400000000000000000000176071226300046600176120ustar00rootroot00000000000000"""A collection of utility algorithms for handling UFL files.""" from __future__ import with_statement # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # Modified by Marie E. Rognes, 2011. # # First added: 2008-03-14 # Last changed: 2012-12-11 import os, re from ufl.log import error, warning from ufl.common import sorted_items from ufl.assertions import ufl_assert from ufl.form import Form from ufl.finiteelement import FiniteElementBase from ufl.expr import Expr from ufl.argument import Argument from ufl.coefficient import Coefficient class FileData(object): def __init__(self): self.elements = [] self.coefficients = [] self.expressions = [] self.forms = [] self.object_names = {} self.object_by_name = {} self.reserved_objects = {} def __nonzero__(self): return bool(self.elements or self.coefficients or self.forms or self.expressions or\ self.object_names or self.object_by_name or self.reserved_objects) infostring = """An exception occured during evaluation of form file. To help you find the location of the error, a temporary script '%s' has been created and will now be executed with debug output enabled:""" def replace_include_statements(code): "Replace '#include foo.ufl' statements with contents of foo.ufl." if "#include" in code: lines = code.split("\n") newlines = [] regexp = re.compile(r"^#include (.*)$") for l in lines: m = regexp.search(l) if m: fn = m.groups()[0] newlines.append("# --- begin %s" % fn) newlines.extend(open(fn).readlines()) newlines.append("# --- end %s" % fn) else: newlines.append(l) return "\n".join(l.rstrip() for l in newlines) return code def read_ufl_file(filename): "Read a .ufl file, handling file extension, file existance, and #include replacement." if not os.path.exists(filename) and filename[-4:] != ".ufl": filename = filename + ".ufl" if not os.path.exists(filename): error("File '%s' doesn't exist." % filename) with open(filename) as f: code = replace_include_statements(f.read()) return code def execute_ufl_code(uflcode, filename): # Execute code namespace = {} try: pycode = "from ufl import *\n" + uflcode exec pycode in namespace except: # Dump python code for debugging if this fails basename = os.path.splitext(os.path.basename(filename))[0] basename = "%s_debug" % basename pyname = "%s.py" % basename pycode = "#!/usr/bin/env python\nfrom ufl import *\nset_level(DEBUG)\n" + uflcode with file(pyname, "w") as f: f.write(pycode) warning(infostring % pyname) m = __import__(basename) error("An error occured, aborting load_forms.") return namespace def interpret_ufl_namespace(namespace): "Takes a namespace dict from an executed ufl file and converts it to a FileData object." # Object to hold all returned data ufd = FileData() # Extract object names for Form, Coefficient and FiniteElementBase objects # The use of id(obj) as key in object_names is necessary # because we need to distinguish between instances, # and not just between objects with different values. for name, value in sorted_items(namespace): # Store objects by reserved name OR instance id reserved_names = ("unknown",) # Currently only one reserved name if name in reserved_names: # Store objects with reserved names ufd.reserved_objects[name] = value # FIXME: Remove after FFC is updated to use reserved_objects: ufd.object_names[name] = value ufd.object_by_name[name] = value elif isinstance(value, (FiniteElementBase, Coefficient, Argument, Form, Expr)): # Store instance <-> name mappings for important objects without a reserved name ufd.object_names[id(value)] = name ufd.object_by_name[name] = value # Get list of exported forms forms = namespace.get("forms") if forms is None: # Get forms from object_by_name, which has already mapped tuple->Form where needed def get_form(name): form = ufd.object_by_name.get(name) return form if isinstance(form, Form) else None a_form = get_form("a") L_form = get_form("L") M_form = get_form("M") forms = [a_form, L_form, M_form] # Add forms F and J if not "a" and "L" are used if a_form is None or L_form is None: F_form = get_form("F") J_form = get_form("J") forms += [F_form, J_form] # Remove Nones forms = [f for f in forms if isinstance(f, Form)] ufd.forms = forms # Validate types ufl_assert(isinstance(ufd.forms, (list, tuple)), "Expecting 'forms' to be a list or tuple, not '%s'." % type(ufd.forms)) ufl_assert(all(isinstance(a, Form) for a in ufd.forms), "Expecting 'forms' to be a list of Form instances.") # Get list of exported elements elements = namespace.get("elements") if elements is None: elements = [ufd.object_by_name.get(name) for name in ("element",)] elements = [e for e in elements if e is not None] ufd.elements = elements # Validate types ufl_assert(isinstance(ufd.elements, (list, tuple)), "Expecting 'elements' to be a list or tuple, not '%s'." % type(ufd.elements)) ufl_assert(all(isinstance(e, FiniteElementBase) for e in ufd.elements), "Expecting 'elements' to be a list of FiniteElementBase instances.") # Get list of exported coefficients # TODO: Temporarily letting 'coefficients' override 'functions', but allow 'functions' for compatibility functions = namespace.get("functions", []) if functions: warning("Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients.") ufd.coefficients = namespace.get("coefficients", functions) #ufd.coefficients = namespace.get("coefficients", []) # Validate types ufl_assert(isinstance(ufd.coefficients, (list, tuple)), "Expecting 'coefficients' to be a list or tuple, not '%s'." % type(ufd.coefficients)) ufl_assert(all(isinstance(e, Coefficient) for e in ufd.coefficients), "Expecting 'coefficients' to be a list of Coefficient instances.") # Get list of exported expressions ufd.expressions = namespace.get("expressions", []) # Validate types ufl_assert(isinstance(ufd.expressions, (list, tuple)), "Expecting 'expressions' to be a list or tuple, not '%s'." % type(ufd.expressions)) ufl_assert(all(isinstance(e, Expr) for e in ufd.expressions), "Expecting 'expressions' to be a list of Expr instances.") # Return file data return ufd def load_ufl_file(filename): "Load a .ufl file with elements, coefficients and forms." # Read code from file and execute it uflcode = read_ufl_file(filename) namespace = execute_ufl_code(uflcode, filename) return interpret_ufl_namespace(namespace) def load_forms(filename): "Return a list of all forms in a file." ufd = load_ufl_file(filename) return ufd.forms ufl-1.3.0/ufl/algorithms/formtransformations.py000066400000000000000000000357251226300046600217420ustar00rootroot00000000000000"""This module defines utilities for transforming complete Forms into new related Forms.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # Modified by Garth N. Wells, 2010. # Modified by Marie E. Rognes, 2010. # # First added: 2008-10-01 # Last changed: 2012-04-12 from ufl.common import product from ufl.log import error, warning, debug from ufl.assertions import ufl_assert # All classes: from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.constantvalue import Zero from ufl.algebra import Sum # Other algorithms: from ufl.algorithms.traversal import traverse_terminals from ufl.algorithms.analysis import extract_arguments from ufl.algorithms.transformer import Transformer, transform_integrands from ufl.algorithms.replace import replace def zero(e): return Zero(e.shape(), e.free_indices(), e.index_dimensions()) class PartExtracter(Transformer): """ PartExtracter extracts those parts of a form that contain the given argument(s). """ def __init__(self, arguments): Transformer.__init__(self) self._want = set(arguments) def expr(self, x): """The default is a nonlinear operator not accepting any Arguments among its children.""" # FIXME: This check makes this an O(n^2) algorithm... if any(isinstance(t, Argument) for t in traverse_terminals(x)): error("Found Argument in %s, this is an invalid expression." % repr(x)) return (x, set()) # Terminals that are not Variables or Arguments behave as default # expr-s. terminal = expr def variable(self, x): "Return relevant parts of this variable." # Extract parts/provides from this variable's expression expression, label = x.operands() part, provides = self.visit(expression) # If the extracted part is zero or we provide more than we # want, return zero if isinstance(part, Zero) or (provides - self._want): return (zero(x), set()) # Reuse varible if possible (or reconstruct from part) x = self.reuse_if_possible(x, part, label) return (x, provides) def argument(self, x): "Return itself unless itself provides too much." # An argument provides itself provides = set((x,)) # If we provide more than we want, return zero if provides - self._want: return (zero(x), set()) return (x, provides) def sum(self, x): """ Return the terms that might eventually yield the correct parts(!) The logic required for sums is a bit elaborate: A sum may contain terms providing different arguments. We should return (a sum of) a suitable subset of these terms. Those should all provide the same arguments. For each term in a sum, there are 2 simple possibilities: 1a) The relevant part of the term is zero -> skip. 1b) The term provides more arguments than we want -> skip 2) If all terms fall into the above category, we can just return zero. Any remaining terms may provide exactly the arguments we want, or fewer. This is where things start getting interesting. 3) Bottom-line: if there are terms with providing different arguments -- provide terms that contain the most arguments. If there are terms providing different sets of same size -> throw error (e.g. Argument(-1) + Argument(-2)) """ parts_that_provide = {} # 1. Skip terms that provide too much original_terms = x.operands() for term in original_terms: # Visit this term in the sum part, term_provides = self.visit(term) # If this part is zero or it provides more than we want, # skip it if isinstance(part, Zero) or (term_provides - self._want): continue # Add the contributions from this part to temporary list term_provides = frozenset(term_provides) if term_provides in parts_that_provide: parts_that_provide[term_provides] += [part] else: parts_that_provide[term_provides] = [part] # 2. If there are no remaining terms, return zero if not parts_that_provide: return (zero(x), set()) # 3. Return the terms that provide the biggest set most_provided = frozenset() for (provideds, parts) in parts_that_provide.iteritems(): # TODO: Just sort instead? # Throw error if size of sets are equal (and not zero) if len(provideds) == len(most_provided) and len(most_provided): error("Don't know what to do with sums with different Arguments.") if provideds > most_provided: most_provided = provideds terms = parts_that_provide[most_provided] if len(terms) == len(original_terms): x = self.reuse_if_possible(x, *terms) else: x = Sum(*terms) return (x, most_provided) def product(self, x, *ops): """ Note: Product is a visit-children-first handler. ops are the visited factors.""" provides = set() factors = [] for factor, factor_provides in ops: # If any factor is zero, return if isinstance(factor, Zero): return (zero(x), set()) # Add factor to factors and extend provides factors.append(factor) provides = provides | factor_provides # If we provide more than we want, return zero if provides - self._want: return (zero(x), provides) # Reuse product if possible (or reconstruct from factors) x = self.reuse_if_possible(x, *factors) return (x, provides) # inner, outer and dot all behave as product inner = product outer = product dot = product def division(self, x): "Return parts_of_numerator/denominator." # Get numerator and denominator numerator, denominator = x.operands() # Check for Arguments in the denominator if any(isinstance(t, Argument) for t in traverse_terminals(denominator)): error("Found Argument in denominator of %s , this is an invalid expression." % repr(x)) # Visit numerator numerator_parts, provides = self.visit(numerator) # If numerator is zero, return zero. (No need to check whether # it provides too much, already checked by visit.) if isinstance(numerator_parts, Zero): return (zero(x), set()) # Reuse x if possible, otherwise reconstruct from (parts of) # numerator and denominator x = self.reuse_if_possible(x, numerator_parts, denominator) return (x, provides) def linear_operator(self, x, arg): """A linear operator with a single operand accepting arity > 0, providing whatever Argument its operand does.""" # linear_operator is a visit-children-first handler. Handled # arguments are in arg. part, provides = arg # Discard if part is zero. (No need to check whether we # provide too much, already checked by children.) if isinstance(part, Zero): return (zero(x), set()) x = self.reuse_if_possible(x, part) return (x, provides) # Positive and negative restrictions behave as linear operators positive_restricted = linear_operator negative_restricted = linear_operator # Cell and facet average are linear operators cell_avg = linear_operator facet_avg = linear_operator # Grad is a linear operator grad = linear_operator def linear_indexed_type(self, x): """Return parts of expression belonging to this indexed expression.""" expression, index = x.operands() part, provides = self.visit(expression) # Return zero if extracted part is zero. (The expression # should already have checked if it provides too much.) if isinstance(part, Zero): return (zero(x), set()) # Reuse x if possible (or reconstruct by indexing part) x = self.reuse_if_possible(x, part, index) return (x, provides) # All of these indexed thingies behave as a linear_indexed_type indexed = linear_indexed_type index_sum = linear_indexed_type component_tensor = linear_indexed_type spatial_derivative = linear_indexed_type def list_tensor(self, x, *ops): # list_tensor is a visit-children-first handler. ops contains # the visited operands with their provides. (It follows that # none of the visited operands provide more than wanted.) # Extract the most arguments provided by any of the components most_provides = ops[0][1] for (component, provides) in ops: if (provides - most_provides): most_provides = provides # Check that all components either provide the same arguments # or vanish. (This check is here b/c it is not obvious what to # return if the components provide different arguments, at # least with the current transformer design.) for (component, provides) in ops: if (provides != most_provides and not isinstance(component, Zero)): error("PartExtracter does not know how to handle list_tensors with non-zero components providing fewer arguments") # Return components components = [op[0] for op in ops] x = self.reuse_if_possible(x, *components) return (x, most_provides) def compute_form_with_arity(form, arity, arguments=None): """Compute parts of form of given arity.""" # Extract all arguments in form if arguments is None: arguments = extract_arguments(form) if len(arguments) < arity: warning("Form has no parts with arity %d." % arity) return 0*form # Assuming that the form is not a sum of terms # that depend on different arguments, e.g. (u+v)*dx # would result in just v*dx. But that doesn't make # any sense anyway. sub_arguments = set(arguments[:arity]) pe = PartExtracter(sub_arguments) def _transform(e): e, provides = pe.visit(e) if provides == sub_arguments: return e return Zero() res = transform_integrands(form, _transform) return res def compute_form_arities(form): """Return set of arities of terms present in form.""" #ufl_assert(form.is_preprocessed(), "Assuming a preprocessed form.") # Extract all arguments present in form arguments = extract_arguments(form) arities = set() for arity in range(len(arguments)+1): # Compute parts with arity "arity" parts = compute_form_with_arity(form, arity, arguments) # Register arity if "parts" does not vanish if parts and parts.integrals(): arities.add(arity) return arities def compute_form_lhs(form): """Compute the left hand side of a form. Example: a = u*v*dx + f*v*dx a = lhs(a) -> u*v*dx """ return compute_form_with_arity(form, 2) def compute_form_rhs(form): """Compute the right hand side of a form. Example: a = u*v*dx + f*v*dx L = rhs(a) -> -f*v*dx """ return -compute_form_with_arity(form, 1) def compute_form_functional(form): """Compute the functional part of a form, that is the terms independent of Arguments. (Used for testing, not sure if it's useful for anything?)""" return compute_form_with_arity(form, 0) def compute_form_action(form, coefficient): """Compute the action of a form on a Coefficient. This works simply by replacing the last Argument with a Coefficient on the same function space (element). The form returned will thus have one Argument less and one additional Coefficient at the end if no Coefficient has been provided. """ # TODO: Check whatever makes sense for coefficient # Extract all arguments arguments = extract_arguments(form) # Pick last argument (will be replaced) u = arguments[-1] e = u.element() if coefficient is None: coefficient = Coefficient(e) else: #ufl_assert(coefficient.element() == e, \ if coefficient.element() != e: debug("Computing action of form on a coefficient in a different element space.") return replace(form, { u: coefficient }) def compute_energy_norm(form, coefficient): """Compute the a-norm of a Coefficient given a form a. This works simply by replacing the two Arguments with a Coefficient on the same function space (element). The Form returned will thus be a functional with no Arguments, and one additional Coefficient at the end if no coefficient has been provided. """ arguments = extract_arguments(form) ufl_assert(len(arguments) == 2, "Expecting bilinear form.") v, u = arguments e = u.element() e2 = v.element() ufl_assert(e == e2, "Expecting equal finite elements for test and trial functions, got '%s' and '%s'." % (str(e), str(e2))) if coefficient is None: coefficient = Coefficient(e) else: ufl_assert(coefficient.element() == e, \ "Trying to compute action of form on a "\ "coefficient in an incompatible element space.") return replace(form, { u: coefficient, v: coefficient }) def compute_form_adjoint(form, reordered_arguments=None): """Compute the adjoint of a bilinear form. This works simply by changing the ordering (count) of the two arguments. """ arguments = extract_arguments(form) ufl_assert(len(arguments) == 2, "Expecting bilinear form.") v, u = arguments ufl_assert(v.count() < u.count(), "Mistaken assumption in code!") if reordered_arguments is None: reordered_arguments = (Argument(u.element()), Argument(v.element())) reordered_u, reordered_v = reordered_arguments ufl_assert(reordered_u.count() < reordered_v.count(), "Ordering of new arguments is the same as the old arguments!") ufl_assert(reordered_u.element() == u.element(), "Element mismatch between new and old arguments (trial functions).") ufl_assert(reordered_v.element() == v.element(), "Element mismatch between new and old arguments (test functions).") return replace(form, {v: reordered_v, u: reordered_u}) ufl-1.3.0/ufl/algorithms/forward_ad.py000066400000000000000000001137371226300046600177350ustar00rootroot00000000000000"""Forward mode AD implementation.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # Modified by Garth N. Wells, 2010. # Modified by Kristian B. Oelgaard, 2011 # Modified by Jan Blechta, 2012. # # First added: 2008-08-19 # Last changed: 2013-08-12 from itertools import izip from math import pi from ufl.log import error, warning, debug from ufl.assertions import ufl_assert from ufl.common import unzip, subdict, lstr from ufl.indexutils import unique_indices # All classes: from ufl.terminal import Terminal from ufl.operatorbase import Tuple from ufl.constantvalue import ConstantValue, Zero, IntValue, Identity,\ is_true_ufl_scalar, is_ufl_scalar from ufl.variable import Variable from ufl.coefficient import ConstantBase, Coefficient, FormArgument from ufl.indexing import MultiIndex, Index, FixedIndex, indices from ufl.indexed import Indexed from ufl.indexsum import IndexSum from ufl.tensors import ListTensor, ComponentTensor, as_tensor, as_scalar, unit_indexed_tensor, unwrap_list_tensor from ufl.algebra import Sum, Product, Division, Power, Abs from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, \ Determinant, Inverse, Deviatoric, Cofactor from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Cos, Sin, Tan, Acos, Asin, Atan, Atan2, Erf, BesselJ, BesselY, BesselI, BesselK from ufl.restriction import Restricted, PositiveRestricted, NegativeRestricted from ufl.differentiation import Derivative, CoefficientDerivative,\ VariableDerivative, Grad from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional from ufl.operators import dot, inner, outer, lt, eq, conditional, sign, \ sqrt, exp, ln, cos, sin, tan, cosh, sinh, tanh, acos, asin, atan, atan_2, \ erf, bessel_J, bessel_Y, bessel_I, bessel_K, \ cell_avg, facet_avg from ufl.algorithms.transformer import Transformer class ForwardAD(Transformer): def __init__(self, spatial_dim, var_shape, var_free_indices, var_index_dimensions, cache=None): Transformer.__init__(self) ufl_assert(all(isinstance(i, Index) for i in var_free_indices), \ "Expecting Index objects.") ufl_assert(all(isinstance(i, Index) for i in var_index_dimensions.keys()), \ "Expecting Index objects.") self._spatial_dim = spatial_dim self._var_shape = var_shape self._var_free_indices = var_free_indices self._var_index_dimensions = dict(var_index_dimensions) #if self._var_free_indices: # warning("TODO: Free indices in differentiation variable may be buggy!") self._cache = {} if cache is None else cache def _cache_visit(self, o): "Cache hook, disable this by renaming to something else than 'visit'." #debug("Visiting object of type %s." % type(o).__name__) # TODO: This doesn't work, why? # NB! Cache added in after copying from Transformer c = self._cache.get(o) if c is not None: return c # Reuse default visit function r = Transformer.visit(self, o) if (c is not None): if r[0].free_indices() != c[0].free_indices(): print "="*70 print "=== f: Difference between cache and recomputed indices:" print str(c[0].free_indices()) print str(r[0].free_indices()) print "="*70 if r[1].free_indices() != c[1].free_indices(): print "="*70 print "=== df: Difference between cache and recomputed indices:" print str(c[1].free_indices()) print str(r[1].free_indices()) print "="*70 if (r != c): print "="*70 print "=== Difference between cache and recomputed:" print str(c[0]) print str(c[1]) print "-"*40 print str(r[0]) print str(r[1]) print "="*70 # NB! Cache added in after copying from Transformer self._cache[o] = r return r def _debug_visit(self, o): "Debugging hook, enable this by renaming to 'visit'." r = Transformer.visit(self, o) f, df = r if not f is o: debug("In ForwardAD.visit, didn't get back o:") debug(" o: %s" % str(o)) debug(" f: %s" % str(f)) debug(" df: %s" % str(df)) fi_diff = set(f.free_indices()) ^ set(df.free_indices()) if fi_diff: debug("In ForwardAD.visit, got free indices diff:") debug(" o: %s" % str(o)) debug(" f: %s" % str(f)) debug(" df: %s" % str(df)) debug(" f.fi(): %s" % lstr(f.free_indices())) debug(" df.fi(): %s" % lstr(df.free_indices())) debug(" fi_diff: %s" % str(fi_diff)) return r def _make_zero_diff(self, o): # Define a zero with the right indices # (kind of cumbersome this... any simpler way?) sh = o.shape() + self._var_shape fi = o.free_indices() idims = dict(o.index_dimensions()) if self._var_free_indices: # Currently assuming only one free variable index i, = self._var_free_indices if i not in idims: fi = unique_indices(fi + (i,)) idims[i] = self._var_index_dimensions[i] fp = Zero(sh, fi, idims) return fp def _make_ones_diff(self, o): ufl_assert(o.shape() == self._var_shape, "This is only used by VariableDerivative, yes?") # Define a scalar value with the right indices # (kind of cumbersome this... any simpler way?) sh = o.shape() fi = o.free_indices() idims = dict(o.index_dimensions()) if self._var_free_indices: # Currently assuming only one free variable index i, = self._var_free_indices if i not in idims: fi = unique_indices(fi + (i,)) idims[i] = self._var_index_dimensions[i] # Create a 1 with index annotations one = IntValue(1, (), fi, idims) res = None if sh == (): return one elif len(sh) == 1: # FIXME: If sh == (1,), I think this will get the wrong shape? # I think we should make sure sh=(1,...,1) is always converted to () early. fp = Identity(sh[0]) else: ind1 = () ind2 = () for d in sh: i, j = indices(2) dij = Identity(d)[i, j] if res is None: res = dij else: res *= dij ind1 += (i,) ind2 += (j,) fp = as_tensor(res, ind1 + ind2) # Apply index annotations if fi: fp *= one return fp # --- Default rules def expr(self, o): error("Missing ForwardAD handler for type %s" % str(type(o))) def terminal(self, o): """Terminal objects are assumed independent of the differentiation variable by default, and simply 'lifted' to the pair (o, 0). Depending on the context, override this with custom rules for non-zero derivatives.""" fp = self._make_zero_diff(o) return (o, fp) def variable(self, o): """Variable objects are just 'labels', so by default the derivative of a variable is the derivative of its referenced expression.""" # Check variable cache to reuse previously transformed variable if possible e, l = o.operands() r = self._variable_cache.get(l) # cache contains (v, vp) tuple if r is not None: return r # Visit the expression our variable represents e2, vp = self.visit(e) # Recreate Variable (with same label) only if necessary v = self.reuse_if_possible(o, e2, l) # Cache and return (v, vp) tuple r = (v, vp) self._variable_cache[l] = r return r # --- Indexing and component handling def multi_index(self, o): return (o, None) # oprime here should never be used, this might even not be called? def indexed(self, o): A, jj = o.operands() A2, Ap = self.visit(A) o = self.reuse_if_possible(o, A2, jj) if isinstance(Ap, Zero): op = self._make_zero_diff(o) else: r = Ap.rank() - len(jj) if r: ii = indices(r) op = Indexed(Ap, jj._indices + ii) op = as_tensor(op, ii) else: op = Indexed(Ap, jj) return (o, op) def list_tensor(self, o, *ops): ops, dops = unzip(ops) o = self.reuse_if_possible(o, *ops) op = ListTensor(*dops) return (o, op) def component_tensor(self, o): A, ii = o.operands() A, Ap = self.visit(A) o = self.reuse_if_possible(o, A, ii) if isinstance(Ap, Zero): op = self._make_zero_diff(o) else: Ap, jj = as_scalar(Ap) op = ComponentTensor(Ap, ii._indices + jj) return (o, op) # --- Algebra operators def index_sum(self, o): A, i = o.operands() # Consider the following case: # (v[i]*u[i]).dx(i) # which is represented like # SpatialDerivative(IndexSum(..., i), i) # if we move the derivative inside the sum, # then the derivative suddenly gets accumulated, # which is completely wrong! if self._var_free_indices: if i[0] in self._var_free_indices: error("Index scope collision. Work around this by reusing indices less in different expressions.\n"\ "An example where this occurs is (v[i]*v[i]).dx(i) where the same index i\n"\ "is bound to an index sum inside the derivative .dx(i).") # TODO: OR get around this by temporarily setting _var_free_indices to nothing? #store = self._var_free_indices #self._var_free_indices = nothing? #visit children #self._var_free_indices = store # But... What would this mean? Will it be correct? # TODO: Get around this with relabeling algorithm! # j = Index() # A = relabel(A, { i0: j }) # i = (j,) A2, Ap = self.visit(A) o = self.reuse_if_possible(o, A2, i) op = IndexSum(Ap, i) return (o, op) def sum(self, o, *ops): ops, opsp = unzip(ops) o2 = self.reuse_if_possible(o, *ops) op = sum(opsp[1:], opsp[0]) return (o2, op) def product(self, o, *ops): # Start with a zero with the right shape and indices fp = self._make_zero_diff(o) # Get operands and their derivatives ops2, dops2 = unzip(ops) o = self.reuse_if_possible(o, *ops2) for i in xrange(len(ops)): # Get scalar representation of differentiated value of operand i dop = dops2[i] dop, ii = as_scalar(dop) # Replace operand i with its differentiated value in product fpoperands = ops2[:i] + [dop] + ops2[i+1:] p = Product(*fpoperands) # Wrap product in tensor again if ii: p = as_tensor(p, ii) # Accumulate terms fp += p return (o, fp) def division(self, o, a, b): f, fp = a g, gp = b o = self.reuse_if_possible(o, f, g) ufl_assert(is_ufl_scalar(f), "Not expecting nonscalar nominator") ufl_assert(is_true_ufl_scalar(g), "Not expecting nonscalar denominator") #do_df = 1/g #do_dg = -h/g #op = do_df*fp + do_df*gp #op = (fp - o*gp) / g # Get o and gp as scalars, multiply, then wrap as a tensor again so, oi = as_scalar(o) sgp, gi = as_scalar(gp) o_gp = so*sgp if oi or gi: o_gp = as_tensor(o_gp, oi + gi) op = (fp - o_gp) / g return (o, op) def power(self, o, a, b): f, fp = a g, gp = b # Debugging prints, should never happen: if not is_true_ufl_scalar(f): print ":"*80 print "f =", str(f) print "g =", str(g) print ":"*80 ufl_assert(is_true_ufl_scalar(f), "Expecting scalar expression f in f**g.") ufl_assert(is_true_ufl_scalar(g), "Expecting scalar expression g in f**g.") # Derivation of the general case: o = f(x)**g(x) # #do_df = g * f**(g-1) #do_dg = ln(f) * f**g #op = do_df*fp + do_dg*gp # #do_df = o * g / f # f**g * g / f #do_dg = ln(f) * o #op = do_df*fp + do_dg*gp # Got two possible alternatives here: if True: # This version looks better. # Rewriting o as f*f**(g-1) we can do: f_g_m1 = f**(g-1) op = f_g_m1*(fp*g + f*ln(f)*gp) # In this case we can rewrite o using new subexpression o = f*f_g_m1 else: # Pulling o out gives: op = o*(fp*g/f + ln(f)*gp) # This produces expressions like (1/w)*w**5 instead of w**4 # If we do this, we reuse o o = self.reuse_if_possible(o, f, g) return (o, op) def abs(self, o, a): f, fprime = a o = self.reuse_if_possible(o, f) #oprime = conditional(eq(f, 0), 0, Product(sign(f), fprime)) oprime = sign(f)*fprime return (o, oprime) # --- Mathfunctions def math_function(self, o, a): if hasattr(o, 'derivative'): # FIXME: Introduce a UserOperator type instead of this hack f, fp = a o = self.reuse_if_possible(o, f) op = fp * o.derivative() return (o, op) error("Unknown math function.") def sqrt(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp / (2*o) return (o, op) def exp(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*o return (o, op) def ln(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) ufl_assert(not isinstance(f, Zero), "Division by zero.") return (o, fp/f) def cos(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = -fp*sin(f) return (o, op) def sin(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*cos(f) return (o, op) def tan(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*2.0/(cos(2.0*f) + 1.0) return (o, op) def cosh(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*sinh(f) return (o, op) def sinh(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*cosh(f) return (o, op) def tanh(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) def sech(y): return (2.0*cosh(y)) / (cosh(2.0*y) + 1.0) op = fp*sech(f)**2 return (o, op) def acos(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = -fp/sqrt(1.0 - f**2) return (o, op) def asin(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp/sqrt(1.0 - f**2) return (o, op) def atan(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp/(1.0 + f**2) return (o, op) def atan_2(self, o, a, b): f, fp = a g, gp = b o = self.reuse_if_possible(o, f, g) op = (g*fp-f*gp)/(f**2+g**2) return (o, op) def erf(self, o, a): f, fp = a o = self.reuse_if_possible(o, f) op = fp*(2.0/sqrt(pi)*exp(-f**2)) return (o, op) def bessel_j(self, o, nu, x): nu, dummy = nu if not (dummy is None or isinstance(dummy, Zero)): error("Differentiation of bessel function w.r.t. nu is not supported.") f, fp = x o = self.reuse_if_possible(o, nu, f) if nu == 0: op = -bessel_J(1, f) else: op = 0.5 * (bessel_J(nu-1, f) - bessel_J(nu+1, f)) return (o, op*fp) def bessel_y(self, o, nu, x): nu, dummy = nu if not (dummy is None or isinstance(dummy, Zero)): error("Differentiation of bessel function w.r.t. nu is not supported.") f, fp = x o = self.reuse_if_possible(o, nu, f) if nu == 0: op = -bessel_Y(1, f) else: op = 0.5 * (bessel_Y(nu-1, f) - bessel_Y(nu+1, f)) return (o, op*fp) def bessel_i(self, o, nu, x): nu, dummy = nu if not (dummy is None or isinstance(dummy, Zero)): error("Differentiation of bessel function w.r.t. nu is not supported.") f, fp = x o = self.reuse_if_possible(o, nu, f) if nu == 0: op = bessel_I(1, f) else: op = 0.5 * (bessel_I(nu-1, f) + bessel_I(nu+1, f)) return (o, op*fp) def bessel_k(self, o, nu, x): nu, dummy = nu if not (dummy is None or isinstance(dummy, Zero)): error("Differentiation of bessel function w.r.t. nu is not supported.") f, fp = x o = self.reuse_if_possible(o, nu, f) if nu == 0: op = -bessel_K(1, f) else: op = -0.5 * (bessel_K(nu-1, f) + bessel_K(nu+1, f)) return (o, op*fp) # --- Restrictions def restricted(self, o, a): # Restriction and differentiation commutes. f, fp = a o = self.reuse_if_possible(o, f) if isinstance(fp, ConstantValue): return (o, fp) # TODO: Necessary? Can't restriction simplify directly instead? else: return (o, fp(o._side)) # (f+-)' == (f')+- def cell_avg(self, o, a): # Cell average of a single function and differentiation commutes. f, fp = a o = self.reuse_if_possible(o, f) if isinstance(fp, ConstantValue): return (o, fp) # TODO: Necessary? Can't CellAvg simplify directly instead? else: return (o, cell_avg(fp)) def facet_avg(self, o, a): # Facet average of a single function and differentiation commutes. f, fp = a o = self.reuse_if_possible(o, f) if isinstance(fp, ConstantValue): return (o, fp) # TODO: Necessary? Can't FacetAvg simplify directly instead? else: return (o, facet_avg(fp)) # --- Conditionals def binary_condition(self, o, l, r): o = self.reuse_if_possible(o, l[0], r[0]) #if any(not (isinstance(op[1], Zero) or op[1] is None) for op in (l, r)): # warning("Differentiating a conditional with a condition "\ # "that depends on the differentiation variable."\ # "Assuming continuity of conditional. The condition "\ # "will not be differentiated.") oprime = None # Shouldn't be used anywhere return (o, oprime) def not_condition(self, o, c): o = self.reuse_if_possible(o, c[0]) #if not (isinstance(c[1], Zero) or c[1] is None): # warning("Differentiating a conditional with a condition "\ # "that depends on the differentiation variable."\ # "Assuming continuity of conditional. The condition "\ # "will not be differentiated.") oprime = None # Shouldn't be used anywhere return (o, oprime) def conditional(self, o, c, t, f): o = self.reuse_if_possible(o, c[0], t[0], f[0]) if isinstance(t[1], Zero) and isinstance(f[1], Zero): tp = t[1] # Assuming t[1] and f[1] have the same indices here, which should be the case fi = tp.free_indices() fid = subdict(tp.index_dimensions(), fi) op = Zero(tp.shape(), fi, fid) else: op = conditional(c[0], 1, 0)*t[1] + conditional(c[0], 0, 1)*f[1] return (o, op) # --- Other derivatives def derivative(self, o): error("This should never occur.") def grad(self, o): error("FIXME") def xspatial_derivative(self, o): # FIXME: Translate to grad situation # If we hit this type, it has already been propagated # to a terminal, so we can simply apply our derivative # to its operand since differentiation commutes. f, ii = o.operands() f, fp = self.visit(f) o = self.reuse_if_possible(o, f, ii) # FIXME: Make plenty of test cases around this kind of situation to document what's going on... if fp.is_cellwise_constant(): sh = fp.shape() fi = fp.free_indices() idims = dict(fp.index_dimensions()) j, = ii if isinstance(j, Index) and j not in idims: fi = fi + (j,) idims.update(ii.index_dimensions()) oprime = Zero(sh, fi, idims) #oprime = self._make_zero_diff(o) # FIXME: Can we just use this? else: oprime = SpatialDerivative(fp, ii) return (o, oprime) class GradAD(ForwardAD): def __init__(self, spatial_dim, cache=None): ForwardAD.__init__(self, spatial_dim, var_shape=(spatial_dim,), var_free_indices=(), var_index_dimensions={}, cache=cache) self._Id = Identity(spatial_dim) def spatial_coordinate(self, o): "Gradient of x w.r.t. x is Id." return (o, self._Id) # This is implicit for all terminals, but just to make this clear to the reader: facet_normal = ForwardAD.terminal # returns zero constant = ForwardAD.terminal # returns zero def form_argument(self, o): "Represent grad(f) as Grad(f)." # Collapse gradient of cellwise function to zero if o.is_cellwise_constant(): return self.terminal(o) return (o, Grad(o)) def grad(self, o): "Represent grad(grad(f)) as Grad(Grad(f))." # TODO: Not sure how to detect that gradient of f is cellwise constant. # Can we trust element degrees? #if o.is_cellwise_constant(): # return self.terminal(o) # TODO: Maybe we can ask "f.has_derivatives_of_order(n)" to check # if we should make a zero here? f, = o.operands() ufl_assert(isinstance(f, (Grad,Terminal)), "Expecting derivatives of child to be already expanded.") return (o, Grad(o)) class VariableAD(ForwardAD): def __init__(self, spatial_dim, var, cache=None): ForwardAD.__init__(self, spatial_dim, var_shape=var.shape(), var_free_indices=var.free_indices(), var_index_dimensions=var.index_dimensions(), cache=cache) self._variable = var def grad(self, o): # If we hit this type, it has already been propagated # to a coefficient, so it cannot depend on the variable. # FIXME: Assert this! return self.terminal(o) def variable(self, o): # Check cache e, l = o.operands() c = self._variable_cache.get(l) if c is not None: return c if o.label() == self._variable.label(): # dv/dv = "1" op = self._make_ones_diff(o) else: # differentiate expression behind variable e2, ep = self.visit(e) op = ep if not e2 == e: o = Variable(e2, l) # return variable and derivative of its expression c = (o, op) self._variable_cache[l] = c return c class CoefficientAD(ForwardAD): "Apply AFD (Automatic Functional Differentiation) to expression." def __init__(self, spatial_dim, coefficients, arguments, coefficient_derivatives, cache=None): ForwardAD.__init__(self, spatial_dim, var_shape=(), var_free_indices=(), var_index_dimensions={}, cache=cache) self._v = arguments self._w = coefficients self._cd = coefficient_derivatives ufl_assert(isinstance(self._w, Tuple), "Expecting a Tuple.") ufl_assert(isinstance(self._v, Tuple), "Expecting a Tuple.") def coefficient(self, o): # Define dw/dw := d/ds [w + s v] = v debug("In CoefficientAD.coefficient:") debug("o = %s" % o) debug("self._w = %s" % self._w) debug("self._v = %s" % self._v) # Find o among w for (w, v) in izip(self._w, self._v): if o == w: return (w, v) # If o is not among coefficient derivatives, return do/dw=0 oprimesum = Zero(o.shape()) oprimes = self._cd._data.get(o) if oprimes is None: if self._cd._data: # TODO: Make it possible to silence this message in particular? # It may be good to have for debugging... warning("Assuming d{%s}/d{%s} = 0." % (o, self._w)) else: # Make sure we have a tuple to match the self._v tuple if not isinstance(oprimes, tuple): oprimes = (oprimes,) ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\ "expecting a matching tuple of coefficient derivatives.") # Compute do/dw_j = do/dw_h : v. # Since we may actually have a tuple of oprimes and vs in a # 'mixed' space, sum over them all to get the complete inner # product. Using indices to define a non-compound inner product. for (oprime, v) in izip(oprimes, self._v): so, oi = as_scalar(oprime) rv = len(v.shape()) oi1 = oi[:-rv] oi2 = oi[-rv:] prod = so*v[oi2] if oi1: oprimesum += as_tensor(prod, oi1) else: oprimesum += prod # Example: # (f : g) -> (dfdu : v) : g + ditto # shape(f) == shape(g) == shape(dfdu : v) # shape(dfdu) == shape(f) + shape(v) return (o, oprimesum) def grad(self, g): # If we hit this type, it has already been propagated # to a coefficient (or grad of a coefficient), # FIXME: Assert this! # so we need to take the gradient of the variation or return zero. # Complications occur when dealing with derivatives w.r.t. single components... # Figure out how many gradients are around the inner terminal ngrads = 0 o = g while isinstance(o, Grad): o, = o.operands() ngrads += 1 if not isinstance(o, FormArgument): error("Expecting gradient of a FormArgument, not %r" % (o,)) def apply_grads(f): if not isinstance(f, FormArgument): print ','*60 print f print o print g print ','*60 error("What?") for i in range(ngrads): f = Grad(f) return f # Find o among all w without any indexing, which makes this easy for (w, v) in izip(self._w, self._v): if o == w and isinstance(v, FormArgument): # Case: d/dt [w + t v] return (g, apply_grads(v)) # If o is not among coefficient derivatives, return do/dw=0 gprimesum = Zero(g.shape()) def analyse_variation_argument(v): # Analyse variation argument if isinstance(v, FormArgument): # Case: d/dt [w[...] + t v] vval, vcomp = v, () elif isinstance(v, Indexed): # Case: d/dt [w + t v[...]] # Case: d/dt [w[...] + t v[...]] vval, vcomp = v.operands() vcomp = tuple(vcomp) else: error("Expecting argument or component of argument.") ufl_assert(all(isinstance(k, FixedIndex) for k in vcomp), "Expecting only fixed indices in variation.") return vval, vcomp def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp): # Apply gradients directly to argument vval, # and get the right indexed scalar component(s) kk = indices(ngrads) Dvkk = apply_grads(vval)[vcomp+kk] # Place scalar component(s) Dvkk into the right tensor positions if wshape: Ejj, jj = unit_indexed_tensor(wshape, wcomp) else: Ejj, jj = 1, () gprimeterm = as_tensor(Ejj*Dvkk, jj+kk) return gprimeterm # Accumulate contributions from variations in different components for (w, v) in izip(self._w, self._v): # Analyse differentiation variable coefficient if isinstance(w, FormArgument): if not w == o: continue wshape = w.shape() if isinstance(v, FormArgument): # Case: d/dt [w + t v] return (g, apply_grads(v)) elif isinstance(v, ListTensor): # Case: d/dt [w + t <...,v,...>] for wcomp, vsub in unwrap_list_tensor(v): if not isinstance(vsub, Zero): vval, vcomp = analyse_variation_argument(vsub) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: ufl_assert(wshape == (), "Expecting scalar coefficient in this branch.") # Case: d/dt [w + t v[...]] wval, wcomp = w, () vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) elif isinstance(w, Indexed): # This path is tested in unit tests, but not actually used? # Case: d/dt [w[...] + t v[...]] # Case: d/dt [w[...] + t v] wval, wcomp = w.operands() if not wval == o: continue assert isinstance(wval, FormArgument) ufl_assert(all(isinstance(k, FixedIndex) for k in wcomp), "Expecting only fixed indices in differentiation variable.") wshape = wval.shape() vval, vcomp = analyse_variation_argument(v) gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp) else: error("Expecting coefficient or component of coefficient.") # FIXME: Handle other coefficient derivatives: oprimes = self._cd._data.get(o) if 0: oprimes = self._cd._data.get(o) if oprimes is None: if self._cd._data: # TODO: Make it possible to silence this message in particular? # It may be good to have for debugging... warning("Assuming d{%s}/d{%s} = 0." % (o, self._w)) else: # Make sure we have a tuple to match the self._v tuple if not isinstance(oprimes, tuple): oprimes = (oprimes,) ufl_assert(len(oprimes) == len(self._v), "Got a tuple of arguments, "+\ "expecting a matching tuple of coefficient derivatives.") # Compute dg/dw_j = dg/dw_h : v. # Since we may actually have a tuple of oprimes and vs in a # 'mixed' space, sum over them all to get the complete inner # product. Using indices to define a non-compound inner product. for (oprime, v) in izip(oprimes, self._v): error("FIXME: Figure out how to do this with ngrads") so, oi = as_scalar(oprime) rv = len(v.shape()) oi1 = oi[:-rv] oi2 = oi[-rv:] prod = so*v[oi2] if oi1: gprimesum += as_tensor(prod, oi1) else: gprimesum += prod return (g, gprimesum) def variable(self, o): # Check variable cache to reuse previously transformed variable if possible e, l = o.operands() c = self._variable_cache.get(l) if c is not None: return c # Visit the expression our variable represents e2, ep = self.visit(e) op = ep # If the expression is not the same, reconstruct Variable object o = self.reuse_if_possible(o, e2, l) # Recreate Variable (with same label) and cache it c = (o, op) self._variable_cache[l] = c return c def compute_grad_forward_ad(f, dim): alg = GradAD(dim) e, ediff = alg.visit(f) return ediff def compute_variable_forward_ad(f, v, dim): alg = VariableAD(dim, v) e, ediff = alg.visit(f) return ediff def compute_coefficient_forward_ad(f, w, v, cd, dim): alg = CoefficientAD(dim, w, v, cd) e, ediff = alg.visit(f) return ediff def apply_nested_forward_ad(expr, dim): if isinstance(expr, Terminal): # A terminal needs no differentiation applied return expr elif not isinstance(expr, Derivative): # Apply AD recursively to children preops = expr.operands() postops = tuple(apply_nested_forward_ad(o, dim) for o in preops) # Reconstruct if necessary need_reconstruct = not (preops == postops) # FIXME: Is this efficient? O(n)? if need_reconstruct: expr = expr.reconstruct(*postops) return expr elif isinstance(expr, Grad): # Apply AD recursively to children f, = expr.operands() f = apply_nested_forward_ad(f, dim) # Apply Grad-specialized AD to expanded child return compute_grad_forward_ad(f, dim) elif isinstance(expr, VariableDerivative): # Apply AD recursively to children f, v = expr.operands() f = apply_nested_forward_ad(f, dim) # Apply Variable-specialized AD to expanded child return compute_variable_forward_ad(f, v, dim) elif isinstance(expr, CoefficientDerivative): # Apply AD recursively to children f, w, v, cd = expr.operands() f = apply_nested_forward_ad(f, dim) # Apply Coefficient-specialized AD to expanded child return compute_coefficient_forward_ad(f, w, v, cd, dim) else: error("Unknown type.") # TODO: We could expand only the compound objects that have no rule # before differentiating, to allow the AD to work on a coarser graph class UnusedADRules(object): def _variable_derivative(self, o, f, v): f, fp = f v, vp = v ufl_assert(isinstance(vp, Zero), "TODO: What happens if vp != 0, i.e. v depends the differentiation variable?") # Are there any issues with indices here? Not sure, think through it... oprime = o.reconstruct(fp, v) return (o, oprime) # --- Tensor algebra (compound types) def outer(self, o, a, b): a, ap = a b, bp = b return (o, outer(ap, b) + outer(a, bp)) # FIXME: Not valid for derivatives w.r.t. nonscalar variables! def inner(self, o, a, b): a, ap = a b, bp = b # NB! Using b : ap because derivative axis should be # last, in case of nonscalar differentiation variable! return (o, inner(b, ap) + inner(a, bp)) # FIXME: Not correct, inner requires equal shapes! def dot(self, o, a, b): a, ap = a b, bp = b # NB! Using b . ap because derivative axis should be # last, in case of nonscalar differentiation variable! return (o, dot(b, ap) + dot(a, bp)) def commute(self, o, a): "This should work for all single argument operators that commute with d/dw with w scalar." aprime = a[1] return (o, o.reconstruct(aprime)) # FIXME: Not true for derivatives w.r.t. nonscalar variables... transposed = commute trace = commute deviatoric = commute # --- Compound differential operators, probably do not want... # FIXME: nabla_div, nabla_grad div = commute curl = commute def grad(self, o, a): a, aprime = a cell = aprime.cell() if cell is None: # FIXME oprime = self._make_zero_diff(o) else: oprime = o.reconstruct(aprime) return (o, oprime) class UnimplementedADRules(object): def cross(self, o, a, b): error("Derivative of cross product not implemented, apply expand_compounds before AD.") u, up = a v, vp = b #oprime = ... return (o, oprime) def determinant(self, o, a): error("Derivative of determinant not implemented, apply expand_compounds before AD.") A, Ap = a #oprime = ... return (o, oprime) def cofactor(self, o, a): error("Derivative of cofactor not implemented, apply expand_compounds before AD.") A, Ap = a #cofacA_prime = detA_prime*Ainv + detA*Ainv_prime #oprime = ... return (o, oprime) def inverse(self, o, a): """Derivation: 0 = d/dx [Ainv*A] = Ainv' * A + Ainv * A' Ainv' * A = - Ainv * A' Ainv' = - Ainv * A' * Ainv """ A, Ap = a return (o, -o*Ap*o) # Any potential index problems here? ufl-1.3.0/ufl/algorithms/graph.py000066400000000000000000000310531226300046600167140ustar00rootroot00000000000000"""Algorithms for working with linearized computational graphs.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-12-28 # Last changed: 2012-04-12 from collections import defaultdict from itertools import imap, izip from heapq import heapify, heappop, heappush #from ufl import * from ufl.algorithms.traversal import fast_pre_traversal from ufl.algorithms.printing import tree_format from ufl.algorithms.multifunction import MultiFunction from ufl.classes import Terminal # O(n) = O(|V|) = O(|E|), since |E| < c|V| for a fairly small c. #--- Basic utility functions --- def lists(n): return [[] for i in xrange(n)] def len_items(sequence): return map(len, sequence) def join_lines(sequence): return "\n".join(imap(str, sequence)) def all_is(seq1, seq2): return all(a is b for (a, b) in izip(seq1, seq2)) def reorder(sequence, ordering): "Rearrange the items in a sequence." return [sequence[i] for i in ordering] #--- Graph building functions --- def build_graph(expr): # O(n) """Build a linearized graph from an UFL Expr. Returns G = (V, E), with V being a list of graph nodes (Expr objects) in post traversal ordering and E being a list of edges. Each edge is represented as a (i, j) tuple where i and j are vertex indices into V. """ V = [] E = [] handled = {} #for v in post_traversal(expr): for v in reversed(list(fast_pre_traversal(expr))): i = handled.get(v) if i is None: i = len(V) handled[v] = i V.append(v) for o in v.operands(): j = handled[o] e = (i, j) E.append(e) G = V, E return G def extract_incoming_edges(G): # O(n) "Build lists of incoming edges to each vertex in a linearized graph." V, E = G n = len(V) Ein = lists(n) for i, e in enumerate(E): Ein[e[1]].append(i) return Ein def extract_outgoing_edges(G): # O(n) "Build list of outgoing edges from each vertex in a linearized graph." V, E = G n = len(V) Eout = lists(n) for i, e in enumerate(E): Eout[e[0]].append(i) return Eout def extract_edges(G): # O(n) """Build lists of incoming and outgoing edges to and from each vertex in a linearized graph. Returns lists Ein and Eout.""" V, E = G n = len(V) Ein = lists(n) Eout = lists(n) for i, e in enumerate(E): a, b = e Ein[b].append(i) Eout[a].append(i) return Ein, Eout def extract_incoming_vertex_connections(G): # O(n) """Build lists of vertices in incoming and outgoing edges to and from each vertex in a linearized graph. Returns lists Vin and Vout.""" V, E = G n = len(V) Vin = lists(n) for a, b in E: Vin[b].append(a) return Vin def extract_outgoing_vertex_connections(G): # O(n) """Build lists of vertices in incoming and outgoing edges to and from each vertex in a linearized graph. Returns lists Vin and Vout.""" V, E = G n = len(V) Vout = lists(n) for a, b in E: Vout[a].append(b) return Vout def extract_vertex_connections(G): # O(n) """Build lists of vertices in incoming and outgoing edges to and from each vertex in a linearized graph. Returns lists Vin and Vout.""" V, E = G n = len(V) Vin = lists(n) Vout = lists(n) for a, b in E: Vin[b].append(a) Vout[a].append(b) return Vin, Vout #--- Graph class --- class Graph: "Graph class which computes connectivity on demand." def __init__(self, expression): self._V, self._E = build_graph(expression) self._Ein = None self._Eout = None self._Vin = None self._Vout = None def V(self): return self._V def E(self): return self._E def Ein(self): if self._Ein is None: self._Ein = extract_incoming_edges((self._V, self._E)) return self._Ein def Eout(self): if self._Eout is None: self._Eout = extract_outgoing_edges((self._V, self._E)) return self._Eout def Vin(self): if self._Vin is None: self._Vin = extract_incoming_vertex_connections((self._V, self._E)) return self._Vin def Vout(self): if self._Vout is None: self._Vout = extract_outgoing_vertex_connections((self._V, self._E)) return self._Vout def __iter__(self): return iter((self._V, self._E)) #--- Graph algorithms --- def format_graph(G): V, E = G lines = ["Graph with %d vertices and %d edges:" % (len(V), sum(map(len, E)))] lines.extend(("", "Vertices:")) lines.extend("v%d: \t%s" % (i, v) for (i, v) in enumerate(V)) lines.extend(("", "Edges:")) lines.extend("e%d: \tv%d -> v%d" % (i, e[0], e[1]) for (i, e) in enumerate(E)) return join_lines(lines) def find_dependencies(G, targets): """Find the set of vertices in a computational graph that a set of target vertices depend on.""" # G is a graph V, E = G n = len(V) # targets is a sequence of vertex indices todo = list(targets) heapify(todo) keep = [False]*n while todo: t = heappop(todo) if not keep[t]: keep[t] = True for edges in Eout[t]: for e in edges: heappush(todo, e[1]) return keep #--- Scheduling algorithms --- class HeapItem(object): def __init__(self, incoming, outgoing, i): self.incoming = incoming self.outgoing = outgoing self.i = i def __lt__(self, other): a = (self.outgoing[self.i], self.incoming[self.i]) b = (other.outgoing[other.i], other.incoming[other.i]) return a < b def __le__(self, other): a = (self.outgoing[self.i], self.incoming[self.i]) b = (other.outgoing[other.i], other.incoming[other.i]) return a <= b def __eq__(self, other): a = (self.outgoing[self.i], self.incoming[self.i]) b = (other.outgoing[other.i], other.incoming[other.i]) return a == b def depth_first_ordering(G): V, E = G Vin = G.Vin() Vout = G.Vout() Ein_count = len_items(Vin) Eout_count = len_items(Vout) # Make a list and a heap of the same items n = len(V) q = [HeapItem(Ein_count, Eout_count, i) for i in xrange(n)] heapify(q) ordering = [] while q: item = heappop(q) iv = item.i ordering.append(iv) for i in Vin[iv]: Eout_count[i] -= 1 # Resort heap, worst case linear time, makes this algorithm O(n^2)... TODO: Not good! heapify(q) # TODO: Can later accumulate dependencies as well during dft-like algorithm. return ordering #--- Expression tree reconstruction algorithms --- def rebuild_tree(G): """Rebuild expression tree from linearized graph. Does not touch the input graph. Assumes the graph is directed, acyclic, and connected, such that there is only one root node. """ V = G.V() E = G.E() n = len(V) Vout = G.Vout() dfo = depth_first_ordering(G) subtrees = [None]*n for i in dfo: v = V[i] if not isinstance(v, Terminal): # Fetch already reconstructed child vertices # and reconstruct non-terminal node from them ops = tuple(subtrees[j] for j in Vout[i]) if all_is(ops, v.operands()): pass else: v = v.reconstruct(*ops) subtrees[i] = v # Assuming last vertex is the root! return v #--- Graph partitoning --- class StringDependencyDefiner(MultiFunction): """Given an expr, returns a frozenset of its dependencies. Possible dependency values are: "c" - depends on runtime information like the cell, local<->global coordinate mappings, facet normals, or coefficients "x" - depends on local coordinates "v%d" % i - depends on argument i, for i in [0,rank) """ def __init__(self, argument_deps = None, coefficient_deps = None): MultiFunction.__init__(self) if argument_deps is None: argument_deps = {} if coefficient_deps is None: coefficient_deps = {} self.argument_deps = argument_deps self.coefficient_deps = coefficient_deps def expr(self, o): return frozenset() def argument(self, x): default = frozenset(("v%d" % x.count(), "x")) return self.argument_deps.get(x, default) def coefficient(self, x): default = frozenset(("c", "x")) return self.coefficient_deps.get(x, default) def constant(self, x): default = frozenset(("c",)) return self.coefficient_deps.get(x, default) def geometric_quantity(self, x): deps = frozenset(("c", "x",)) return deps def facet_normal(self, o): deps = frozenset(("c",)) # Enabling coordinate dependency for higher order geometries # (not handled anywhere else though, so consider this experimental) #if o.has_higher_degree_cell_geometry(): # deps = deps | frozenset(("x",)) return deps def spatial_derivative(self, o): # TODO: What about (basis) functions of which derivatives are constant? Should special-case spatial_derivative in partition(). deps = frozenset(("c",)) # Enabling coordinate dependency for higher order geometries (not handled anywhere else though). #if o.has_higher_degree_cell_geometry(): # deps = deps | frozenset(("x",)) return deps dd = StringDependencyDefiner() def string_set_criteria(v, keys): # Using sets of ufl objects key = dd(v) for k in keys: key |= k return frozenset(key) def partition(G, criteria=string_set_criteria): V, E = G n = len(V) Vout = G.Vout() partitions = defaultdict(list) keys = [None]*n for iv, v in enumerate(V): # Get keys from all outgoing edges edge_keys = [keys[ii] for ii in Vout[iv]] # Calculate key for this vertex key = criteria(v, edge_keys) # Store mappings from key to vertex and back partitions[key].append(iv) keys[iv] = key return partitions, keys #--- Test code --- def test_expr(): from ufl import triangle, FiniteElement, TestFunction, TrialFunction, Coefficient element = FiniteElement("CG", triangle, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) g = Coefficient(element) expr = (f+g)*u.dx(0)*(g-1)*v return expr if __name__ == "__main__": expr = test_expr() G = Graph(expr) V, E = G n = len(V) Ein = G.Ein() Eout = G.Eout() print print "Entire graph:" for iv, v in enumerate(V): print "Vertex %03d: %s" % (iv, v) for ie, e in enumerate(E): print "Edge %03d: %s" % (ie, e) for iv, eout in enumerate(Eout): print "Edges out for vertex %03d: %s" % (iv, eout) for iv, ein in enumerate(Ein): print "Edges in for vertex %03d: %s" % (iv, ein) print from ufl.common import sstr partitions, keys = partition(G, string_set_criteria) for k in partitions: print print "Partition with key", sstr(k) for iv in partitions[k]: print "Vertex %03d: %s" % (iv, V[iv]) if __name__ == "__main_": expr = test_expr() G = Graph(expr) V, E = G e2 = rebuild_tree(G) Ein = G.Ein() Eout = G.Eout() Ein_count = len_items(Ein) Eout_count = len_items(Eout) dfo = depth_first_ordering(G) print print "expr:" print expr print print "e2:" print e2 print print tree_format(expr) print print format_graph(G) print print "Ein:" print join_lines(Ein) print print "Eout:" print join_lines(Eout) print print "Ein_count:" print join_lines(Ein_count) print print "Eout_count:" print join_lines(Eout_count) print print "dfo:" print join_lines(reorder(V, dfo)) ufl-1.3.0/ufl/algorithms/latextools.py000066400000000000000000000072011226300046600200070ustar00rootroot00000000000000"This module defines basic utilities for stitching together LaTeX documents." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-05-07 # Last changed: 2011-06-02 # --- Basic LaTeX tools --- documenttemplate = """\ \\documentclass{article} \\usepackage{amsmath} \\title{%s} \\begin{document} \\maketitle{} %s \\end{document} """ sectiontemplate = """\ \\section{%s} %s """ subsectiontemplate = """\ \\subsection{%s} %s """ subsubsectiontemplate = """\ \\subsubsection{%s} %s """ itemizetemplate = """\ \\begin{itemize} %s \\end{itemize} """ aligntemplate = """\ \\begin{align} %s \\end{align} """ verbatimtemplate = """\ \\begin{verbatim} %s \\end{verbatim} """ def verbatim(string): return verbatimtemplate % string def align(lines): # Calculate column lengths if isinstance(lines[0], str): body = " \\\\\n".join(l for l in lines) else: n = len(lines[0]) collengths = [0]*n for l in lines: for i,s in enumerate(l): collengths[i] = max(collengths[i], len(s)) def coljoin(cols): return " & ".join(c.ljust(collengths[i]) for (i,c) in enumerate(cols)) body = " \\\\\n".join(coljoin(l) for l in lines) return aligntemplate % body def itemize(items): body = "\n".join(items) return itemizetemplate % body def subsubsection(s): if isinstance(s, str): return s if isinstance(s, tuple): title, body = s if isinstance(body, list): body = itemize(map(str,body)) return subsubsectiontemplate % (title, body) def subsection(s): if isinstance(s, str): return s if isinstance(s, tuple): title, body = s if isinstance(body, list): body = "\n".join(subsubsection(ss) for ss in body) return subsectiontemplate % (title, body) def section(s): if isinstance(s, str): return s if isinstance(s, tuple): title, body = s if isinstance(body, list): body = "\n".join(subsection(ss) for ss in body) return sectiontemplate % (title, body) def document(title, sections): body = "\n".join(section(s) for s in sections) return documenttemplate % (title, body) def testdocument(): title = "Test title 1" sections = ["sec1", "sec2"] print document(title, sections) title = "Test title 2" sections = [("sec1", "secbody1"), ("sec2", "secbody2")] print document(title, sections) title = "Test title 3" section1 = [("subsec1", "subsecbody1"), ("subsec2", "subsecbody2")] section2 = [("subsec1", "subsecbody1"), ("subsec2", "subsecbody2"), ("subsec3", "subsecbody3"), ] section3 = "\\section{manual sec}\ntestelest" sections = [("sec1", section1), ("sec2", section2), ("sec3", section3)] print document(title, sections) matrix = [ ("a(...) ", "= \\int_\\Omega foo dx0"), ("", "+ \\int_\\Omega foo dx1"), ("", "+ \\int_\\Omega foo dx1"), ] print align(matrix) ufl-1.3.0/ufl/algorithms/multifunction.py000066400000000000000000000046321226300046600205160ustar00rootroot00000000000000"""Multifunctions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-05-07 # Last changed: 2012-04-10 from ufl.log import error from ufl.classes import all_ufl_classes class MultiFunction(object): """Base class for collections of nonrecursive expression node handlers.""" _handlers_cache = {} def __init__(self): # Analyse class properties and cache handler data the # first time this is run for a particular class cache_data = MultiFunction._handlers_cache.get(type(self)) if not cache_data: cache_data = [None]*len(all_ufl_classes) # For all UFL classes for classobject in all_ufl_classes: # Iterate over the inheritance chain # (NB! This assumes that all UFL classes inherits from # a single Expr subclass and that the first superclass # is always from the UFL Expr hierarchy!) for c in classobject.mro(): # Register classobject with handler for the first encountered superclass name = c._handlername if getattr(self, name, None): cache_data[classobject._classid] = name break MultiFunction._handlers_cache[type(self)] = cache_data # Build handler list for this particular class (get functions bound to self) self._handlers = [getattr(self, name) for name in cache_data] def __call__(self, o, *args, **kwargs): h = self._handlers[o._classid] return h(o, *args, **kwargs) def undefined(self, o): "Trigger error." error("No handler defined for %s." % o._uflclass.__name__) # Set default behaviour for any Expr expr = undefined ufl-1.3.0/ufl/algorithms/pdiffs.py000066400000000000000000000173461226300046600170770ustar00rootroot00000000000000"""This module defines partial differentiation rules for all relevant operands for use with reverse mode AD.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard, 2011 # # First added: 2009-01-06 # Last changed: 2012-04-12 from math import pi from ufl.log import error from ufl.assertions import ufl_assert from ufl.classes import Zero, IntValue from ufl.operators import cos, sin, cosh, sinh, exp, ln, sqrt, conditional, sign from ufl.tensors import unit_vectors, ListTensor from ufl.algorithms.multifunction import MultiFunction class PartialDerivativeComputer(MultiFunction): """NB! The main reason for keeping this out of the Expr hierarchy is to avoid user mistakes in the form of mixups with total derivatives, and to allow both reverse and forward mode AD.""" #def __init__(self, spatial_dim): #self._spatial_dim = spatial_dim def __init__(self): MultiFunction.__init__(self) # TODO: Make sure we have implemented partial derivatives of all operators. # At least non-compound ones should be covered, but compound ones # may be a good idea in future versions. def expr(self, o): error("No partial derivative defined for %s" % type(o)) # --- Basic algebra operators def index_sum(self, f): "d/dx sum_j x = TODO" TODO def sum(self, f): "d/dx_i sum_j x_j = 1" #_1 = IntValue(1, o.free_indices(), o.index_dimensions()) _1 = IntValue(1) # TODO: Handle non-scalars return (_1,)*len(f.operands()) def product(self, f): a, b = f.operands() # TODO: Assuming binary operator for now da = b # TODO: Is this right even for non-scalar b? db = a return (da, db) def division(self, f): """f = x/y d/dx x/y = 1/y d/dy x/y = -x/y**2 = -f/y""" x, y = f.operands() # Nonscalar x not supported ufl_assert(x.shape() == (), "Expecting scalars in division.") ufl_assert(y.shape() == (), "Expecting scalars in division.") d = 1 / y return (d, -f*d) def power(self, f): """f = x**y d/dx x**y = y*x**(y-1) = y*f/x d/dy x**y = ln(x)*x**y = ln(x)*f""" x, y = f.operands() dx = y*f/x dy = ln(x)*f return (dx, dy) def abs(self, f): r".. math:: \\frac{d}{dx} f(x) = \\frac{d}{dx} abs(x) = sign(x)" x, = f.operands() dx = sign(x) return (dx,) # --- Mathfunctions def sqrt(self, f): "d/dx sqrt(x) = 1 / (2*sqrt(x))" return (0.5/f,) def exp(self, f): "d/dx exp(x) = exp(x)" return (f,) def ln(self, f): "d/dx ln x = 1 / x" x, = f.operands() return (1/x,) def cos(self, f): "d/dx cos x = -sin(x)" x, = f.operands() return (-sin(x),) def sin(self, f): "d/dx sin x = cos(x)" x, = f.operands() return (cos(x),) def tan(self, f): "d/dx tan x = (sec(x))^2 = 2/(cos(2x) + 1)" x, = f.operands() return (2.0/(cos(2.0*x) + 1.0),) def cosh(self, f): "d/dx cosh x = sinh(x)" x, = f.operands() return (sinh(x),) def sinh(self, f): "d/dx sinh x = cosh(x)" x, = f.operands() return (cosh(x),) def tanh(self, f): "d/dx tanh x = (sech(x))^2 = (2 cosh(x) / (cosh(2x) + 1))^2" x, = f.operands() return (((2.0*cosh(x))/(cosh(2.0*x) + 1.0))**2,) def acos(self, f): r".. math:: \\frac{d}{dx} f(x) = \frac{d}{dx} \arccos(x) = \frac{-1}{\sqrt{1 - x^2}}" x, = f.operands() return (-1.0/sqrt(1.0 - x**2),) def asin(self, f): "d/dx asin x = 1/sqrt(1 - x^2)" x, = f.operands() return (1.0/sqrt(1.0 - x**2),) def atan(self, f): "d/dx atan x = 1/(1 + x^2)" x, = f.operands() return (1.0/(1.0 + x**2),) def atan_2(self, f): """ f = atan2(x,y) d/dx atan2(x,y) = y / (x**2 + y**2 ) d/dy atan2(x,y) = -x / (x**2 + y**2) """ x,y = f.operands() d = x**2 + y**2 return (y/d,-x/d) def erf(self, f): "d/dx erf x = 2/sqrt(pi)*exp(-x^2)" x, = f.operands() return (2.0/sqrt(pi)*exp(-x**2),) def bessel_function(self, nu, x): return NotImplemented # --- Shape and indexing manipulators def indexed(self, f): # TODO: Is this right? Fix for non-scalars too. "d/dx x_i = (1)_i = 1" s = f.shape() ufl_assert(s == (), "TODO: Assuming a scalar expression.") _1 = IntValue(1) # TODO: Non-scalars return (_1, None) def list_tensor(self, f): # TODO: Is this right? Fix for higher order tensors too. "d/dx_i [x_0, ..., x_n-1] = e_i (unit vector)" ops = f.operands() n = len(ops) s = ops[0].shape() ufl_assert(s == (), "TODO: Assuming a vector, i.e. scalar operands.") return unit_vectors(n) # TODO: Non-scalars def component_tensor(self, f): x, i = f.operands() s = f.shape() ufl_assert(len(s) == 1, "TODO: Assuming a vector, i.e. scalar operands.") n, = s d = ListTensor([1]*n) # TODO: Non-scalars return (d, None) # --- Restrictions def positive_restricted(self, f): _1 = IntValue(1) return (_1,) # or _1('+')? TODO: is this right? # Note that _1('+') would become 0 with the current implementation def negative_restricted(self, f): _1 = IntValue(1) return (_1,) # or _1('-')? TODO: is this right? def cell_avg(self, f): error("Not sure how to implement partial derivative of this operator at all actually.") def facet_avg(self, f): error("Not sure how to implement partial derivative of this operator at all actually.") # --- Conditionals def condition(self, f): return (None, None) def conditional(self, f): # TODO: Is this right? What about non-scalars? c, a, b = f.operands() s = f.shape() ufl_assert(s == (), "TODO: Assuming scalar valued expressions.") _0 = Zero() _1 = IntValue(1) da = conditional(c, _1, _0) db = conditional(c, _0, _1) return (None, da, db) # --- Derivatives def spatial_derivative(self, f): error("Partial derivative of spatial_derivative not implemented, "\ "when is this called? apply_ad should make sure it isn't called.") x, i = f.operands() return (None, None) def variable_derivative(self, f): error("Partial derivative of variable_derivative not implemented, "\ "when is this called? apply_ad should make sure it isn't called.") x, v = f.operands() return (None, None) def coefficient_derivative(self, f): error("Partial derivative of coefficient_derivative not implemented, "\ "when is this called? apply_ad should make sure it isn't called.") a, w, v = f.operands() return (None, None, None) # Example usage: def pdiffs(exprs): pd = PartialDerivativeComputer() return [pd(e) for e in exprs] ufl-1.3.0/ufl/algorithms/predicates.py000066400000000000000000000071611226300046600177410ustar00rootroot00000000000000"""Functions to check properties of forms and integrals.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-14 # Last changed: 2012-04-12 from ufl.log import warning, debug from ufl.algorithms.traversal import iter_expressions from ufl.algorithms.argument_dependencies import extract_argument_dependencies, NotMultiLinearException #--- Utilities for checking properties of forms --- def is_multilinear(form): "Check if form is multilinear in arguments." # An attempt at implementing is_multilinear using extract_argument_dependencies. # TODO: This has some false negatives for "multiple configurations". (Does it still? Needs testing!) # TODO: FFC probably needs a variant of this which checks for some sorts of linearity # in Coefficients as well, this should be a fairly simple extension of the current algorithm. try: for e in iter_expressions(form): deps = extract_argument_dependencies(e) nargs = [len(d) for d in deps] if len(nargs) == 0: debug("This form is a functional.") if len(nargs) == 1: debug("This form is linear in %d arguments." % nargs[0]) if len(nargs) > 1: warning("This form has more than one argument "\ "'configuration', it has terms that are linear in %s "\ "arguments respectively." % str(nargs)) except NotMultiLinearException, msg: warning("Form is not multilinear, the offending term is: %s" % msg) return False return True # TODO: Remove this code if nobody needs it for anything: #=============================================================================== # def is_multilinear(form): # "Check if form is multilinear." # # # Check that we get a form # ufl_assert(isinstance(form, Form), "Not a form: %s" % str(form)) # # # Check that all operators applied to arguments are linear # for e in iter_expressions(form): # stack = [] # for o in pre_traversal(e, stack): # if isinstance(o, Argument): # for operator in stack: # if not operator.is_linear(): # warning("Nonlinear operator applied to argument:" + str(operator)) # return False # # # Extract monomials # monomials = [] # for e in iter_expressions(form): # monomials += _extract_monomials(e) # # # Extract arguments # arguments = set() # for monomial in monomials: # for v in monomial: # arguments.add(v) # # # Check that each argument appears exactly once in each monomial term # for monomial in monomials: # for v in arguments: # if not len([w for w in monomial if w == v]) == 1: # warning("Argument %s does not appear exactly once in each term." % str(v)) # return False # # return True #=============================================================================== ufl-1.3.0/ufl/algorithms/preprocess.py000066400000000000000000000454421226300046600200070ustar00rootroot00000000000000"""This module provides the preprocess function which form compilers will typically call prior to code generation to preprocess/simplify a raw input form given by a user.""" # Copyright (C) 2009-2013 Anders Logg and Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-12-07 # Last changed: 2012-04-12 from itertools import chain from time import time import ufl from ufl.log import error from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.form import Form from ufl.common import slice_dict from ufl.geometry import Cell from ufl.domains import as_domain from ufl.algorithms.renumbering import renumber_indices from ufl.algorithms.replace import replace from ufl.algorithms.analysis import (extract_arguments_and_coefficients, build_argument_replace_map, extract_elements, extract_sub_elements, unique_tuple, _domain_types, extract_num_sub_domains, extract_domain_data) from ufl.algorithms.domain_analysis import ( integral_dict_to_sub_integral_data, print_sub_integral_data, reconstruct_form_from_sub_integral_data, convert_sub_integral_data_to_integral_data) from ufl.algorithms.formdata import FormData, ExprData from ufl.algorithms.expand_indices import expand_indices from ufl.algorithms.ad import expand_derivatives from ufl.algorithms.propagate_restrictions import propagate_restrictions from ufl.algorithms.formtransformations import compute_form_arities from ufl.algorithms.signature import compute_expression_signature, compute_form_signature class Timer: def __init__(self, name): self.name = name self.times = [] self('begin %s' % self.name) def __call__(self, msg): self.times.append((time(), msg)) def end(self): self('end %s' % self.name) def __str__(self): line = "-"*60 s = [line, "Timing of %s" % self.name] for i in range(len(self.times)-1): t = self.times[i+1][0] - self.times[i][0] msg = self.times[i][1] s.append("%9.2e s %s" % (t, msg)) s.append('Total time: %9.2e s' % (self.times[-1][0] - self.times[0][0])) s.append(line) return '\n'.join(s) def preprocess(form, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input form to obtain form metadata, including a modified (preprocessed) form more easily manipulated by form compilers. The original form is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess') # TODO: Reposition tic calls after refactoring. # Check that we get a form ufl_assert(isinstance(form, Form), "Expecting Form.") original_form = form # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty form data form_data = FormData() # Store copies of preprocess input data, for future validation if called again... form_data._input_object_names = dict(object_names) form_data._input_element_mapping = dict(element_mapping) #form_data._input_common_cell = no need to store this # Store name of form if given, otherwise empty string # such that automatic names can be assigned externally form_data.name = object_names.get(id(form), "") # Extract common cell common_cell = extract_common_cell(form, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') form = expand_derivatives(form, common_cell.geometric_dimension()) # EXPR # Propagate restrictions of interior facet integrals to the terminal nodes form = propagate_restrictions(form) # INTEGRAL, EXPR # --- BEGIN DOMAIN SPLITTING AND JOINING # Store domain metadata per domain type form_data.domain_data = extract_domain_data(form) # Split up integrals and group by domain type and domain id, # adding integrands with same domain and compiler data sub_integral_data = integral_dict_to_sub_integral_data(form.integral_groups()) # TODO: Replace integral_data with this through ufl and ffc? if 0: print_sub_integral_data(sub_integral_data) # Reconstruct form from these integrals in a more canonical representation form = reconstruct_form_from_sub_integral_data(sub_integral_data, form_data.domain_data) # Represent in the way ffc expects TODO: Change ffc? Fine for now. form_data.integral_data = convert_sub_integral_data_to_integral_data(sub_integral_data) # --- END DOMAIN SPLITTING AND JOINING # --- BEGIN FUNCTION ANALYSIS # --- BEGIN SPLIT EXPR JOIN # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(form) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') # TODO: Remove renumbered ones? replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) # Note: This is the earliest point signature can be computed # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # TODO: Build mapping from object to position instead? But we need mapped elements as well anyway. #argument_positions = { v: i } #coefficient_positions = { w: i } # Store data extracted by preprocessing form_data.original_arguments = original_arguments form_data.original_coefficients = original_coefficients # --- END SPLIT INTEGRAL JOIN # Mappings from elements and functions (coefficients and arguments) # that reside in form to objects with canonical numbering as well as # completed cells and elements # TODO: Create additional function mappings per integral, # to have different counts? Depends on future UFC design. form_data.element_replace_map = element_mapping form_data.function_replace_map = replace_map # Store some useful dimensions form_data.rank = len(form_data.original_arguments) form_data.num_coefficients = len(form_data.original_coefficients) # Store argument names form_data.argument_names = \ [object_names.get(id(form_data.original_arguments[i]), "v%d" % i) for i in range(form_data.rank)] # Store coefficient names form_data.coefficient_names = \ [object_names.get(id(form_data.original_coefficients[i]), "w%d" % i) for i in range(form_data.num_coefficients)] # --- END FUNCTION ANALYSIS # --- BEGIN SIGNATURE COMPUTATION # TODO: Compute signatures of each INTEGRAL and EXPR as well, perhaps compute it hierarchially from integral_data? # Store signature of form tic('signature') # TODO: Remove signature() from Form, not safe to cache with a replacement map #form_data.signature = form.signature(form_data.function_replace_map) form_data.signature = compute_form_signature(form, form_data.function_replace_map) # --- END SIGNATURE COMPUTATION # --- BEGIN CONSISTENCY CHECKS # Check that we don't have a mixed linear/bilinear form or anything like that ufl_assert(len(compute_form_arities(form)) == 1, "All terms in form must have same rank.") # --- END CONSISTENCY CHECKS # --- BEGIN ELEMENT DATA # Store elements, sub elements and element map tic('extract_elements') form_data.argument_elements = tuple(f.element() for f in renumbered_arguments) form_data.coefficient_elements = tuple(f.element() for f in renumbered_coefficients) form_data.elements = form_data.argument_elements + form_data.coefficient_elements form_data.unique_elements = unique_tuple(form_data.elements) form_data.sub_elements = extract_sub_elements(form_data.elements) form_data.unique_sub_elements = unique_tuple(form_data.sub_elements) # --- END ELEMENT DATA # --- BEGIN DOMAIN DATA # Store element domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.domains = tuple(sorted(set(element.domain() for element in form_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in form_data.domains))) # Store common cell form_data.cell = common_cell # Store data related to cell form_data.geometric_dimension = form_data.cell.geometric_dimension() form_data.topological_dimension = form_data.cell.topological_dimension() # Store number of domains for integral types form_data.num_sub_domains = extract_num_sub_domains(form) # --- END DOMAIN DATA # A coarse profiling implementation TODO: Add counting of nodes, Add memory usage tic.end() if preprocess.enable_profiling: print tic # Store preprocessed form and return form_data.preprocessed_form = form form_data.preprocessed_form._is_preprocessed = True # Attach signatures to original and preprocessed forms TODO: Avoid this? ufl_assert(form_data.preprocessed_form._signature is None, "") form_data.preprocessed_form._signature = form_data.signature ufl_assert(original_form._signature is None, "") original_form._signature = form_data.signature return form_data preprocess.enable_profiling = False def preprocess_expression(expr, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input expression to obtain expression metadata, including a modified (preprocessed) expression more easily manipulated by expression compilers. The original expression is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess_expression') # Check that we get an expression ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty expression data expr_data = ExprData() # Store original expression expr_data.original_expr = expr # Store name of expr if given, otherwise empty string # such that automatic names can be assigned externally expr_data.name = object_names.get(id(expr), "") # TODO: Or default to 'expr'? # Extract common cell common_cell = extract_common_cell(expr, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') expr = expand_derivatives(expr, common_cell.geometric_dimension()) # Renumber indices #expr = renumber_indices(expr) # TODO: No longer needed? # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(expr) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) expr = replace(expr, replace_map) # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # Store data extracted by preprocessing expr_data.arguments = renumbered_arguments # TODO: Needed? expr_data.coefficients = renumbered_coefficients # TODO: Needed? expr_data.original_arguments = original_arguments expr_data.original_coefficients = original_coefficients expr_data.renumbered_arguments = renumbered_arguments expr_data.renumbered_coefficients = renumbered_coefficients tic('replace') # Mappings from elements and functions (coefficients and arguments) # that reside in expr to objects with canonical numbering as well as # completed cells and elements expr_data.element_replace_map = element_mapping expr_data.function_replace_map = replace_map # Store signature of form tic('signature') expr_data.signature = compute_expression_signature(expr, expr_data.function_replace_map) # Store elements, sub elements and element map tic('extract_elements') expr_data.elements = tuple(f.element() for f in chain(renumbered_arguments, renumbered_coefficients)) expr_data.unique_elements = unique_tuple(expr_data.elements) expr_data.sub_elements = extract_sub_elements(expr_data.elements) expr_data.unique_sub_elements = unique_tuple(expr_data.sub_elements) # Store element domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.domains = tuple(sorted(set(element.domain() for element in expr_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in expr_data.domains))) # Store common cell expr_data.cell = common_cell # Store data related to cell expr_data.geometric_dimension = expr_data.cell.geometric_dimension() expr_data.topological_dimension = expr_data.cell.topological_dimension() # Store some useful dimensions expr_data.rank = len(expr_data.arguments) # TODO: Is this useful for expr? expr_data.num_coefficients = len(expr_data.coefficients) # Store argument names # TODO: Is this useful for expr? expr_data.argument_names = \ [object_names.get(id(expr_data.original_arguments[i]), "v%d" % i) for i in range(expr_data.rank)] # Store coefficient names expr_data.coefficient_names = \ [object_names.get(id(expr_data.original_coefficients[i]), "w%d" % i) for i in range(expr_data.num_coefficients)] # Store preprocessed expression expr_data.preprocessed_expr = expr tic.end() # A coarse profiling implementation # TODO: Add counting of nodes # TODO: Add memory usage if preprocess_expression.enable_profiling: print tic return expr_data preprocess_expression.enable_profiling = False def extract_common_cell(form, common_cell=None): "Extract common cell for form or expression." # Either use given argument or try to find in form or expression common_cell = common_cell or form.cell() # Check common cell if common_cell is None: error("Unable to extract common cell; "\ "missing cell definition in form or expression.") return common_cell def build_element_mapping(element_mapping, common_cell, arguments, coefficients): """Complete an element mapping for all elements used by arguments and coefficients, using a well defined common cell.""" # Build a new dict to avoid modifying the dict passed from non-ufl code new_element_mapping = {} # Check that the given initial mapping has no invalid entries as values for v in element_mapping.itervalues(): ufl_assert(v.cell() is not None, "Found incomplete element with undefined cell in element mapping.") ufl_assert(v.family() is not None, "Found incomplete element with undefined family in element mapping.") common_domain = as_domain(common_cell) # FIXME: handle better somehow? # Reconstruct all elements we need to map for f in chain(arguments, coefficients): e = f.element() # Prefer the given mapping: new_e = element_mapping.get(e) if new_e is None: if e.cell() is None: # Otherwise complete with domain by reconstructing if cell is missing new_e = e.reconstruct(domain=common_domain) else: # Or just use the original element new_e = e new_element_mapping[e] = new_e # Check that the new mapping has no invalid entries as values for v in new_element_mapping.itervalues(): ufl_assert(v.cell() is not None, "Found incomplete element with undefined cell in new element mapping.") ufl_assert(v.family() is not None, "Found incomplete element with undefined family in new element mapping.") return new_element_mapping ufl-1.3.0/ufl/algorithms/printing.py000066400000000000000000000111441226300046600174440ustar00rootroot00000000000000"""A collection of utility algorithms for printing of UFL objects, mostly intended for debugging purposes.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # # First added: 2008-03-14 # Last changed: 2011-06-02 from itertools import chain from ufl.log import error from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.terminal import Terminal from ufl.form import Form from ufl.integral import Integral, Measure from ufl.algorithms.analysis import extract_arguments, extract_coefficients #--- Utilities for constructing informative strings from UFL objects --- def integral_info(integral): ufl_assert(isinstance(integral, Integral), "Expecting an Integral.") s = " Integral:\n" s += " Measure representation:\n" s += " %r\n" % integral.measure() s += " Integrand expression representation:\n" s += " %r\n" % integral.integrand() s += " Integrand expression short form:\n" s += " %s" % integral.integrand() return s def form_info(form): ufl_assert(isinstance(form, Form), "Expecting a Form.") bf = extract_arguments(form) cf = extract_coefficients(form) ci = form.integrals(Measure.CELL) ei = form.integrals(Measure.EXTERIOR_FACET) ii = form.integrals(Measure.INTERIOR_FACET) pi = form.integrals(Measure.POINT) mi = form.integrals(Measure.MACRO_CELL) s = "Form info:\n" s += " rank: %d\n" % len(bf) s += " num_coefficients: %d\n" % len(cf) s += " num_cell_integrals: %d\n" % len(ci) s += " num_exterior_facet_integrals: %d\n" % len(ei) s += " num_interior_facet_integrals: %d\n" % len(ii) s += " num_point_integrals: %d\n" % len(pi) s += " num_macro_cell_integrals: %d\n" % len(mi) for f in cf: if f._name: s += "\n" s += " Coefficient %d is named '%s'" % (f._count, f._name) s += "\n" for itg in ci: s += "\n" s += integral_info(itg) for itg in ei: s += "\n" s += integral_info(itg) for itg in ii: s += "\n" s += integral_info(itg) for itg in mi: s += "\n" s += integral_info(itg) return s def _indent_string(n): return " "*n def _tree_format_expression(expression, indentation, parentheses): ind = _indent_string(indentation) if isinstance(expression, Terminal): s = ind + "%s" % repr(expression) else: sops = [_tree_format_expression(o, indentation+1, parentheses) for o in expression.operands()] s = ind + "%s\n" % expression._uflclass.__name__ if parentheses and len(sops) > 1: s += ind + "(\n" s += "\n".join(sops) if parentheses and len(sops) > 1: s += "\n" + ind + ")" return s def tree_format(expression, indentation=0, parentheses=True): s = "" if isinstance(expression, Form): ci = expression.integrals(Measure.CELL) ei = expression.integrals(Measure.EXTERIOR_FACET) ii = expression.integrals(Measure.INTERIOR_FACET) pi = expression.integrals(Measure.POINT) mi = expression.integrals(Measure.MACRO_CELL) ind = _indent_string(indentation) s += ind + "Form:\n" s += "\n".join(tree_format(itg, indentation+1, parentheses) for itg in chain(ci, ei, ii, pi, mi)) elif isinstance(expression, Integral): ind = _indent_string(indentation) s += ind + "Integral:\n" ind = _indent_string(indentation+1) s += ind + "domain type: %s\n" % expression.measure().domain_type() s += ind + "domain id: %s\n" % expression.measure().domain_id() s += ind + "integrand:\n" s += tree_format(expression._integrand, indentation+2, parentheses) elif isinstance(expression, Expr): s += _tree_format_expression(expression, indentation, parentheses) else: error("Invalid object type %s" % type(expression)) return s ufl-1.3.0/ufl/algorithms/propagate_restrictions.py000066400000000000000000000106771226300046600224160ustar00rootroot00000000000000"Algorithms related to restrictions." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-05-14 # Last changed: 2012-04-12 from ufl.expr import Expr from ufl.classes import Measure from ufl.assertions import ufl_assert from ufl.algorithms.transformer import Transformer, ReuseTransformer, apply_transformer class RestrictionPropagator(ReuseTransformer): def __init__(self): ReuseTransformer.__init__(self) self.current_restriction = None def restricted(self, o): ufl_assert(self.current_restriction is None, "Not expecting twice restricted expression.") self.current_restriction = o._side e, = o.operands() r = self.visit(e) self.current_restriction = None return r def facet_normal(self, o): ufl_assert(self.current_restriction is not None, "FacetNormal must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) def cell_volume(self, o): ufl_assert(self.current_restriction is not None, "CellVolume must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) def circumradius(self, o): ufl_assert(self.current_restriction is not None, "Circumradius must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) def cell_surface_area(self, o): ufl_assert(self.current_restriction is not None, "CellSurfaceArea must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) # facet_area, facet_diameter, max_facet_edge_length are all the same from both sides of a facet def form_argument(self, o): ufl_assert(self.current_restriction is not None, "Form argument must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) def variable(self, o): ufl_assert(self.current_restriction is not None, "Form argument must be restricted.") #if self.current_restriction is None: # return o return o(self.current_restriction) class RestrictionChecker(Transformer): def __init__(self, require_restriction): Transformer.__init__(self) self.current_restriction = None self.require_restriction = require_restriction def expr(self, o): pass def restricted(self, o): ufl_assert(self.current_restriction is None, "Not expecting twice restricted expression.") self.current_restriction = o._side e, = o.operands() self.visit(e) self.current_restriction = None def facet_normal(self, o): if self.require_restriction: ufl_assert(self.current_restriction is not None, "Facet normal must be restricted in interior facet integrals.") else: ufl_assert(self.current_restriction is None, "Restrictions are only allowed for interior facet integrals.") def form_argument(self, o): if self.require_restriction: ufl_assert(self.current_restriction is not None, "Form argument must be restricted in interior facet integrals.") else: ufl_assert(self.current_restriction is None, "Restrictions are only allowed for interior facet integrals.") def propagate_restrictions(expression): "Propagate restriction nodes to wrap terminal objects directly." return apply_transformer(expression, RestrictionPropagator(), domain_type=Measure.INTERIOR_FACET) def check_restrictions(expression, require_restriction): ufl_assert(isinstance(expression, Expr), "Expecting Expr instance.") return RestrictionChecker(require_restriction).visit(expression) ufl-1.3.0/ufl/algorithms/renumbering.py000066400000000000000000000215631226300046600201350ustar00rootroot00000000000000"Algorithms for renumbering of counted objects, currently variables and indices." # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-02-22 # Last changed: 2012-04-12 from itertools import izip from ufl.common import Stack, StackDict from ufl.log import error from ufl.expr import Expr from ufl.indexing import Index, FixedIndex, MultiIndex from ufl.indexed import Indexed from ufl.indexsum import IndexSum from ufl.variable import Label, Variable from ufl.algorithms.transformer import ReuseTransformer, apply_transformer class VariableRenumberingTransformer(ReuseTransformer): def __init__(self): ReuseTransformer.__init__(self) self.variable_map = {} def variable(self, o): e, l = o.operands() v = self.variable_map.get(l) if v is None: e = self.visit(e) l2 = Label(len(self.variable_map)) v = Variable(e, l2) self.variable_map[l] = v return v class IndexRenumberingTransformer(VariableRenumberingTransformer): def __init__(self): VariableRenumberingTransformer.__init__(self) self.index_map = {} def index_annotated(self, o): new_indices = tuple(map(self.index, o.free_indices())) return o.reconstruct(new_indices) zero = index_annotated scalar_value = index_annotated def multi_index(self, o): new_indices = tuple(map(self.index, o._indices)) idims = o.index_dimensions() new_idims = dict((b, idims[a]) for (a,b) in izip(o._indices, new_indices) if isinstance(a, Index)) return MultiIndex(new_indices, new_idims) def index(self, o): if isinstance(o, FixedIndex): return o c = o._count i = self.index_map.get(c) if i is None: i = Index(len(self.index_map)) self.index_map[c] = i return i # TODO: Concepts in this implementation can handle unique # renumbering of indices used multiple places, like # (v[i]*v[i] + u[i]*u[i]) -> (v[i]*v[i] + u[j]*u[j]) # which could be a useful invariant some other places. # However, there are bugs here. class IndexRenumberingTransformer2(VariableRenumberingTransformer): def __init__(self): VariableRenumberingTransformer.__init__(self) # The number of indices labeled up to now self.index_counter = 0 # A stack of dicts holding an "old Index" -> "new Index" # mapping, with "old Index" -> None meaning undefined in # the current scope. Indices get defined and numbered # in self.index_map = StackDict() # Current component, a tuple of FixedIndex and Index # objects, which are in the new numbering. self.components = Stack() self.components.push(()) def new_index(self): "Create a new index using our contiguous numbering." i = Index(self.index_counter) self.index_counter += 1 #print "::: Making new index", repr(i) return i def define_new_indices(self, ii): #self.define_indices(ii, [self.new_index() for i in ii]) ni = [] for i in ii: v = self.new_index() ni.append(v) #print "::: Defining new index", repr(i), "= ", repr(v) if self.index_map.get(i) is not None: print ";"*80 print i self.print_visit_stack() error("Trying to define already defined index!") self.index_map.push(i, v) return tuple(ni) def define_indices(self, ii, values): for i, v in izip(ii, values): #print "::: Defining index", repr(i), "= ", repr(v) if v is None: if self.index_map.get(i) is None: print ";"*80 print i self.print_visit_stack() error("Trying to undefine index that isn't defined!") else: if self.index_map.get(i) is not None: print ";"*80 print i self.print_visit_stack() error("Trying to define already defined index!") self.index_map.push(i, v) def revert_indices(self, ii): for i in ii: j = self.index_map.pop() #print "::: Reverting index", repr(i), "(j =", repr(j), ")" # as_tensor( # u[i,j] # * v[i] # , j ) # [i] # * ( # u[i,j] # * (v + w)[j]) def index(self, o): if isinstance(o, FixedIndex): return o i = self.index_map.get(o) if i is None: print ";"*80 print o self.print_visit_stack() error("Index %s isn't defined!" % repr(o)) return i def multi_index(self, o): new_indices = tuple(map(self.index, o._indices)) idims = o.index_dimensions() new_idims = dict((b, idims[a]) for (a,b) in izip(o._indices, new_indices) if isinstance(a, Index)) return MultiIndex(new_indices, new_idims) def index_annotated(self, o): new_indices = tuple(map(self.index, o.free_indices())) return o.reconstruct(new_indices) zero = index_annotated scalar_value = index_annotated def expr(self, o, *ops): r = self.reuse_if_possible(o, *ops) c = self.components.peek() if c: #if isinstance(r, ListTensor): # pass # TODO: If c has FixedIndex objects, extract subtensor, or evt. move this functionality from ListTensor.__getitem__ to Indexed.__new__ # Take component r = Indexed(r, c) return r def terminal(self, o): r = o c = self.components.peek() if c: r = Indexed(r, c) return r def _spatial_derivative(self, o, *ops): r = self.reuse_if_possible(o, *ops) return r def _sum(self, o, *ops): r = self.reuse_if_possible(o, *ops) return r def indexed(self, f): """Binds indices to component, ending their scope as free indices. If indices with the same count occur later in a subexpression, they represent new indices in a different scope.""" # Get expression and indices g, ii = f.operands() # Get values of indices c = self.multi_index(ii) # Define indices as missing jj = [i for i in ii if isinstance(i, Index)] jj = tuple(jj) #print "::: NOT defining indices as None:", jj #self.define_indices(jj, (None,)*len(jj)) # Push new component self.components.push(c) # Evaluate expression r = self.visit(g) # Pop component self.components.pop() # Revert indices to previous state #self.revert_indices(jj) return r def index_sum(self, o): "Defines a new index." f, ii = o.operands() ni = self.define_new_indices(ii) g = self.visit(f) r = IndexSum(g, ni) self.revert_indices(ii) return r def component_tensor(self, o): """Maps component to indices.""" f, ii = o.operands() # Read component and push new one c = self.components.peek() self.components.push(()) # Map component to indices self.define_indices(ii, c) # Evaluate! r = self.visit(f) # Pop component to revert to the old self.components.pop() # Revert index definitions self.revert_indices(ii) return r def renumber_indices1(expr): if isinstance(expr, Expr) and expr.free_indices(): error("Not expecting any free indices left in expression.") return apply_transformer(expr, IndexRenumberingTransformer()) def renumber_indices2(expr): if isinstance(expr, Expr) and expr.free_indices(): error("Not expecting any free indices left in expression.") return apply_transformer(expr, IndexRenumberingTransformer2()) renumber_indices = renumber_indices1 def renumber_variables(expr): if isinstance(expr, Expr) and expr.free_indices(): error("Not expecting any free indices left in expression.") return apply_transformer(expr, VariableRenumberingTransformer()) ufl-1.3.0/ufl/algorithms/replace.py000066400000000000000000000043261226300046600172310ustar00rootroot00000000000000"""Algorithm for replacing terminals in an expression.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # # First added: 2008-05-07 # Last changed: 2012-04-12 from ufl.log import error from ufl.assertions import ufl_assert from ufl.classes import Terminal, CoefficientDerivative from ufl.constantvalue import as_ufl from ufl.algorithms.transformer import ReuseTransformer, apply_transformer from ufl.algorithms.analysis import extract_type class Replacer(ReuseTransformer): def __init__(self, mapping): ReuseTransformer.__init__(self) self._mapping = mapping ufl_assert(all(isinstance(k, Terminal) for k in mapping.keys()), \ "This implementation can only replace Terminal objects.") def terminal(self, o): e = self._mapping.get(o) return o if e is None else e def coefficient_derivative(self, o): error("Coefficient derivatives should be expanded before applying replace.") def replace(e, mapping): """Replace terminal objects in expression. @param e: An Expr or Form. @param mapping: A dict with from:to replacements to perform. """ mapping2 = dict((k, as_ufl(v)) for (k,v) in mapping.iteritems()) # TODO: Should this be sorted? # Workaround for problem with delayed derivative evaluation if extract_type(e, CoefficientDerivative): # Hack to avoid circular dependencies from ufl.algorithms.ad import expand_derivatives e = expand_derivatives(e) return apply_transformer(e, Replacer(mapping2)) ufl-1.3.0/ufl/algorithms/signature.py000066400000000000000000000134571226300046600176240ustar00rootroot00000000000000"""Signature computation for forms.""" # Copyright (C) 2012-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2012-03-29 # Last changed: 2012-04-12 import hashlib from ufl.classes import Index, MultiIndex, Coefficient, Argument, Terminal, Label from ufl.log import error from ufl.algorithms.traversal import traverse_terminals2 from ufl.common import fast_pre_traversal, sorted_by_count def compute_multiindex_hashdata(expr, index_numbering): data = [] for i in expr: if isinstance(i, Index): j = index_numbering.get(i) if j is None: # Use negative ints for Index j = -(len(index_numbering)+1) index_numbering[i] = j data.append(j) else: # Use nonnegative ints for FixedIndex data.append(int(i)) return data def compute_terminal_hashdata(expressions, function_replace_map=None): if not isinstance(expressions, list): expressions = [expressions] if function_replace_map is None: function_replace_map = {} # Extract a unique numbering of free indices, # as well as form arguments, and just take # repr of the rest of the terminals while # we're iterating over them terminal_hashdata = {} labels = {} index_numbering = {} coefficients = set() arguments = set() for expression in expressions: for expr in traverse_terminals2(expression): if isinstance(expr, MultiIndex): terminal_hashdata[expr] = compute_multiindex_hashdata(expr, index_numbering) elif isinstance(expr, Coefficient): coefficients.add(expr) elif isinstance(expr, Argument): arguments.add(expr) elif isinstance(expr, Label): data = labels.get(expr) if data is None: data = "L%d" % len(labels) labels[expr] = data terminal_hashdata[expr] = data else: terminal_hashdata[expr] = repr(expr) # Apply renumbering of form arguments # (Note: some duplicated work here and in preprocess, # to allow using this function without preprocess.) coefficients = sorted_by_count(coefficients) arguments = sorted_by_count(arguments) for i, e in enumerate(coefficients): er = function_replace_map.get(e) or e.reconstruct(count=i) terminal_hashdata[e] = repr(er) for i, e in enumerate(arguments): er = function_replace_map.get(e) or e.reconstruct(count=i) terminal_hashdata[e] = repr(er) return terminal_hashdata def compute_expression_hashdata(expression, terminal_hashdata): # The hashdata computed here can be interpreted as # prefix operator notation, i.e. we store the equivalent # of '+ * a b * c d' for the expression (a*b)+(c*d) expression_hashdata = [] for expr in fast_pre_traversal(expression): if isinstance(expr, Terminal): data = terminal_hashdata[expr] else: data = expr._classid expression_hashdata.append(data) return expression_hashdata def compute_expression_signature(expr, function_replace_map=None): # Build hashdata for all terminals first terminal_hashdata = compute_terminal_hashdata([expr], function_replace_map) # Build hashdata for full expression expression_hashdata = compute_expression_hashdata(expr, terminal_hashdata) # Pass it through a seriously overkill hashing algorithm :) TODO: How fast is this? Reduce? return hashlib.sha512(str(expression_hashdata)).hexdigest() def compute_form_signature(form, function_replace_map=None): integrals = form.integrals() integrands = [integral.integrand() for integral in integrals] # Build hashdata for all terminals first, with on-the-fly # replacement of functions and index labels. terminal_hashdata = compute_terminal_hashdata(integrands, function_replace_map) # Build hashdata for each integral hashdata = [] for integral in integrals: # Compute hash data for expression, this is the expensive part expression_hashdata = compute_expression_hashdata(integral.integrand(), terminal_hashdata) # Collect all data about integral that should be reflected in signature, # including compiler data but not domain data, because compiler data # affects the way the integral is compiled while domain data is only # carried for convenience in the problem solving environment. integral_hashdata = (expression_hashdata, integral.domain_type(), integral.domain_id(), repr(integral.compiler_data()), ) hashdata.append(integral_hashdata) # Pass hashdata through a seriously overkill hashing algorithm :) TODO: How fast is this? Reduce? return hashlib.sha512(str(hashdata)).hexdigest() ufl-1.3.0/ufl/algorithms/transformations.py000066400000000000000000000041101226300046600210360ustar00rootroot00000000000000"""This module defines expression transformation utilities, either converting UFL expressions to new UFL expressions or converting UFL expressions to other representations.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # # First added: 2008-05-07 # Last changed: 2012-04-12 # --- BEGIN dummy imports to keep imports in external code working: from ufl.algorithms.multifunction import MultiFunction from ufl.algorithms.transformer import Transformer, is_post_handler from ufl.algorithms.transformer import transform, transform_integrands, apply_transformer from ufl.algorithms.transformer import ReuseTransformer, ufl2ufl from ufl.algorithms.transformer import CopyTransformer, ufl2uflcopy from ufl.algorithms.transformer import VariableStripper, strip_variables from ufl.algorithms.replace import Replacer, replace from ufl.algorithms.expand_compounds import CompoundExpander, expand_compounds from ufl.algorithms.estimate_degrees import SumDegreeEstimator, estimate_total_polynomial_degree from ufl.algorithms.argument_dependencies import ArgumentDependencyExtracter, extract_argument_dependencies, NotMultiLinearException from ufl.algorithms.deprecated import TreeFlattener, flatten from ufl.algorithms.deprecated import DuplicationMarker, mark_duplications from ufl.algorithms.deprecated import DuplicationPurger, purge_duplications # --- END dummy imports to keep imports in external code working. ufl-1.3.0/ufl/algorithms/transformer.py000066400000000000000000000240651226300046600201620ustar00rootroot00000000000000"""This module defines the Transformer base class and some basic specializations to further base other algorithms upon, as well as some utilities for easier application of such algorithms.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010 # # First added: 2008-05-07 # Last changed: 2012-04-12 from inspect import getargspec from ufl.log import error, debug from ufl.assertions import ufl_assert from ufl.form import Form from ufl.integral import Integral from ufl.classes import Expr, Terminal, Variable, Zero, all_ufl_classes def is_post_handler(function): "Is this a handler that expects transformed children as input?" insp = getargspec(function) num_args = len(insp[0]) + int(insp[1] is not None) visit_children_first = num_args > 2 return visit_children_first class Transformer(object): """Base class for a visitor-like algorithm design pattern used to transform expression trees from one representation to another.""" _handlers_cache = {} def __init__(self, variable_cache=None): if variable_cache is None: variable_cache = {} self._variable_cache = variable_cache # Analyse class properties and cache handler data the # first time this is run for a particular class cache_data = Transformer._handlers_cache.get(type(self)) if not cache_data: cache_data = [None]*len(all_ufl_classes) # For all UFL classes for classobject in all_ufl_classes: # Iterate over the inheritance chain (NB! This assumes that all UFL classes inherits a single Expr subclass and that this is the first superclass!) for c in classobject.mro(): # Register classobject with handler for the first encountered superclass name = c._handlername function = getattr(self, name, None) if function: cache_data[classobject._classid] = name, is_post_handler(function) break Transformer._handlers_cache[type(self)] = cache_data # Build handler list for this particular class (get functions bound to self) self._handlers = [(getattr(self, name), post) for (name, post) in cache_data] # Keep a stack of objects visit is called on, to ease backtracking self._visit_stack = [] def print_visit_stack(self): print "/"*80 print "Visit stack in Transformer:" def sstr(s): ss = str(type(s)) + " ; " n = 160 - len(ss) return ss + str(s)[:n] print "\n".join(sstr(s) for s in self._visit_stack) print "\\"*80 def visit(self, o): #debug("Visiting object of type %s." % type(o).__name__) # Update stack self._visit_stack.append(o) # Get handler for the UFL class of o (type(o) may be an external subclass of the actual UFL class) h, visit_children_first = self._handlers[o._classid] #if not h: # # Failed to find a handler! Should never happen, but will happen if a non-Expr object is visited. # error("Can't handle objects of type %s" % str(type(o))) # Is this a handler that expects transformed children as input? if visit_children_first: # Yes, visit all children first and then call h. r = h(o, *map(self.visit, o.operands())) else: # No, this is a handler that handles its own children # (arguments self and o, where self is already bound) r = h(o) # Update stack and return self._visit_stack.pop() return r def undefined(self, o): "Trigger error." error("No handler defined for %s." % o._uflclass.__name__) def reuse(self, o): "Always reuse Expr (ignore children)" return o def reuse_if_possible(self, o, *operands): "Reuse Expr if possible, otherwise reconstruct from given operands." # FIXME: Use hashes of operands instead for a faster probability based version? #if all(a is b for (a, b) in izip(operands, o.operands())): ufl_assert(len(operands) == len(o.operands()), "Expecting number of operands to match") if operands == o.operands(): return o #return o.reconstruct(*operands) # Debugging version: try: r = o.reconstruct(*operands) except: print print "FAILURE in reuse_if_possible:" print "type(o) =", type(o) print "operands =" print print "\n\n".join(map(str,operands)) print print "stack =" self.print_visit_stack() print raise return r def always_reconstruct(self, o, *operands): "Always reconstruct expr." return o.reconstruct(*operands) # Set default behaviour for any Expr expr = undefined # Set default behaviour for any Terminal terminal = reuse def reuse_variable(self, o): # Check variable cache to reuse previously transformed variable if possible e, l = o.operands() v = self._variable_cache.get(l) if v is not None: return v # Visit the expression our variable represents e2 = self.visit(e) # If the expression is the same, reuse Variable object if e == e2: v = o else: # Recreate Variable (with same label) v = Variable(e2, l) # Cache variable self._variable_cache[l] = v return v def reconstruct_variable(self, o): # Check variable cache to reuse previously transformed variable if possible e, l = o.operands() v = self._variable_cache.get(l) if v is not None: return v # Visit the expression our variable represents e2 = self.visit(e) # Always reconstruct Variable (with same label) v = Variable(e2, l) self._variable_cache[l] = v return v class ReuseTransformer(Transformer): def __init__(self, variable_cache=None): Transformer.__init__(self, variable_cache) # Set default behaviour for any Expr expr = Transformer.reuse_if_possible # Set default behaviour for any Terminal terminal = Transformer.reuse # Set default behaviour for Variable variable = Transformer.reuse_variable class CopyTransformer(Transformer): def __init__(self, variable_cache=None): Transformer.__init__(self, variable_cache) # Set default behaviour for any Expr expr = Transformer.always_reconstruct # Set default behaviour for any Terminal terminal = Transformer.reuse # Set default behaviour for Variable variable = Transformer.reconstruct_variable class VariableStripper(ReuseTransformer): def __init__(self): ReuseTransformer.__init__(self) def variable(self, o): return self.visit(o._expression) def transform(expression, handlers): """Convert a UFLExpression according to rules defined by the mapping handlers = dict: class -> conversion function.""" if isinstance(expression, Terminal): ops = () else: ops = [transform(o, handlers) for o in expression.operands()] c = expression._uflclass h = handlers.get(c, None) if c is None: error("Didn't find class %s among handlers." % c) return h(expression, *ops) def transform_integrands(form, transform, domain_type=None): """Apply transform(expression) to each integrand expression in form, or to form if it is an Expr.""" if isinstance(form, Form): newintegrals = [] for itg in form.integrals(): integrand = itg.integrand() if domain_type is None or domain_type == itg.domain_type(): integrand = transform(integrand) if not isinstance(integrand, Zero): newitg = itg.reconstruct(integrand) newintegrals.append(newitg) if not newintegrals: debug("No integrals left after transformation, returning empty form.") return Form(newintegrals) elif isinstance(form, Integral): integral = form integrand = transform(integral.integrand()) new_integral = integral.reconstruct(integrand) return new_integral elif isinstance(form, Expr): expr = form return transform(expr) else: error("Expecting Form or Expr.") def apply_transformer(e, transformer, domain_type=None): """Apply transformer.visit(expression) to each integrand expression in form, or to form if it is an Expr.""" def _transform(expr): return transformer.visit(expr) return transform_integrands(e, _transform, domain_type) def ufl2ufl(e): """Convert an UFL expression to a new UFL expression, with no changes. This is used for testing that objects in the expression behave as expected.""" return apply_transformer(e, ReuseTransformer()) def ufl2uflcopy(e): """Convert an UFL expression to a new UFL expression. All nonterminal object instances are replaced with identical copies, while terminal objects are kept. This is used for testing that objects in the expression behave as expected.""" return apply_transformer(e, CopyTransformer()) def strip_variables(e): "Replace all Variable instances with the expression they represent." return apply_transformer(e, VariableStripper()) ufl-1.3.0/ufl/algorithms/traversal.py000066400000000000000000000120211226300046600176100ustar00rootroot00000000000000"""This module contains algorithms for traversing expression trees in different ways.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-03-14 # Last changed: 2011-06-02 from ufl.log import error from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.terminal import Terminal from ufl.integral import Integral from ufl.form import Form #--- Traversal utilities --- def iter_expressions(a): """Utility function to handle Form, Integral and any Expr the same way when inspecting expressions. Returns an iterable over Expr instances: - a is an Expr: (a,) - a is an Integral: the integrand expression of a - a is a Form: all integrand expressions of all integrals """ if isinstance(a, Form): return (itg._integrand for itg in a.integrals()) elif isinstance(a, Integral): return (a._integrand,) elif isinstance(a, Expr): return (a,) error("Not an UFL type: %s" % str(type(a))) # Slow recursive version of traverse_terminals, kept here for illustration: def __old_traverse_terminals(expr): if isinstance(expr, Terminal): yield expr else: for o in expr.operands(): for t in traverse_terminals(o): yield t # Faster (factor 10 or so) non-recursive version using a table instead of recursion (dynamic programming) def traverse_terminals(expr): input = [expr] while input: e = input.pop() if isinstance(e, Terminal): yield e else: input.extend(e.operands()) def traverse_terminals2(expr, visited=None): input = [expr] visited = visited or set() while input: e = input.pop() if e not in visited: visited.add(e) if isinstance(e, Terminal): yield e else: input.extend(e.operands()) def traverse_operands(expr): input = [expr] while input: e = input.pop() if not isinstance(e, Terminal): yield e input.extend(e.operands()) # Moved to common because it is without dependencies and this avoids circular deps from ufl.common import fast_pre_traversal, fast_post_traversal def pre_traversal(expr, stack=None): """Yields o for each tree node o in expr, parent before child. If a list is provided, the stack is updated while iterating.""" ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # yield parent yield expr # yield children if not isinstance(expr, Terminal): if stack is not None: stack.append(expr) for o in expr.operands(): for i in pre_traversal(o, stack): yield i if stack is not None: stack.pop() def post_traversal(expr, stack=None): """Yields o for each tree node o in expr, parent after child. If a list is provided, the stack is updated while iterating.""" ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # yield children if stack is not None: stack.append(expr) for o in expr.operands(): for i in post_traversal(o, stack): yield i if stack is not None: stack.pop() # yield parent yield expr def pre_walk(a, func): """Call func on each expression tree node in a, parent before child. The argument a can be a Form, Integral or Expr.""" for e in iter_expressions(a): for o in pre_traversal(e): func(o) def post_walk(a, func): """Call func on each expression tree node in a, parent after child. The argument a can be a Form, Integral or Expr.""" for e in iter_expressions(a): for o in post_traversal(e): func(o) def _walk(expr, pre_func, post_func, stack): # visit parent on the way in pre_func(expr, stack) # visit children stack.append(expr) for o in expr.operands(): _walk(o, pre_func, post_func, stack) stack.pop() # visit parent on the way out post_func(expr, stack) def walk(a, pre_func, post_func, stack=None): """Call pre_func and post_func on each expression tree node in a. The functions are called on a node before and after its children are visited respectively. The argument a can be a Form, Integral or Expr.""" if stack is None: stack = [] for e in iter_expressions(a): _walk(e, pre_func, post_func, stack) ufl-1.3.0/ufl/algorithms/tuplenotation.py000066400000000000000000000021511226300046600205150ustar00rootroot00000000000000"Deprecated file." # Copyright (C) 2008-2013 Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-04-05 # Last changed: 2011-06-02 from ufl.log import error from ufl.form import Form # TODO: Move this to form.py or some other file, or just get rid of calls to it def as_form(form): "Convert to form if not a form, otherwise return form." # Check form Form if isinstance(form, Form): return form error("Unable to convert object to a UFL form: %s" % repr(form)) ufl-1.3.0/ufl/algorithms/ufl2dot.py000066400000000000000000000210741226300046600171740ustar00rootroot00000000000000"""A collection of utility algorithms for printing of UFL objects in the DOT graph visualization language, mostly intended for debugging purposers.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-11-17 # Last changed: 2011-10-14 from itertools import chain from ufl.log import error from ufl.expr import Expr from ufl.terminal import Terminal from ufl.form import Form from ufl.variable import Variable from ufl.constantvalue import ScalarValue from ufl.geometry import FacetNormal from ufl.classes import FormArgument, MultiIndex, NegativeRestricted, PositiveRestricted from ufl.algorithms.multifunction import MultiFunction class ReprLabeller(MultiFunction): def __init__(self): MultiFunction.__init__(self) def terminal(self, e): return repr(e) def operator(self, e): return e._uflclass.__name__.split(".")[-1] class CompactLabeller(ReprLabeller): def __init__(self, function_mapping=None): ReprLabeller.__init__(self) self._function_mapping = function_mapping # Terminals: def scalar_value(self, e): return repr(e._value) def zero(self, e): return "0" def identity(self, e): return "Id" def multi_index(self, e): return str(e) def form_argument(self, e): return self._function_mapping.get(id(e)) or str(e) def spatial_coordinate(self, e): return "x" def facet_normal(self, e): return "n" def circumradius(self, e): return "circumradius" def cell_volume(self, e): return "cell volume" def cell_surface_area(self, e): return "surface area" def max_facet_edge_length(self, e): return "max facet edge length" def facet_area(self, e): return "facet area" def facet_diameter(self, e): return "facet diameter" # Operators: def sum(self, e): return "+" def product(self, e): return "*" def division(self, e): return "/" def power(self, e): return "**" def math_function(self, e): return e._name def index_sum(self, e): return "∑" def indexed(self, e): return "[]" def component_tensor(self, e): # TODO: Understandable short notation for this? return "][" def negative_restricted(self, e): return "[-]" def positive_restricted(self, e): return "[+]" def cell_avg(self, e): # TODO: Understandable short notation for this? return "_K_" def facet_avg(self, e): # TODO: Understandable short notation for this? return "_F_" def inner(self, e): return "inner" def dot(self, e): return "dot" def outer(self, e): return "outer" def transposed(self, e): return "transp." def determinant(self, e): return "det" def trace(self, e): return "tr" def dev(self, e): return "dev" def skew(self, e): return "skew" def grad(self, e): return "grad" def div(self, e): return "div" def curl(self, e): return "curl" def nabla_grad(self, e): return "nabla_grad" def nabla_div(self, e): return "nabla_div" def diff(self, e): return "diff" # Make this class like the ones above to use fancy math symbol labels class2label = { \ "IndexSum": "∑", "Sum": "∑", "Product": "∏", "Division": "/", "Inner": ":", "Dot": "⋅", "Outer": "⊗", "Grad": "grad", "Div": "div", "NablaGrad": "∇⊗", "NablaDiv": "∇⋅", "Curl": "∇×", } class FancyLabeller(CompactLabeller): pass def build_entities(e, nodes, edges, nodeoffset, prefix="", labeller=None): # TODO: Maybe this can be cleaner written using the graph utilities. # TODO: To collapse equal nodes with different objects, do not use id as key. Make this an option? # Cutoff if we have handled e before if id(e) in nodes: return if labeller is None: labeller = ReprLabeller() # Special-case Variable instances if isinstance(e, Variable): # FIXME: Is this really necessary? ops = (e._expression,) label = "variable %d" % e._label._count else: ops = e.operands() label = labeller(e) # Create node for parent e nodename = "%sn%04d" % (prefix, len(nodes) + nodeoffset) nodes[id(e)] = (nodename, label) # Handle all children recursively n = len(ops) if n == 2: #oplabels = ["left", "right"] oplabels = ["L", "R"] elif n > 2: oplabels = ["op%d" % i for i in range(n)] else: oplabels = [None]*n for i, o in enumerate(ops): # Handle entire subtree for expression o build_entities(o, nodes, edges, nodeoffset, prefix, labeller) # Add edge between e and child node o edges.append((id(e), id(o), oplabels[i])) def format_entities(nodes, edges): entities = [] for (nodename, label) in nodes.itervalues(): node = ' %s [label="%s"];' % (nodename, label) entities.append(node) for (aid, bid, label) in edges: anodename = nodes[aid][0] bnodename = nodes[bid][0] if label is None: edge = ' %s -> %s ;' % (anodename, bnodename) else: edge = ' %s -> %s [label="%s"] ;' % (anodename, bnodename, label) entities.append(edge) return "\n".join(entities) integralgraphformat = """ %(node)s [label="%(label)s"] form_%(formname)s -> %(node)s ; %(node)s -> %(root)s ; %(entities)s""" exprgraphformat = """ digraph ufl_expression { %s }""" def ufl2dot(expression, formname="a", nodeoffset=0, begin=True, end=True, labeling="repr", object_names=None): if labeling == "repr": labeller = ReprLabeller() elif labeling == "compact": labeller = CompactLabeller(object_names or {}) print object_names if isinstance(expression, Form): form = expression ci = form.integrals(Measure.CELL) ei = form.integrals(Measure.EXTERIOR_FACET) ii = form.integrals(Measure.INTERIOR_FACET) pi = form.integrals(Measure.POINT) mi = form.integrals(Measure.MACRO_CELL) subgraphs = [] k = 0 for itgs in (ci, ei, ii, pi, mi): for itg in itgs: prefix = "itg%d_" % k integralkey = "%s%s" % (itg.measure().domain_type(), itg.measure().domain_id()) integrallabel = "%s %s" % (itg.measure().domain_type().capitalize().replace("_", " "), "integral") if len(itgs) > 1: integrallabel += " %s" % (itg.measure().domain_id(),) integrand = itg.integrand() nodes = {} edges = [] build_entities(integrand, nodes, edges, nodeoffset, prefix, labeller) rootnode = nodes[id(integrand)][0] entitylist = format_entities(nodes, edges) integralnode = "%s_%s" % (formname, integralkey) subgraphs.append(integralgraphformat % { 'node': integralnode, 'label': integrallabel, 'formname': formname, 'root': rootnode, 'entities': entitylist, }) nodeoffset += len(nodes) s = "" if begin: s += 'digraph ufl_form\n{\n node [shape="box"] ;\n' s += ' form_%s [label="Form %s"] ;' % (formname, formname) s += "\n".join(subgraphs) if end: s += "\n}" elif isinstance(expression, Expr): nodes = {} edges = [] build_entities(integrand, nodes, edges, nodeoffset, labeller) entitylist = format_entities(nodes, edges) s = exprgraphformat % entitylist nodeoffset += len(nodes) else: error("Invalid object type %s" % type(expression)) return s, nodeoffset ufl-1.3.0/ufl/algorithms/ufl2latex.py000066400000000000000000000541041226300046600175230ustar00rootroot00000000000000"""This module defines expression transformation utilities, either converting UFL expressions to new UFL expressions or converting UFL expressions to other representations.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # Modified by Kristian B. Oelgaard, 2011 # # First added: 2008-05-07 # Last changed: 2011-10-21 from itertools import chain from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.common import write_file, pdflatex, openpdf from ufl.permutation import compute_indices # All classes: from ufl.variable import Variable from ufl.indexing import Index, FixedIndex from ufl.indexed import Indexed from ufl.tensors import ListTensor, ComponentTensor from ufl.algebra import Sum, Product, Division, Power, Abs from ufl.indexsum import IndexSum from ufl.tensoralgebra import Transposed, Outer, Inner, Dot, Cross, Trace, Determinant, Inverse, Deviatoric, Cofactor from ufl.mathfunctions import Sqrt, Exp, Ln, Cos, Sin, Tan, Cosh, Sinh, Tanh, Acos, Asin, Atan, Atan2, Erf, BesselJ, BesselY, BesselI, BesselK from ufl.restriction import PositiveRestricted, NegativeRestricted, CellAvg, FacetAvg from ufl.differentiation import VariableDerivative, Grad, Div, Curl, NablaGrad, NablaDiv from ufl.conditional import EQ, NE, LE, GE, LT, GT, Conditional from ufl.form import Form from ufl.integral import Measure from ufl.classes import terminal_classes from ufl.domains import Domain, Region # Other algorithms: from ufl.algorithms.preprocess import preprocess from ufl.algorithms.analysis import extract_variables from ufl.algorithms.formfiles import load_forms from ufl.algorithms.latextools import align, document, verbatim from ufl.algorithms.transformer import Transformer from ufl.algorithms.graph import build_graph, partition, extract_outgoing_vertex_connections # TODO: Maybe this can be cleaner written using the graph utilities # --- Tools for LaTeX rendering of UFL expressions --- # TODO: Finish precedence mapping def build_precedence_map(): precedence_list = [] # TODO: Review this list very carefully! precedence_list.append((Sum,)) precedence_list.append((IndexSum,)) # TODO: What to do with these? precedence_list.append((ListTensor, ComponentTensor)) precedence_list.append((CellAvg, )) precedence_list.append((NegativeRestricted, PositiveRestricted)) precedence_list.append((Conditional,)) precedence_list.append((LE, GT, GE, NE, EQ, LT)) precedence_list.append((Div, Grad, NablaGrad, NablaDiv, Curl, VariableDerivative, Determinant, Trace, Cofactor, Inverse, Deviatoric)) precedence_list.append((Product, Division, Cross, Dot, Outer, Inner)) precedence_list.append((Indexed, Transposed, Power)) precedence_list.append((Abs, Cos, Cosh, Exp, Ln, Sin, Sinh, Sqrt, Tan, Tanh, Acos, Asin, Atan, Atan2, Erf, BesselJ, BesselY, BesselI, BesselK)) precedence_list.append((Variable,)) precedence_list.append(terminal_classes) precedence_map = {} k = 0 for p in precedence_list: for c in p: precedence_map[c] = k k += 1 return precedence_map # Utility for parentesizing string def par(s, condition=True): # TODO: Finish precedence handling by adding condition argument to calls to this function! if condition: return r"\left(%s\right)" % s return str(s) def format_index(ii): if isinstance(ii, FixedIndex): s = "%d" % ii._value elif isinstance(ii, Index): s = "i_{%d}" % ii._count else: error("Invalid index type %s." % type(ii)) return s def format_multi_index(ii, formatstring="%s"): return "".join(formatstring % format_index(i) for i in ii) def bfname(i): return "{v_h^%d}" % i def cfname(i): return "{w_h^%d}" % i # TODO: Handle line wrapping # TODO: Handle ListTensors of rank > 1 correctly class Expression2LatexHandler(Transformer): def __init__(self, argument_names=None, coefficient_names=None): Transformer.__init__(self) self.argument_names = argument_names self.coefficient_names = coefficient_names # --- Terminal objects --- def scalar_value(self, o): if o.shape(): return r"{\mathbf %s}" % o._value return "{%s}" % o._value def zero(self, o): return "0" if not o.shape() else r"{\mathbf 0}" def identity(self, o): return r"{\mathbf I}" def permutation_symbol(self, o): return r"{\mathbf \varepsilon}" def facet_normal(self, o): return r"{\mathbf n}" def argument(self, o): # Using ^ for argument numbering and _ for indexing since indexing is more common than exponentiation if self.argument_names is None: return bfname(o.count()) return self.argument_names[o.count()] def coefficient(self, o): # Using ^ for coefficient numbering and _ for indexing since indexing is more common than exponentiation if self.coefficient_names is None: return cfname(o.count()) return self.coefficient_names[o.count()] constant = coefficient def multi_index(self, o): return format_multi_index(o, formatstring="{%s}") def variable(self, o): # TODO: Ensure variable has been handled e, l = o.operands() return "s_{%d}" % l._count # --- Non-terminal objects --- def index_sum(self, o, f, i): return r"\sum_{%s}%s" % (i, par(f)) def sum(self, o, *ops): return " + ".join(par(op) for op in ops) def product(self, o, *ops): return " ".join(par(op) for op in ops) def division(self, o, a, b): return r"\frac{%s}{%s}" % (a, b) def abs(self, o, a): return r"\|%s\|" % a def transposed(self, o, a): return "{%s}^T" % par(a) def indexed(self, o, a, b): return "{%s}_{%s}" % (par(a), b) def variable_derivative(self, o, f, v): nom = r"\partial%s" % par(f) denom = r"\partial%s" % par(v) return r"\frac{%s}{%s}" % (nom, denom) def coefficient_derivative(self, o, f, w, v): nom = r"\partial%s" % par(f) denom = r"\partial%s" % par(w) return r"\frac{%s}{%s}[%s]" % (nom, denom, v) # TODO: Fix this syntax... def grad(self, o, f): return r"\mathbf{grad}{%s}" % par(f) def div(self, o, f): return r"\mathbf{grad}{%s}" % par(f) def nabla_grad(self, o, f): return r"\nabla{\otimes %s}" % par(f) def nabla_div(self, o, f): return r"\nabla{\cdot %s}" % par(f) def curl(self, o, f): return r"\nabla{\times %s}" % par(f) def sqrt(self, o, f): return r"%s^{\frac 1 2}" % par(f) def exp(self, o, f): return "e^{%s}" % f def ln(self, o, f): return r"\ln{%s}" % par(f) def cos(self, o, f): return r"\cos{%s}" % par(f) def sin(self, o, f): return r"\sin{%s}" % par(f) def tan(self, o, f): return r"\tan{%s}" % par(f) def cosh(self, o, f): return r"\cosh{%s}" % par(f) def sinh(self, o, f): return r"\sinh{%s}" % par(f) def tanh(self, o, f): return r"\tanh{%s}" % par(f) def acos(self, o, f): return r"\arccos{%s}" % par(f) def asin(self, o, f): return r"\arcsin{%s}" % par(f) def atan(self, o, f): return r"\arctan{%s}" % par(f) def atan2(self, o, f1, f2): return r"\arctan_2{%s,%s}" % (par(f1), par(f2)) def erf(self, o, f): return r"\erf{%s}" % par(f) def bessel_j(self, o, nu, f): return r"J_{%s}{%s}" % (nu, par(f)) def bessel_y(self, o, nu, f): return r"Y_{%s}{%s}" % (nu, par(f)) def bessel_i(self, o, nu, f): return r"I_{%s}{%s}" % (nu, par(f)) def bessel_K(self, o, nu, f): return r"K_{%s}{%s}" % (nu, par(f)) def power(self, o, a, b): return "{%s}^{%s}" % (par(a), par(b)) def outer(self, o, a, b): return r"{%s}\otimes{%s}" % (par(a), par(b)) def inner(self, o, a, b): return "{%s}:{%s}" % (par(a), par(b)) def dot(self, o, a, b): return r"{%s}\cdot{%s}" % (par(a), par(b)) def cross(self, o, a, b): return r"{%s}\times{%s}" % (par(a), par(b)) def trace(self, o, A): return "tr{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def determinant(self, o, A): return "det{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def inverse(self, o, A): return "{%s}^{-1}" % par(A) def deviatoric(self, o, A): return "dev{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def cofactor(self, o, A): return "cofac{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def skew(self, o, A): return "skew{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def sym(self, o, A): return "sym{%s}" % par(A) # TODO: Get built-in function syntax like \sin for this def list_tensor(self, o): shape = o.shape() if len(shape) == 1: ops = [self.visit(op) for op in o.operands()] l = " \\\\ \n ".join(ops) elif len(shape) == 2: rows = [] for row in o.operands(): cols = (self.visit(op) for op in row.operands()) rows.append( " & \n ".join(cols) ) l = " \\\\ \n ".join(rows) else: error("TODO: LaTeX handler for list tensor of rank 3 or higher not implemented!") return "\\left[\\begin{matrix}{%s}\\end{matrix}\\right]^T" % l def component_tensor(self, o, *ops): A, ii = ops return "\\left[A \\quad | \\quad A_{%s} = {%s} \\quad \\forall {%s} \\right]" % (ii, A, ii) def positive_restricted(self, o, f): return "{%s}^+" % par(f) def negative_restricted(self, o, f): return "{%s}^-" % par(f) def cell_avg(self, o, f): return "{%s}_K" % par(f) def eq(self, o, a, b): return "(%s = %s)" % (a, b) def ne(self, o, a, b): return r"(%s \ne %s)" % (a, b) def le(self, o, a, b): return r"(%s \le %s)" % (a, b) def ge(self, o, a, b): return r"(%s \ge %s)" % (a, b) def lt(self, o, a, b): return "(%s < %s)" % (a, b) def gt(self, o, a, b): return "(%s > %s)" % (a, b) def and_condition(self, o, a, b): return "(%s && %s)" % (a, b) def or_condition(self, o, a, b): return "(%s || %s)" % (a, b) def not_condition(self, o, a): return "!(%s)" % (a,) def conditional(self, o, c, t, f): l = "\\begin{cases}\n" l += "%s, &\text{if }\quad %s, \\\\\n" % (t, c) l += "%s, &\text{otherwise.}\n" % f l += "\\end{cases}" return l def expr(self, o): error("Missing handler for type %s" % str(type(o))) def expression2latex(expression, argument_names=None, coefficient_names=None): visitor = Expression2LatexHandler(argument_names, coefficient_names) return visitor.visit(expression) def element2latex(element): e = str(element) e = e.replace("<", "") e = e.replace(">", "") return r"{\mbox{%s}}" % e domain_strings = { Measure.CELL: r"\Omega", Measure.EXTERIOR_FACET: r"\Gamma^{ext}", Measure.INTERIOR_FACET: r"\Gamma^{int}", Measure.POINT: r"\Gamma^{point}", Measure.MACRO_CELL: r"\Omega^{macro}", Measure.SURFACE: r"\Gamma^{surface}", } default_domain_string = "d(?)" dx_strings = { Measure.CELL: "dx", Measure.EXTERIOR_FACET: "ds", Measure.INTERIOR_FACET: "dS", Measure.POINT: "dP", Measure.MACRO_CELL: "dE", Measure.SURFACE: "dc", } def form2latex(form, formdata): formname = formdata.name argument_names = formdata.argument_names coefficient_names = formdata.coefficient_names # List of sections to make latex document from sections = [] # Define elements lines = [] for i, f in enumerate(formdata.original_arguments): lines.append(r"\mathcal{P}_{%d} = \{%s\} " % (i, element2latex(f.element()))) for i, f in enumerate(formdata.original_coefficients): lines.append(r"\mathcal{Q}_{%d} = \{%s\} " % (i, element2latex(f.element()))) if lines: sections.append(("Finite elements", align(lines))) # Define function spaces lines = [] for i, f in enumerate(formdata.original_arguments): lines.append("V_h^{%d} = \\{v : v \\vert_K \\in \\mathcal{P}_{%d}(K) \\quad \\forall K \\in \\mathcal{T}\\} " % (i, i)) for i, f in enumerate(formdata.original_coefficients): lines.append("W_h^{%d} = \\{v : v \\vert_K \\in \\mathcal{Q}_{%d}(K) \\quad \\forall K \\in \\mathcal{T}\\} " % (i, i)) if lines: sections.append(("Function spaces", align(lines))) # Define arguments and coefficients lines = [] for i, f in enumerate(formdata.original_arguments): lines.append("%s = %s \\in V_h^{%d} " % (argument_names[i], bfname(i), i)) for i, f in enumerate(formdata.original_coefficients): lines.append("%s = %s \\in W_h^{%d} " % (coefficient_names[i], cfname(i), i)) if lines: sections.append(("Form arguments", align(lines))) # TODO: Wrap ListTensors, ComponentTensor and Conditionals in expression as variables before transformation # Define variables handled_variables = set() integrals = list(chain(form.integrals(Measure.CELL), form.integrals(Measure.EXTERIOR_FACET), form.integrals(Measure.INTERIOR_FACET), form.integrals(Measure.POINT), form.integrals(Measure.MACRO_CELL), form.integrals(Measure.SURFACE))) ufl_assert(len(integrals) == len(form.integrals()), "Not handling non-standard integral types!") lines = [] for itg in integrals: variables = extract_variables(itg.integrand()) for v in variables: l = v._label if not l in handled_variables: handled_variables.add(l) exprlatex = expression2latex(v._expression, formdata.argument_names, formdata.coefficient_names) lines.append(("s_{%d}" % l._count, "= %s" % exprlatex)) if lines: sections.append(("Variables", align(lines))) # Join form arguments for signature "a(...) =" b = ", ".join(formdata.argument_names) c = ", ".join(formdata.coefficient_names) arguments = "; ".join((b, c)) signature = "%s(%s) = " % (formname, arguments, ) # Define form as sum of integrals lines = [] a = signature p = "" for itg in integrals: # TODO: Get list of expression strings instead of single expression! integrand_string = expression2latex(itg.integrand(), formdata.argument_names, formdata.coefficient_names) domain_type = itg.domain_type() dstr = domain_strings[domain_type] domain_id = itg.domain_id() if isinstance(domain_id, int): dstr += "_{%d}" % domain_id elif isinstance(domain_id, Domain) or domain_id == Measure.DOMAIN_ID_EVERYWHERE: pass elif domain_id == Measure.DOMAIN_ID_OTHERWISE: dstr += "_{\text{oth}}" elif isinstance(domain_id, tuple): dstr += "_{%s}" % domain_id b = p + "\\int_{%s}" % (dstr,) dxstr = Measure._domain_types[domain_type] dxstr = dx_strings[domain_type] c = "{ %s } \\,%s" % (integrand_string, dxstr) lines.append((a, b, c)) a = "{}" p = "{}+ " sections.append(("Form", align(lines))) return sections def ufl2latex(expression): "Generate LaTeX code for a UFL expression or form (wrapper for form2latex and expression2latex)." if isinstance(expression, Form): form_data = expression.compute_form_data() preprocessed_form = form_data.preprocessed_form return form2latex(preprocessed_form, form_data) else: return expression2latex(expression) # --- LaTeX rendering of composite UFL objects --- def deps2latex(deps): return "Dependencies: ${ %s }$." % ", ".join(sorted(deps)) def dependency_sorting(deplist, rank): #print "deplist = ", deplist def split(deps, state): left = [] todo = [] for dep in deps: if dep - state: left.append(dep) else: todo.append(dep) return todo, left deplistlist = [] state = set() left = deplist # --- Initialization time #state.remove("x") precompute, left = split(left, state) deplistlist.append(precompute) state.add("x") precompute_quad, left = split(left, state) deplistlist.append(precompute_quad) # Permutations of 0/1 dependence of arguments indices = compute_indices((2,)*rank) for bfs in indices[1:]: # skip (0,...,0), already handled that for i, bf in reversed(list(enumerate(bfs))): n = "v%d" % i if bf: if n in state: state.remove(n) else: state.add(n) next, left = split(left, state) deplistlist.append(next) # --- Runtime state.add("c") state.add("w") state.remove("x") runtime, left = split(left, state) deplistlist.append(runtime) state.add("x") runtime_quad, left = split(left, state) deplistlist.append(runtime_quad) indices = compute_indices((2,)*rank) for bfs in indices[1:]: # skip (0,...,0), already handled that for i, bf in reversed(list(enumerate(bfs))): n = "v%d" % i if bf: state.add(n) else: if n in state: state.remove(n) next, left = split(left, state) deplistlist.append(next) ufl_assert(not left, "Shouldn't have anything left!") #print #print "Created deplistlist:" #for deps in deplistlist: # print # print "--- New stage:" # print "\n".join(map(str, deps)) #print return deplistlist def code2latex(G, partitions, formdata): "TODO: Document me" bfn = formdata.argument_names cfn = formdata.coefficient_names V, E = G Vout = extract_outgoing_vertex_connections(G) # Sort dependency sets in a sensible way (preclude to a good quadrature code generator) deplistlist = dependency_sorting(partitions.keys(), len(bfn)) def format_v(i): return "s_{%d}" % i pieces = [] for deplist in deplistlist: #pieces.append("\n\n(Debugging: getting next list of dependencies)") for dep in deplist: lines = [] for iv in partitions[dep]: v = V[iv] vout = Vout[iv] vl = format_v(iv) args = ", ".join(format_v(i) for i in vout) if args: el = r"{\mbox{%s}}(%s)" % (v._uflclass.__name__, args) else: # terminal el = r"{\mbox{%s}}" % (repr(v),) lines.append((vl, "= " + el)) pieces.extend(("\n", deps2latex(dep), align(lines))) # Add final variable representing integrand vl = format_v(len(V)-1) pieces.append("\n") pieces.append("Variable representing integrand: %s" % vl) # Could also return list of (title, body) parts for subsections if wanted body = "\n".join(pieces) return body def integrand2code(integrand, formdata): G = build_graph(integrand) partitions, keys = partition(G) return G, partitions def formdata2latex(formdata): # TODO: Format better return verbatim(str(formdata)) def form2code2latex(formdata): # Render introductory sections title = "Form data" body = formdata2latex(formdata) sections = [(title, body)] # Render each integral as a separate section for itg in formdata.form.integrals(Measure.CELL): title = "%s integral over domain %d" % (itg.domain_type(), itg.domain_id()) G, partitions = integrand2code(itg.integrand(), formdata) body = code2latex(G, partitions, formdata) sections.append((title, body)) return sections # --- Creating complete documents --- def forms2latexdocument(forms, uflfilename, compile=False): "Render forms from a .ufl file as a LaTeX document." # Render one section for each form sections = [] for form in forms: # Compute form data form = preprocess(form) form_data = form.form_data() # Generate LaTex code title = "Form %s" % form_data.name if compile: body = form2code2latex(form, form_data) else: body = form2latex(form, form_data) sections.append((title, body)) # Render title suffix = "from UFL file %s" % uflfilename.replace("_", "\\_") if compile: title = "Compiled forms " + suffix else: title = "Forms " + suffix return document(title, sections) # --- File operations --- def ufl2tex(uflfilename, latexfilename, compile=False): "Compile a .tex file from a .ufl file." forms = load_forms(uflfilename) latex = forms2latexdocument(forms, uflfilename, compile) write_file(latexfilename, latex) def tex2pdf(latexfilename, pdffilename): "Compile a .pdf file from a .tex file." pdflatex(latexfilename, pdffilename) openpdf(pdffilename) def ufl2pdf(uflfilename, latexfilename, pdffilename, compile=False): "Compile a .pdf file from a .ufl file." ufl2tex(uflfilename, latexfilename, compile) tex2pdf(latexfilename, pdffilename) ufl-1.3.0/ufl/argument.py000066400000000000000000000115261226300046600152670ustar00rootroot00000000000000"""This module defines the class Argument and a number of related classes (functions), including TestFunction and TrialFunction.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # # First added: 2008-03-14 # Last changed: 2011-10-20 from ufl.assertions import ufl_assert from ufl.terminal import FormArgument from ufl.split_functions import split from ufl.finiteelement import FiniteElementBase # --- Class representing an argument (basis function) in a form --- class Argument(FormArgument): """UFL value: Representation of an argument to a form.""" __slots__ = ("_count", "_repr", "_element",) _globalcount = 0 def __init__(self, element, count=None): FormArgument.__init__(self, count, Argument) ufl_assert(isinstance(element, FiniteElementBase), "Expecting a FiniteElementBase instance.") self._element = element self._repr = "Argument(%r, %r)" % (self._element, self._count) def reconstruct(self, element=None, count=None): if element is None or element == self._element: element = self._element if count is None or count == self._count: count = self._count if count is self._count and element is self._element: return self ufl_assert(isinstance(element, FiniteElementBase), "Expecting an element, not %s" % element) ufl_assert(isinstance(count, int), "Expecting an int, not %s" % count) ufl_assert(element.value_shape() == self._element.value_shape(), "Cannot reconstruct an Argument with a different value shape.") return Argument(element, count) def element(self): return self._element def shape(self): return self._element.value_shape() def cell(self): return self._element.cell() def domain(self): return self._element.domain() def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." # TODO: Should in principle do like with Coefficient, # but that may currently simplify away some arguments # we want to keep, or? return False def __str__(self): count = str(self._count) if len(count) == 1: return "v_%s" % count else: return "v_{%s}" % count def __repr__(self): return self._repr def __eq__(self, other): """Deliberately comparing exact type and not using isinstance here, meaning eventual subclasses must reimplement this function to work correctly, and instances of this class will compare not equal to instances of eventual subclasses. The overloading allows subclasses to distinguish between test and trial functions with a different non-ufl payload, such as dolfin FunctionSpace with different mesh. This is necessary because of the test/trial function hack with -2/-1 counts which always gives TestFunction(V) == TestFunction(V) from a pure ufl point of view. """ return (type(self) == type(other) and self._count == other._count and self._element == other._element) # --- Helper functions for pretty syntax --- def TestFunction(element): """UFL value: Create a test function argument to a form.""" return Argument(element, -2) def TrialFunction(element): """UFL value: Create a trial function argument to a form.""" return Argument(element, -1) # --- Helper functions for creating subfunctions on mixed elements --- def Arguments(element): """UFL value: Create an Argument in a mixed space, and return a tuple with the function components corresponding to the subelements.""" return split(Argument(element)) def TestFunctions(element): """UFL value: Create a TestFunction in a mixed space, and return a tuple with the function components corresponding to the subelements.""" return split(TestFunction(element)) def TrialFunctions(element): """UFL value: Create a TrialFunction in a mixed space, and return a tuple with the function components corresponding to the subelements.""" return split(TrialFunction(element)) ufl-1.3.0/ufl/assertions.py000066400000000000000000000031141226300046600156310ustar00rootroot00000000000000"""This module provides assertion functions used by the UFL implementation.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-01-28 # Last changed: 2011-06-02 from ufl.log import error #--- Standardized error messages --- expecting_instance = lambda v, c: error("Expecting %s instance, not %s." % (c.__name__, repr(v))) expecting_python_scalar = lambda v: error("Expecting Python scalar, not %s." % repr(v)) expecting_expr = lambda v: error("Expecting Expr instance, not %s." % repr(v)) expecting_terminal = lambda v: error("Expecting Terminal instance, not %s." % repr(v)) expecting_true_ufl_scalar = lambda v: error("Expecting UFL scalar expression with no free indices, not %s." % repr(v)) #--- Standardized assertions --- def ufl_assert(condition, *message): "Assert that condition is true and otherwise issue an error with given message." if not condition: error(*message) ufl-1.3.0/ufl/classes.py000066400000000000000000000105511226300046600150770ustar00rootroot00000000000000"""This file is useful for external code like tests and form compilers, since it enables the syntax "from ufl.classes import FooBar" for getting implementation details not exposed through the default ufl namespace. It also contains functionality used by algorithms for dealing with groups of classes, and for mapping types to different handler functions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # Modified by Kristian B. Oelgaard, 2011 # # First added: 2008-08-15 # Last changed: 2013-03-29 from ufl.assertions import ufl_assert # Elements from ufl.finiteelement import (FiniteElementBase, FiniteElement, MixedElement, VectorElement, TensorElement, EnrichedElement, RestrictedElement, TensorProductElement) # Base class for all expressions from ufl.expr import Expr # Terminal types from ufl.terminal import Terminal, FormArgument, UtilityType, Data from ufl.constantvalue import ConstantValue, IndexAnnotated, Zero, ScalarValue,\ FloatValue, IntValue, Identity, PermutationSymbol from ufl.argument import Argument, TestFunction, TrialFunction from ufl.coefficient import (Coefficient, ConstantBase, VectorConstant, TensorConstant, Constant) from ufl.geometry import (Cell, ProductCell, GeometricQuantity, SpatialCoordinate, FacetNormal, CellVolume, Circumradius, CellSurfaceArea, FacetArea, MinFacetEdgeLength, MaxFacetEdgeLength, FacetDiameter, LocalCoordinate, GeometryJacobi, GeometryJacobiDeterminant, InverseGeometryJacobi) from ufl.indexing import IndexBase, FixedIndex, Index, MultiIndex # Operator types from ufl.operatorbase import Operator, WrapperType, AlgebraOperator, Tuple from ufl.indexed import Indexed from ufl.indexsum import IndexSum from ufl.variable import Variable, Label from ufl.tensors import ListTensor, ComponentTensor from ufl.algebra import Sum, Product, Division, Power, Abs from ufl.tensoralgebra import CompoundTensorOperator, Transposed, Outer,\ Inner, Dot, Cross, Trace, Determinant, Cofactor, Inverse, Deviatoric, Skew, Sym from ufl.mathfunctions import MathFunction, Sqrt, Exp, Ln, Erf,\ Cos, Sin, Tan, Cosh, Sinh, Tanh, Acos, Asin, Atan, Atan2, \ BesselFunction, BesselJ, BesselY, BesselI, BesselK from ufl.differentiation import Derivative, CompoundDerivative, CoefficientDerivative,\ VariableDerivative, Grad, Div, Curl, NablaGrad, NablaDiv from ufl.conditional import Condition, BinaryCondition,\ EQ, NE, LE, GE, LT, GT,\ AndCondition, OrCondition, NotCondition, Conditional from ufl.restriction import Restricted, PositiveRestricted, NegativeRestricted, CellAvg, FacetAvg # Higher level abstractions from ufl.integral import Measure, ProductMeasure, Integral from ufl.form import Form from ufl.equation import Equation # Make sure we import exproperators which attaches special functions to Expr from ufl import exproperators as __exproperators # Collect all classes in sets automatically classified by some properties __all_classes = (c for c in locals().values() if isinstance(c, type)) all_ufl_classes = set(c for c in __all_classes if issubclass(c, Expr)) abstract_classes = set(s for c in all_ufl_classes for s in c.mro()[1:-1]) abstract_classes.remove(Coefficient) ufl_classes = set(c for c in all_ufl_classes if c not in abstract_classes) terminal_classes = set(c for c in all_ufl_classes if issubclass(c, Terminal)) nonterminal_classes = set(c for c in all_ufl_classes if not issubclass(c, Terminal)) # Add _uflclass and _classid to all classes: from ufl.common import camel2underscore as _camel2underscore for _i, _c in enumerate(sorted(all_ufl_classes, key=lambda x:x.__name__)): _c._classid = _i _c._uflclass = _c _c._handlername = _camel2underscore(_c.__name__) #__all__ = all_ufl_classes ufl-1.3.0/ufl/coefficient.py000066400000000000000000000143531226300046600157240ustar00rootroot00000000000000"""This module defines the Coefficient class and a number of related classes, including Constant.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009. # # First added: 2008-03-14 # Last changed: 2011-10-20 from ufl.log import warning from ufl.assertions import ufl_assert from ufl.terminal import FormArgument from ufl.finiteelement import FiniteElementBase, FiniteElement, VectorElement, TensorElement from ufl.split_functions import split # --- The Coefficient class represents a coefficient in a form --- class Coefficient(FormArgument): """UFL form argument type: Representation of a form coefficient.""" # Slots are disabled here because they cause trouble in PyDOLFIN multiple inheritance pattern: #__slots__ = ("_count", "_element", "_repr", "_gradient", "_derivatives") _globalcount = 0 def __init__(self, element, count=None): FormArgument.__init__(self, count, Coefficient) ufl_assert(isinstance(element, FiniteElementBase), "Expecting a FiniteElementBase instance.") self._element = element self._repr = None def reconstruct(self, element=None, count=None): # This code is shared with the FooConstant classes if element is None or element == self._element: element = self._element if count is None or count == self._count: count = self._count if count is self._count and element is self._element: return self ufl_assert(isinstance(element, FiniteElementBase), "Expecting an element, not %s" % element) ufl_assert(isinstance(count, int), "Expecting an int, not %s" % count) ufl_assert(element.value_shape() == self._element.value_shape(), "Cannot reconstruct a Coefficient with a different value shape.") return self._reconstruct(element, count) def _reconstruct(self, element, count): # This code is class specific return Coefficient(element, count) def element(self): return self._element def shape(self): return self._element.value_shape() def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return self._element.is_cellwise_constant() def cell(self): return self._element.cell() def domain(self): return self._element.domain() def __str__(self): count = str(self._count) if len(count) == 1: return "w_%s" % count else: return "w_{%s}" % count def __repr__(self): if self._repr is None: self._repr = "Coefficient(%r, %r)" % (self._element, self._count) return self._repr def __eq__(self, other): if not isinstance(other, Coefficient): return False if self is other: return True return (self._count == other._count and self._element == other._element) # --- Subclasses for defining constant coefficients without specifying element --- class ConstantBase(Coefficient): __slots__ = () def __init__(self, element, count): Coefficient.__init__(self, element, count) class Constant(ConstantBase): """UFL value: Represents a globally constant scalar valued coefficient.""" __slots__ = () def __init__(self, domain, count=None): e = FiniteElement("Real", domain, 0) ConstantBase.__init__(self, e, count) self._repr = "Constant(%r, %r)" % (e.domain(), self._count) def _reconstruct(self, element, count): return Constant(element.domain(), count) def __str__(self): count = str(self._count) if len(count) == 1: return "c_%s" % count else: return "c_{%s}" % count class VectorConstant(ConstantBase): """UFL value: Represents a globally constant vector valued coefficient.""" __slots__ = () def __init__(self, domain, dim=None, count=None): e = VectorElement("Real", domain, 0, dim) ConstantBase.__init__(self, e, count) ufl_assert(self._repr is None, "Repr should not have been set yet!") self._repr = "VectorConstant(%r, %r, %r)" % (e.domain(), e.value_shape()[0], self._count) def _reconstruct(self, element, count): return VectorConstant(element.domain(), element.value_shape()[0], count) def __str__(self): count = str(self._count) if len(count) == 1: return "C_%s" % count else: return "C_{%s}" % count class TensorConstant(ConstantBase): """UFL value: Represents a globally constant tensor valued coefficient.""" __slots__ = () def __init__(self, domain, shape=None, symmetry=None, count=None): e = TensorElement("Real", domain, 0, shape=shape, symmetry=symmetry) ConstantBase.__init__(self, e, count) ufl_assert(self._repr is None, "Repr should not have been set yet!") self._repr = "TensorConstant(%r, %r, %r, %r)" % (e.domain(), e.value_shape(), e._symmetry, self._count) def _reconstruct(self, element, count): e = element return TensorConstant(e.domain(), e.value_shape(), e._symmetry, count) def __str__(self): count = str(self._count) if len(count) == 1: return "C_%s" % count else: return "C_{%s}" % count # --- Helper functions for subfunctions on mixed elements --- def Coefficients(element): """UFL value: Create a Coefficient in a mixed space, and return a tuple with the function components corresponding to the subelements.""" return split(Coefficient(element)) ufl-1.3.0/ufl/common.py000066400000000000000000000270111226300046600147310ustar00rootroot00000000000000"This module contains a collection of common utilities." # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian Oelgaard, 2009 # # First added: 2008-08-05 # Last changed: 2011-06-02 from itertools import izip import operator # Taken from http://ivory.idyll.org/blog/mar-07/replacing-commands-with-subprocess from subprocess import Popen, PIPE, STDOUT def get_status_output(cmd, input=None, cwd=None, env=None): pipe = Popen(cmd, shell=True, cwd=cwd, env=env, stdout=PIPE, stderr=STDOUT) (output, errout) = pipe.communicate(input=input) assert not errout status = pipe.returncode return (status, output) def write_file(filename, text): f = open(filename, "w") f.write(text) f.close() def pdflatex(latexfilename, pdffilename, flags): # TODO: Options for this. "Execute pdflatex to compile a latex file into pdf." flags = "-file-line-error-style -interaction=nonstopmode" latexcmd = "pdflatex" cmd = "%s %s %s %s" % (latexcmd, flags, latexfilename, pdffilename) s, o = get_status_output(cmd) def openpdf(pdffilename): "Open PDF file in external pdf viewer." reader_cmd = "evince %s &" # TODO: Add option for which reader to use. Is there a portable way to do this? Like "get default pdf reader from os"? cmd = reader_cmd % pdffilename s, o = get_status_output(cmd) def product(sequence): "Return the product of all elements in a sequence." return reduce(operator.__mul__, sequence, 1) class EmptyDictType(dict): def __setitem__(self, key, value): from ufl.log import error error("This is a frozen unique empty dictionary object, inserting values is an error.") def update(self, *args, **kwargs): from ufl.log import error error("This is a frozen unique empty dictionary object, inserting values is an error.") EmptyDict = EmptyDictType() def sorted_by_count(seq): return sorted(seq, key=lambda x: x._count) def sorted_items(mapping): return sorted(mapping.iteritems(), key=lambda x: x[0]) def mergedicts(dicts): d = dict(dicts[0]) for d2 in dicts[1:]: d.update(d2) return d def subdict(superdict, keys): return dict((k, superdict[k]) for k in keys) def dict_sum(items): "Construct a dict, in between dict(items) and sum(items), by accumulating items for each key." d = {} for k, v in items: if k not in d: d[k] = v else: d[k] += v return d def unzip(seq): "Inverse operation of zip: unzip(zip(a, b)) == (a, b)" return [s[0] for s in seq], [s[1] for s in seq] def xor(a, b): return bool(a) if b else not a def or_tuples(seqa, seqb): "Return 'or' of all pairs in two sequences of same length." return tuple(a or b for (a,b) in izip(seqa, seqb)) def and_tuples(seqa, seqb): "Return 'and' of all pairs in two sequences of same length." return tuple(a and b for (a,b) in izip(seqa, seqb)) def iter_tree(tree): """Iterate over all nodes in a tree represented by lists of lists of leaves.""" if isinstance(tree, list): for node in tree: for i in iter_tree(node): yield i else: yield tree def fast_pre_traversal(expr): """Yields o for each tree node o in expr, parent before child.""" input = [expr] while input: l = input.pop() yield l input.extend(l.operands()) def unique_pre_traversal(expr, visited=None): """Yields o for each tree node o in expr, parent before child. This version only visits each node once! """ input = [expr] visited = visited or set() while input: l = input.pop() if l not in visited: visited.add(l) yield l input.extend(l.operands()) fast_pre_traversal2 = unique_pre_traversal # TODO: Remove def unique_post_traversal(expr, visited=None): """Yields o for each node o in expr, child before parent. Never visits a node twice.""" stack = [] stack.append((expr, list(expr.operands()))) visited = visited or set() while stack: expr, ops = stack[-1] for i, o in enumerate(ops): if o is not None and o not in visited: stack.append((o, list(o.operands()))) ops[i] = None break else: yield expr visited.add(expr) stack.pop() def fast_post_traversal2(expr, visited=None): """Yields o for each tree node o in expr, child before parent.""" stack = [expr] visited = visited or set() while stack: curr = stack[-1] for o in curr.operands(): if o not in visited: stack.append(o) break else: yield curr visited.add(curr) stack.pop() def fast_post_traversal(expr): # TODO: Would a non-recursive implementation save anything here? """Yields o for each tree node o in expr, child before parent.""" # yield children for o in expr.operands(): for i in fast_post_traversal(o): yield i # yield parent yield expr def split_dict(d, criteria): "Split a dict d into two dicts based on a criteria on the keys." a = {} b = {} for (k,v) in d.iteritems(): if criteria(k): a[k] = v else: b[k] = v return a, b def slice_dict(dictionary, keys, default=None): return tuple(dictionary.get(k, default) for k in keys) def some_key(a_dict): "Return an arbitrary key from a dictionary." return a_dict.iterkeys().next() def camel2underscore(name): "Convert a CamelCaps string to underscore_syntax." letters = [] lastlower = False for l in name: thislower = l.islower() if not thislower: # Don't insert _ between multiple upper case letters if lastlower: letters.append("_") l = l.lower() lastlower = thislower letters.append(l) return "".join(letters) def lstr(l): "Pretty-print list or tuple, invoking str() on items instead of repr() like str() does." if isinstance(l, list): return "[" + ", ".join(lstr(item) for item in l) + "]" elif isinstance(l, tuple): return "(" + ", ".join(lstr(item) for item in l) + ")" return str(l) def dstr(d, colsize=80): "Pretty-print dictionary of key-value pairs." sorted_keys = sorted(d.keys()) return tstr([(key, d[key]) for key in sorted_keys], colsize) def tstr(t, colsize=80): "Pretty-print list of tuples of key-value pairs." if not t: return "" # Compute maximum key length keylen = max([len(str(item[0])) for item in t]) # Key-length cannot be larger than colsize if keylen > colsize: return str(t) # Pretty-print table s = "" for (key, value) in t: key = str(key) if isinstance(value, str): value = "'%s'" % value else: value = str(value) s += key + ":" + " "*(keylen - len(key) + 1) space = "" while len(value) > 0: end = min(len(value), colsize - keylen) s += space + value[:end] + "\n" value = value[end:] space = " "*(keylen + 2) return s def sstr(s): "Pretty-print set." return ", ".join(str(x) for x in s) def istr(o): "Format object as string, inserting ? for None." if o is None: return "?" else: return str(o) def estr(elements): "Format list of elements for printing." return ", ".join(e.shortstr() for e in elements) def recursive_chain(lists): for l in lists: if isinstance(l, str): yield l else: for s in recursive_chain(l): yield s class ExampleCounted(object): """An example class for classes of objects identified by a global counter. The old inheritance pattern is deprecated. Mimic this class instead. """ __slots__ = ("_count",) _globalcount = 0 def __init__(self, count=None): counted_init(self, count, ExampleCounted) def count(self): return self._count def counted_init(self, count=None, countedclass=None): if countedclass is None: countedclass = type(self) if count is None: count = countedclass._globalcount self._count = count if self._count >= countedclass._globalcount: countedclass._globalcount = self._count + 1 class Stack(list): "A stack datastructure." def __init__(self, *args): list.__init__(self, *args) def push(self, v): list.append(self, v) def peek(self): return self[-1] class StackDict(dict): "A dict that can be changed incrementally with 'd.push(k,v)' and have changes rolled back with 'k,v = d.pop()'." def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) self._l = [] def push(self, k, v): # Store previous state for this key self._l.append((k, self.get(k, None))) if v is None: if k in self: del self[k] else: self[k] = v def pop(self): # Restore previous state for this key k, v = self._l.pop() if v is None: if k in self: del self[k] else: self[k] = v return k, v class UFLTypeDict(dict): def __init__(self): dict.__init__(self) def __getitem__(self, key): return dict.__getitem__(self, key._uflclass) def __setitem__(self, key, value): return dict.__setitem__(self, key._uflclass, value) def __delitem__(self, key): return dict.__delitem__(self, key._uflclass) def __contains__(self, key): return dict.__contains__(self, key._uflclass) class UFLTypeDefaultDict(dict): def __init__(self, default): dict.__init__(self) def make_default(): return default self.setdefault(make_default) def __getitem__(self, key): return dict.__getitem__(self, key._uflclass) def __setitem__(self, key, value): return dict.__setitem__(self, key._uflclass, value) def __delitem__(self, key): return dict.__delitem__(self, key._uflclass) def __contains__(self, key): return dict.__contains__(self, key._uflclass) def strides(shape): if not shape: return () stride = 1 result = [1] for s in shape[-1:0:-1]: stride *= s result.append(stride) return tuple(reversed(result)) def component_to_index(component, shape): i = 0 for (c,s) in izip(component, strides(shape)): i += c*s return i def index_to_component(index, shape): assert index >= 0 component = [] a, b = -123, -123 for s in strides(shape): a = index // s b = index % s index = b component.append(a) assert all(c >= 0 for c in component) assert all(c < s for (c,s) in izip(component, shape)) return tuple(component) ufl-1.3.0/ufl/conditional.py000066400000000000000000000221661226300046600157520ustar00rootroot00000000000000"""This module defines classes for conditional expressions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-08-20 # Last changed: 2012-11-30 from ufl.log import warning, error from ufl.assertions import ufl_assert from ufl.operatorbase import Operator from ufl.constantvalue import as_ufl from ufl.precedence import parstr from ufl.exprequals import expr_equals #--- Condition classes --- class Condition(Operator): __slots__ = () def __init__(self): Operator.__init__(self) # Functions like these are an indication that a better type system could be useful: def free_indices(self): error("Calling free_indices on Condition is an error.") def index_dimensions(self): error("Calling index_dimensions on Condition is an error.") def shape(self): error("Calling shape on Condition is an error.") def __nonzero__(self): # Showing explicit error here to protect against misuse error("UFL conditions cannot be evaluated as bool in a Python context.") #return NotImplemented class BinaryCondition(Condition): __slots__ = ('_name', '_left', '_right',) def __init__(self, name, left, right): Operator.__init__(self) self._name = name self._left = as_ufl(left) self._right = as_ufl(right) if name in ('!=', '=='): # Since equals and not-equals are used for comparing representations, # we have to allow any shape here. The scalar properties must be # checked when used in conditional instead! pass elif name in ('&&', '||'): # Binary operators acting on boolean expressions allow only conditions ufl_assert(isinstance(self._left, Condition), "Expecting a Condition, not a %s." % self._left._uflclass) ufl_assert(isinstance(self._right, Condition), "Expecting a Condition, not a %s." % self._right._uflclass) else: # Binary operators acting on non-boolean expressions allow only scalars ufl_assert(self._left.shape() == () \ and self._right.shape() == (), "Expecting scalar arguments.") ufl_assert(self._left.free_indices() == () \ and self._right.free_indices() == (), "Expecting scalar arguments.") def operands(self): # A BinaryCondition should never be constructed directly, # so these two arguments correspond to the constructor # arguments of the subclasses EQ etc. return (self._left, self._right) def __str__(self): return "%s %s %s" % (parstr(self._left, self), self._name, parstr(self._right, self)) def __repr__(self): return "%s(%r, %r)" % (type(self).__name__, self._left, self._right) class EQ(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "==", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a == b) def __nonzero__(self): return expr_equals(self._left, self._right) class NE(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "!=", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a != b) def __nonzero__(self): return not expr_equals(self._left, self._right) class LE(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "<=", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a <= b) class GE(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, ">=", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a >= b) class LT(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "<", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a < b) class GT(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, ">", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a > b) class AndCondition(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "&&", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a and b) class OrCondition(BinaryCondition): __slots__ = () def __init__(self, left, right): BinaryCondition.__init__(self, "||", left, right) def evaluate(self, x, mapping, component, index_values): a = self._left.evaluate(x, mapping, component, index_values) b = self._right.evaluate(x, mapping, component, index_values) return bool(a or b) class NotCondition(Condition): __slots__ = ('_condition',) def __init__(self, condition): Condition.__init__(self) ufl_assert(isinstance(condition, Condition), "Expecting a condition.") self._condition = condition def operands(self): return (self._condition,) def evaluate(self, x, mapping, component, index_values): a = self._condition.evaluate(x, mapping, component, index_values) return bool(not a) def __str__(self): return "!(%s)" % (str(self._condition),) def __repr__(self): return "NotCondition(%r)" % (self._condition,) #--- Conditional expression (condition ? true_value : false_value) --- class Conditional(Operator): __slots__ = ("_condition", "_true_value", "_false_value",) def __init__(self, condition, true_value, false_value): Operator.__init__(self) ufl_assert(isinstance(condition, Condition), "Expectiong condition as first argument.") true_value = as_ufl(true_value) false_value = as_ufl(false_value) tsh = true_value.shape() fsh = false_value.shape() ufl_assert(tsh == fsh, "Shape mismatch between conditional branches.") tfi = true_value.free_indices() ffi = false_value.free_indices() ufl_assert(tfi == ffi, "Free index mismatch between conditional branches.") if isinstance(condition, (EQ,NE)): ufl_assert(condition._left.shape() == () and condition._left.free_indices() == () and condition._right.shape() == () and condition._right.free_indices() == (), "Non-scalar == or != is not allowed.") self._condition = condition self._true_value = true_value self._false_value = false_value def operands(self): return (self._condition, self._true_value, self._false_value) def free_indices(self): return self._true_value.free_indices() def index_dimensions(self): return self._true_value.index_dimensions() def shape(self): return self._true_value.shape() def evaluate(self, x, mapping, component, index_values): c = self._condition.evaluate(x, mapping, component, index_values) if c: a = self._true_value else: a = self._false_value return a.evaluate(x, mapping, component, index_values) def __str__(self): return "%s ? %s : %s" % tuple(parstr(o, self) for o in self.operands()) def __repr__(self): return "Conditional(%r, %r, %r)" % self.operands() ufl-1.3.0/ufl/constantvalue.py000066400000000000000000000323441226300046600163340ustar00rootroot00000000000000"This module defines classes representing constant values." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2011. # # First added: 2008-11-01 # Last changed: 2011-11-10 from itertools import izip from ufl.log import warning, error from ufl.assertions import ufl_assert, expecting_python_scalar from ufl.expr import Expr from ufl.terminal import Terminal from ufl.indexing import Index, FixedIndex from ufl.common import EmptyDict #--- "Low level" scalar types --- # TODO: Using high precision float from numpy if available? int_type = int float_type = float python_scalar_types = (int, float) #try: # import numpy # int_type = numpy.int64 # float_type = numpy.float96 # python_scalar_types += (int_type, float_type) #except: # pass # Precision for float formatting precision = None def format_float(x): "Format float value based on global UFL precision." if precision is None: return repr(x) else: return ("%%.%dg" % precision) % x #--- Base classes for constant types --- class ConstantValue(Terminal): __slots__ = () def __init__(self): Terminal.__init__(self) def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return True def __getnewargs__(self): return (self._dim,) class IndexAnnotated(ConstantValue): """Class to annotate expressions that don't depend on indices with a set of free indices, used internally to keep index properties intact during automatic differentiation.""" __slots__ = ("_shape", "_free_indices", "_index_dimensions") def __init__(self, shape=(), free_indices=(), index_dimensions=None): ConstantValue.__init__(self) if not all(isinstance(i, int) for i in shape): error("Expecting tuple of int.") if not all(isinstance(i, Index) for i in free_indices): error("Expecting tuple of Index objects.") self._shape = shape self._free_indices = tuple(sorted(free_indices, key=lambda x: x.count())) self._index_dimensions = dict(index_dimensions) if index_dimensions else EmptyDict if (set(self._free_indices) ^ set(self._index_dimensions.keys())): error("Index set mismatch.") #--- Class for representing zero tensors of different shapes --- # TODO: Add geometric dimension and Argument dependencies to Zero? class Zero(IndexAnnotated): "UFL literal type: Representation of a zero valued expression." __slots__ = () _cache = {} def __new__(cls, shape=(), free_indices=(), index_dimensions=None): if free_indices: self = IndexAnnotated.__new__(cls) else: self = Zero._cache.get(shape) if self is None: self = IndexAnnotated.__new__(cls) Zero._cache[shape] = self return self def __getnewargs__(self): return (self._shape, self._free_indices, self._index_dimensions) def __init__(self, shape=(), free_indices=(), index_dimensions=None): if not hasattr(self, '_shape'): ufl_assert(isinstance(free_indices, tuple), "Expecting tuple of free indices, not %s" % str(free_indices)) IndexAnnotated.__init__(self, shape, free_indices, index_dimensions) def reconstruct(self, free_indices=None): if not free_indices: return self ufl_assert(len(free_indices) == len(self._free_indices), "Size mismatch between old and new indices.") new_index_dimensions = dict((b, self._index_dimensions[a]) for (a,b) in izip(self._free_indices, free_indices)) return Zero(self._shape, free_indices, new_index_dimensions) def shape(self): return self._shape def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def evaluate(self, x, mapping, component, index_values): return 0.0 def __str__(self): if self._shape == () and self._free_indices == (): return "0" return "(0<%r, %r>)" % (self._shape, self._free_indices) def __repr__(self): return "Zero(%r, %r, %r)" % (self._shape, self._free_indices, self._index_dimensions) def __eq__(self, other): if not isinstance(other, Zero): return isinstance(other, (int,float)) and other == 0 if self is other: return True return (self._shape == other._shape and self._free_indices == other._free_indices and self._index_dimensions == other._index_dimensions) def __neg__(self): return self def __abs__(self): return self def __nonzero__(self): return False def __float__(self): return 0.0 def __int__(self): return 0 def zero(*shape): "UFL literal constant: Return a zero tensor with the given shape." if len(shape) == 1 and isinstance(shape[0], tuple): return Zero(shape[0]) else: return Zero(shape) #--- Scalar value types --- class ScalarValue(IndexAnnotated): "A constant scalar value." __slots__ = ("_value",) def __new__(cls, value, shape=(), free_indices=(), index_dimensions=None): is_python_scalar(value) or expecting_python_scalar(value) if value == 0: return Zero(shape, free_indices, index_dimensions) return IndexAnnotated.__new__(cls) def __getnewargs__(self): return (self._value, self._shape, self._free_indices, self._index_dimensions) def __init__(self, value, shape=(), free_indices=(), index_dimensions=None): IndexAnnotated.__init__(self, shape, free_indices, index_dimensions) self._value = value def reconstruct(self, free_indices=None): "Reconstruct with new free indices." if not free_indices: return self ufl_assert(len(free_indices) == len(self._free_indices), "Size mismatch between old and new indices.") new_index_dimensions = dict((b, self._index_dimensions[a]) for (a,b) in izip(self._free_indices, free_indices)) return self._uflclass(self._value, self._shape, free_indices, new_index_dimensions) def shape(self): return self._shape def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def value(self): return self._value def evaluate(self, x, mapping, component, index_values): return self._value def __eq__(self, other): """This is implemented to allow comparison with python scalars. Note that this will make IntValue(1) != FloatValue(1.0), but ufl-python comparisons like IntValue(1) == 1.0 FloatValue(1.0) == 1 can still succeed. These will however not have the same hash value and therefore not collide in a dict.""" if not isinstance(other, self._uflclass): return isinstance(other, (int,float)) and other == self._value else: return self._value == other._value def __str__(self): return str(self._value) def __float__(self): return float(self._value) def __int__(self): return int(self._value) def __neg__(self): return type(self)(-self._value) def __abs__(self): return type(self)(abs(self._value)) class FloatValue(ScalarValue): "UFL literal type: Representation of a constant scalar floating point value." __slots__ = () def __init__(self, value, shape=(), free_indices=(), index_dimensions=None): ScalarValue.__init__(self, float_type(value), shape, free_indices, index_dimensions) def __repr__(self): return "%s(%s, %s, %s, %s)" % (type(self).__name__, format_float(self._value), repr(self._shape), repr(self._free_indices), repr(self._index_dimensions)) class IntValue(ScalarValue): "UFL literal type: Representation of a constant scalar integer value." __slots__ = () _cache = {} def __new__(cls, value, shape=(), free_indices=(), index_dimensions=None): # Check if it is cache-able if shape or free_indices or index_dimensions or abs(value) > 100: self = ScalarValue.__new__(cls, value, shape, free_indices, index_dimensions) else: self = IntValue._cache.get(value) if self is None: self = ScalarValue.__new__(cls, value, shape, free_indices, index_dimensions) IntValue._cache[value] = self return self def __init__(self, value, shape=(), free_indices=(), index_dimensions=None): if not hasattr(self, '_value'): ScalarValue.__init__(self, int_type(value), shape, free_indices, index_dimensions) def __repr__(self): return "%s(%s, %s, %s, %s)" % (type(self).__name__, repr(self._value), repr(self._shape), repr(self._free_indices), repr(self._index_dimensions)) #--- Identity matrix --- class Identity(ConstantValue): "UFL literal type: Representation of an identity matrix." __slots__ = ("_dim",) def __init__(self, dim): ConstantValue.__init__(self) self._dim = dim def shape(self): return (self._dim, self._dim) def evaluate(self, x, mapping, component, index_values): a, b = component return 1 if a == b else 0 def __getitem__(self, key): ufl_assert(len(key) == 2, "Size mismatch for Identity.") if all(isinstance(k, (int, FixedIndex)) for k in key): return IntValue(1) if (int(key[0]) == int(key[1])) else Zero() return Expr.__getitem__(self, key) def __str__(self): return "I" def __repr__(self): return "Identity(%d)" % self._dim def __eq__(self, other): return isinstance(other, Identity) and self._dim == other._dim #--- Permutation symbol --- class PermutationSymbol(ConstantValue): """UFL literal type: Representation of a permutation symbol. This is also known as the Levi-Civita symbol, antisymmetric symbol, or alternating symbol.""" __slots__ = ("_dim",) def __init__(self, dim): ConstantValue.__init__(self) self._dim = dim def shape(self): s = () for i in range(self._dim): s += (self._dim,) return s def evaluate(self, x, mapping, component, index_values): return self.__eps(component) def __getitem__(self, key): ufl_assert(len(key) == self._dim, "Size mismatch for PermutationSymbol.") if all(isinstance(k, (int, FixedIndex)) for k in key): return self.__eps(key) return Expr.__getitem__(self, key) def __str__(self): return "eps" def __repr__(self): return "PermutationSymbol(%d)" % self._dim def __eq__(self, other): return isinstance(other, PermutationSymbol) and self._dim == other._dim def __eps(self, x): """This function body is taken from http://www.mathkb.com/Uwe/Forum.aspx/math/29865/N-integer-Levi-Civita""" result = IntValue(1) for i, x1 in enumerate(x): for j in xrange(i + 1, len(x)): x2 = x[j] if x1 > x2: result = -result elif x1 == x2: return Zero() return result #--- Helper functions --- def is_python_scalar(expression): "Return True iff expression is of a Python scalar type." return isinstance(expression, python_scalar_types) def is_ufl_scalar(expression): """Return True iff expression is scalar-valued, but possibly containing free indices.""" return isinstance(expression, Expr) and not expression.shape() def is_true_ufl_scalar(expression): """Return True iff expression is scalar-valued, with no free indices.""" return isinstance(expression, Expr) and \ not (expression.shape() or expression.free_indices()) def as_ufl(expression): "Converts expression to an Expr if possible." if isinstance(expression, Expr): return expression if isinstance(expression, (int, int_type)): return IntValue(expression) if isinstance(expression, (float, float_type)): return FloatValue(expression) error(("Invalid type conversion: %s can not be converted to any UFL type.\n"+\ "The representation of the object is:\n%r") % (type(expression), expression)) ufl-1.3.0/ufl/differentiation.py000066400000000000000000000316421226300046600166200ustar00rootroot00000000000000"Differential operators." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # # First added: 2008-03-14 # Last changed: 2011-11-10 from ufl.log import warning, error from ufl.assertions import ufl_assert from ufl.common import subdict, mergedicts, EmptyDict from ufl.expr import Expr from ufl.terminal import Terminal, Data from ufl.operatorbase import Operator, Tuple from ufl.constantvalue import Zero from ufl.indexing import Index, FixedIndex, MultiIndex, as_multi_index from ufl.indexed import Indexed from ufl.indexutils import unique_indices from ufl.variable import Variable from ufl.precedence import parstr #--- Basic differentiation objects --- class Derivative(Operator): "Base class for all derivative types." __slots__ = () def __init__(self): Operator.__init__(self) class CoefficientDerivative(Derivative): """Derivative of the integrand of a form w.r.t. the degrees of freedom in a discrete Coefficient.""" __slots__ = ("_integrand", "_coefficients", "_arguments", "_coefficient_derivatives") def __new__(cls, integrand, coefficients, arguments, coefficient_derivatives): ufl_assert(isinstance(coefficients, Tuple), "Expecting Tuple instance with Coefficients.") ufl_assert(isinstance(arguments, Tuple), "Expecting Tuple instance with Arguments.") ufl_assert(isinstance(coefficient_derivatives, (dict, Data)), "Expecting a dict for coefficient derivatives.") if isinstance(integrand, Zero): return integrand return Derivative.__new__(cls) def __init__(self, integrand, coefficients, arguments, coefficient_derivatives): Derivative.__init__(self) self._integrand = integrand self._coefficients = coefficients self._arguments = arguments if isinstance(coefficient_derivatives, Data): self._coefficient_derivatives = coefficient_derivatives else: self._coefficient_derivatives = Data(coefficient_derivatives) def operands(self): return (self._integrand, self._coefficients, self._arguments, self._coefficient_derivatives) def shape(self): return self._integrand.shape() def free_indices(self): return self._integrand.free_indices() def index_dimensions(self): return self._integrand.index_dimensions() def __str__(self): return "d/dfj { %s }, with fh=%s, dfh/dfj = %s, and coefficient derivatives %s"\ % (self._integrand, self._coefficients, self._arguments, self._coefficient_derivatives) def __repr__(self): return "CoefficientDerivative(%r, %r, %r, %r)"\ % (self._integrand, self._coefficients, self._arguments, self._coefficient_derivatives) def split_indices(expression, idx): idims = dict(expression.index_dimensions()) if isinstance(idx, Index) and idims.get(idx) is None: idims[idx] = expression.geometric_dimension() fi = unique_indices(expression.free_indices() + (idx,)) return fi, idims class VariableDerivative(Derivative): __slots__ = ("_f", "_v", "_free_indices", "_index_dimensions", "_shape",) def __new__(cls, f, v): # Return zero if expression is trivially independent of variable if isinstance(f, Terminal): free_indices = tuple(set(f.free_indices()) ^ set(v.free_indices())) index_dimensions = mergedicts([f.index_dimensions(), v.index_dimensions()]) index_dimensions = subdict(index_dimensions, free_indices) return Zero(f.shape() + v.shape(), free_indices, index_dimensions) return Derivative.__new__(cls) def __init__(self, f, v): Derivative.__init__(self) ufl_assert(isinstance(f, Expr), "Expecting an Expr in VariableDerivative.") if isinstance(v, Indexed): ufl_assert(isinstance(v._expression, Variable), \ "Expecting a Variable in VariableDerivative.") error("diff(f, v[i]) isn't handled properly in all code.") # TODO: Should we allow this? Can do diff(f, v)[..., i], which leads to additional work but does the same. else: ufl_assert(isinstance(v, Variable), \ "Expecting a Variable in VariableDerivative.") self._f = f self._v = v fi = f.free_indices() vi = v.free_indices() fid = f.index_dimensions() vid = v.index_dimensions() #print "set(fi)", set(fi) #print "set(vi)", set(vi) #print "^", (set(fi) ^ set(vi)) ufl_assert(not (set(fi) & set(vi)), \ "Repeated indices not allowed in VariableDerivative.") # TODO: Allow diff(f[i], v[i]) = sum_i VariableDerivative(f[i], v[i])? Can implement direct expansion in diff as a first approximation. self._free_indices = tuple(fi + vi) self._index_dimensions = dict(fid) self._index_dimensions.update(vid) self._shape = f.shape() + v.shape() def operands(self): return (self._f, self._v) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return self._shape def __str__(self): if isinstance(self._f, Terminal): return "d%s/d[%s]" % (self._f, self._v) return "d/d[%s] %s" % (self._v, parstr(self._f, self)) def __repr__(self): return "VariableDerivative(%r, %r)" % (self._f, self._v) #--- Compound differentiation objects --- class CompoundDerivative(Derivative): "Base class for all compound derivative types." __slots__ = () def __init__(self): Derivative.__init__(self) class Grad(CompoundDerivative): __slots__ = ("_f", "_dim",) def __new__(cls, f): # Return zero if expression is trivially constant dim = f.geometric_dimension() if f.is_cellwise_constant(): free_indices = f.free_indices() index_dimensions = subdict(f.index_dimensions(), free_indices) return Zero(f.shape() + (dim,), free_indices, index_dimensions) return CompoundDerivative.__new__(cls) def __init__(self, f): CompoundDerivative.__init__(self) self._f = f self._dim = f.geometric_dimension() def reconstruct(self, op): "Return a new object of the same type with new operands." if op.is_cellwise_constant(): ufl_assert(op.shape() == self._f.shape(), "Operand shape mismatch in Grad reconstruct.") ufl_assert(self._f.free_indices() == op.free_indices(), "Free index mismatch in Grad reconstruct.") return Zero(self.shape(), self.free_indices(), self.index_dimensions()) return self.__class__._uflclass(op) def evaluate(self, x, mapping, component, index_values, derivatives=()): "Get child from mapping and return the component asked for." r = len(component) component, i = component[:-1], component[-1] derivatives = derivatives + (i,) result = self._f.evaluate(x, mapping, component, index_values, derivatives=derivatives) return result def operands(self): return (self._f,) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def shape(self): return self._f.shape() + (self._dim,) def __str__(self): return "grad(%s)" % self._f def __repr__(self): return "Grad(%r)" % self._f class Div(CompoundDerivative): __slots__ = ("_f",) def __new__(cls, f): ufl_assert(not f.free_indices(), \ "TODO: Taking divergence of an expression with free indices,"\ "should this be a valid expression? Please provide examples!") # Return zero if expression is trivially constant if f.is_cellwise_constant(): return Zero(f.shape()[:-1]) # No free indices asserted above return CompoundDerivative.__new__(cls) def __init__(self, f): CompoundDerivative.__init__(self) self._f = f def operands(self): return (self._f, ) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def shape(self): return self._f.shape()[:-1] def __str__(self): return "div(%s)" % self._f def __repr__(self): return "Div(%r)" % self._f class NablaGrad(CompoundDerivative): __slots__ = ("_f", "_dim",) def __new__(cls, f): # Return zero if expression is trivially constant dim = f.geometric_dimension() if f.is_cellwise_constant(): free_indices = f.free_indices() index_dimensions = subdict(f.index_dimensions(), free_indices) return Zero((dim,) + f.shape(), free_indices, index_dimensions) return CompoundDerivative.__new__(cls) def __init__(self, f): CompoundDerivative.__init__(self) self._f = f self._dim = f.geometric_dimension() def reconstruct(self, op): "Return a new object of the same type with new operands." if op.is_cellwise_constant(): ufl_assert(op.shape() == self._f.shape(), "Operand shape mismatch in NablaGrad reconstruct.") ufl_assert(self._f.free_indices() == op.free_indices(), "Free index mismatch in NablaGrad reconstruct.") return Zero(self.shape(), self.free_indices(), self.index_dimensions()) return self.__class__._uflclass(op) def operands(self): return (self._f, ) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def shape(self): return (self._dim,) + self._f.shape() def __str__(self): return "nabla_grad(%s)" % self._f def __repr__(self): return "NablaGrad(%r)" % self._f class NablaDiv(CompoundDerivative): __slots__ = ("_f",) def __new__(cls, f): ufl_assert(not f.free_indices(), \ "TODO: Taking divergence of an expression with free indices,"\ "should this be a valid expression? Please provide examples!") # Return zero if expression is trivially constant if f.is_cellwise_constant(): return Zero(f.shape()[1:]) # No free indices asserted above return CompoundDerivative.__new__(cls) def __init__(self, f): CompoundDerivative.__init__(self) self._f = f def operands(self): return (self._f, ) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def shape(self): return self._f.shape()[1:] def __str__(self): return "nabla_div(%s)" % self._f def __repr__(self): return "NablaDiv(%r)" % self._f class Curl(CompoundDerivative): __slots__ = ("_f", "_shape",) def __new__(cls, f): # Validate input sh = f.shape() ufl_assert(f.shape() in ((), (2,), (3,)), "Expecting a scalar, 2D vector or 3D vector.") ufl_assert(not f.free_indices(), \ "TODO: Taking curl of an expression with free indices, should this be a valid expression? Please provide examples!") # Return zero if expression is trivially constant if f.is_cellwise_constant(): sh = { (): (2,), (2,): (), (3,): (3,) }[sh] #free_indices = f.free_indices() #index_dimensions = subdict(f.index_dimensions(), free_indices) #return Zero((f.geometric_dimension(),), free_indices, index_dimensions) return Zero(sh) return CompoundDerivative.__new__(cls) def __init__(self, f): CompoundDerivative.__init__(self) sh = { (): (2,), (2,): (), (3,): (3,) }[f.shape()] self._f = f self._shape = sh def operands(self): return (self._f, ) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def shape(self): return self._shape def __str__(self): return "curl(%s)" % self._f def __repr__(self): return "Curl(%r)" % self._f ufl-1.3.0/ufl/domains.py000066400000000000000000000131711226300046600150750ustar00rootroot00000000000000"Types for specification of domains and subdomain relations." # Copyright (C) 2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2013-01-10 # Last changed: 2013-01-10 from ufl.log import warning, error from ufl.assertions import ufl_assert from ufl.common import istr from ufl.terminal import Terminal from ufl.geometry import as_cell, Cell class DomainDescription(object): __slots__ = ('_cell', '_name', '_gdim', '_tdim', ) def __init__(self, cell, name, gdim, tdim): ufl_assert(isinstance(cell, Cell), "Expecting a Cell.") self._cell = cell self._name = name or "%s_%s" % (cell.cellname(), "multiverse") self._gdim = gdim or cell.geometric_dimension() self._tdim = tdim or cell.topological_dimension() ufl_assert(isinstance(self._name, str), "Expecting a string.") ufl_assert(isinstance(self._gdim, int), "Expecting an integer.") ufl_assert(isinstance(self._tdim, int), "Expecting an integer.") def geometric_dimension(self): return self._gdim def topological_dimension(self): return self._tdim def cell(self): return self._cell def name(self): return self._name def top_domain(self): raise NotImplementedException("Missing implementation of top_domain.") def __eq__(self, other): return (isinstance(other, DomainDescription) and self._cell == other._cell and self._name == other._name and self._gdim == other._gdim and self._tdim == other._tdim) def __hash__(self): return hash(repr(self)) def __lt__(self, other): return repr(self) < repr(other) # FIXME: Sort in a more predictable way class Domain(DomainDescription): __slots__ = () def __init__(self, cell, name=None, gdim=None, tdim=None): DomainDescription.__init__(self, cell, name, gdim, tdim) def __repr__(self): return "Domain(%r, %r, %r, %r)" % (self._cell, self._name, self._gdim, self._tdim) def __eq__(self, other): return (isinstance(other, Domain) and DomainDescription.__eq__(self, other)) def top_domain(self): return self def subdomain_ids(self): return None def __getitem__(self, subdomain_id): if isinstance(subdomain_id, int): return Region(self, (subdomain_id,), "%s_%d" % (self._name, subdomain_id)) else: error("Invalid subdomain label %r, expecting integer." % (subdomain_id,)) class Region(DomainDescription): __slots__ = ('_parent', '_subdomain_ids') def __init__(self, parent, subdomain_ids, name): DomainDescription.__init__(self, parent.cell(), name, parent._gdim, parent._tdim) self._parent = parent self._subdomain_ids = tuple(sorted(set(subdomain_ids))) ufl_assert(name != parent.name(), "Cannot assign same name to top level domain and region.") def top_domain(self): return self._parent def subdomain_ids(self): return self._subdomain_ids def __repr__(self): return "Region(%r, %r, %r)" % (self._parent, self._subdomain_ids, self._name) def __eq__(self, other): return (isinstance(other, Region) and self._parent == other._parent and self._subdomain_ids == other._subdomain_ids and DomainDescription.__eq__(self, other)) # Map cells to a default domain for compatibility and cache this: _default_domains = {} def as_domain(domain): if isinstance(domain, DomainDescription): return domain else: cell = as_cell(domain) if isinstance(cell, Cell): if cell not in _default_domains: _default_domains[cell] = Domain(cell) return _default_domains[cell] else: error("Invalid domain %s." % str(domain)) # TODO: Move somewhere else def extract_top_domains(integrand): from ufl.terminal import FormArgument from ufl.algorithms.traversal import traverse_terminals top_domains = set() for t in traverse_terminals(integrand): if isinstance(t, FormArgument): domain = t.element().domain() if domain is not None: top_domains.add(domain.top_domain()) # FIXME: Check geometry here too when that becomes necessary return sorted(top_domains) def extract_domains(integrand): from ufl.terminal import FormArgument from ufl.algorithms.traversal import traverse_terminals regions = set() for t in traverse_terminals(integrand): # FIXME: Need to analyse which components of functions are actually referenced if isinstance(t, FormArgument): reg = t.element().regions() # FIXME: Implement regions.update(reg) regions.update(r.top_domain() for r in reg) # FIXME: Check geometry here too when that becomes necessary return sorted(regions) ufl-1.3.0/ufl/equation.py000066400000000000000000000027661226300046600153000ustar00rootroot00000000000000"The Equation class, used to express equations like a == L." # Copyright (C) 2012 Anders Logg and Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2011-06-21 # Last changed: 2011-06-22 class Equation: """This class is used to represent equations expressed by the "==" operator. Examples include a == L and F == 0 where a, L and F are Form objects.""" def __init__(self, lhs, rhs): "Create equation lhs == rhs" self.lhs = lhs self.rhs = rhs def __nonzero__(self): return type(self.lhs) == type(self.rhs) and \ repr(self.lhs) == repr(self.rhs) # REPR not a problem def __eq__(self, other): return isinstance(other, Equation) and \ bool(self.lhs == other.lhs) and \ bool(self.rhs == other.rhs) def __repr__(self): return "Equation(%r, %r)" % (self.lhs, self.rhs) ufl-1.3.0/ufl/expr.py000066400000000000000000000176601226300046600144300ustar00rootroot00000000000000"""This module defines the Expr class, the superclass for all expression tree node types in UFL. NB! A note about other operators not implemented here: More operators (special functions) on Exprs are defined in exproperators.py, as well as the transpose "A.T" and spatial derivative "a.dx(i)". This is to avoid circular dependencies between Expr and its subclasses. """ # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-03-14 # Last changed: 2012-03-20 #--- The base object for all UFL expression tree nodes --- from collections import defaultdict from ufl.log import warning, error def print_expr_statistics(): for k in sorted(Expr._class_usage_statistics.keys()): born = Expr._class_usage_statistics[k] live = born - Expr._class_del_statistics.get(k, 0) print "%40s: %10d / %10d" % (k.__name__, live, born) class Expr(object): "Base class for all UFL objects." # Freeze member variables for objects of this class __slots__ = () _class_usage_statistics = defaultdict(int) _class_del_statistics = defaultdict(int) def __init__(self): # Comment out this line to disable class construction # statistics (used in some unit tests) Expr._class_usage_statistics[self.__class__._uflclass] += 1 def x__del__(self): # Enable for profiling # Comment out this line to disable class construction # statistics (used for manual memory profiling) Expr._class_del_statistics[self.__class__._uflclass] += 1 #=== Abstract functions that must be implemented by subclasses === #--- Functions for reconstructing expression --- # All subclasses must implement reconstruct def reconstruct(self, *operands): "Return a new object of the same type with new operands." raise NotImplementedError(self.__class__.reconstruct) #--- Functions for expression tree traversal --- # All subclasses must implement operands def operands(self): "Return a sequence with all subtree nodes in expression tree." raise NotImplementedError(self.__class__.operands) #--- Functions for general properties of expression --- # All subclasses must implement shape def shape(self): "Return the tensor shape of the expression." raise NotImplementedError(self.__class__.shape) # Subclasses can implement rank if it is known directly def rank(self): "Return the tensor rank of the expression." return len(self.shape()) # All subclasses must implement domain if it is known def domain(self): # TODO: Is it better to use an external traversal algorithm for this? "Return the domain this expression is defined on." result = None for o in self.operands(): domain = o.domain().top_domain() if domain is not None: result = domain # Best we have so far cell = domain.cell() if cell is not None: # A domain with a fully defined cell, we have a winner! break return result # All subclasses must implement cell if it is known def cell(self): # TODO: Deprecate this "Return the cell this expression is defined on." for o in self.operands(): cell = o.cell() if cell is not None: return cell return None # This function was introduced to clarify and # eventually reduce direct dependencies on cells. def geometric_dimension(self): # TODO: Deprecate this, use external analysis algorithm "Return the geometric dimension this expression lives in." cell = self.cell() if cell is None: error("Cannot get geometric dimension from an expression with no cell!") return cell.geometric_dimension() def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." raise NotImplementedError(self.__class__.is_cellwise_constant) #--- Functions for float evaluation --- def evaluate(self, x, mapping, component, index_values, derivatives=()): """Evaluate expression at given coordinate with given values for terminals.""" raise NotImplementedError(self.__class__.evaluate) def __float__(self): if self.shape() != () or self.free_indices() != (): raise NotImplementedError(self.__class__.__float__) return self(()) # No known x #--- Functions for index handling --- # All subclasses that can have indices must implement free_indices def free_indices(self): "Return a tuple with the free indices (unassigned) of the expression." raise NotImplementedError(self.__class__.free_indices) # All subclasses must implement index_dimensions def index_dimensions(self): """Return a dict with the free or repeated indices in the expression as keys and the dimensions of those indices as values.""" raise NotImplementedError(self.__class__.index_dimensions) #--- Special functions for string representations --- # All subclasses must implement signature_data def signature_data(self): "Return data that uniquely identifies this object." raise NotImplementedError(self.__class__.signature_data) # All subclasses must implement __repr__ def __repr__(self): "Return string representation this object can be reconstructed from." raise NotImplementedError(self.__class__.__repr__) # All subclasses must implement __str__ def __str__(self): "Return pretty print string representation of this object." raise NotImplementedError(self.__class__.__str__) def _repr_latex_(self): from ufl.algorithms import ufl2latex return "$%s$" % ufl2latex(self) def _repr_png_(self): from IPython.lib.latextools import latex_to_png return latex_to_png(self._repr_latex_()) #--- Special functions used for processing expressions --- def __hash__(self): "Compute a hash code for this expression. Used by sets and dicts." raise NotImplementedError(self.__class__.__hash__) def __eq__(self, other): """Checks whether the two expressions are represented the exact same way. This does not check if the expressions are mathematically equal or equivalent! Used by sets and dicts.""" raise NotImplementedError(self.__class__.__eq__) def __nonzero__(self): "By default, all Expr are nonzero." return True def __len__(self): "Length of expression. Used for iteration over vector expressions." s = self.shape() if len(s) == 1: return s[0] raise NotImplementedError("Cannot take length of non-vector expression.") def __iter__(self): "Iteration over vector expressions." for i in range(len(self)): yield self[i] def __floordiv__(self, other): "UFL does not support integer division." raise NotImplementedError(self.__class__.__floordiv__) def __pos__(self): "Unary + is a no-op." return self #def __getnewargs__(self): # TODO: Test pickle and copy with this. Must implement differently for Terminal objects though. # "Used for pickle and copy operations." # return self.operands() ufl-1.3.0/ufl/exprequals.py000066400000000000000000000113271226300046600156350ustar00rootroot00000000000000 from ufl.operatorbase import Operator from ufl.terminal import Terminal from ufl.common import fast_pre_traversal def _expr_equals0(a, b): # TODO: Which is faster? # Cutoff for different type if type(a) != type(b): return False # Cutoff for same object if a is b: return True # Iterate over pairs of potentially matching subexpressions input = [(a, b)] while input: a, b = input.pop() # Cutoff for different type if type(a) != type(b): return False # Get operands aops = a.operands() bops = b.operands() if aops: if len(aops) != len(bops): return False # Add children for checking input.extend(izip(aops, bops)) else: # Compare terminals if not a == b: return False # Everything checked out fine, expressions must be equal return True def _expr_equals1(a, b): # TODO: Which is faster? # Cutoff for different type if type(a) != type(b): return False # Cutoff for same object if a is b: return True # Compare entire expression structure for x,y in izip(fast_pre_traversal(a), fast_pre_traversal(b)): if type(x) != type(y): return False #if isinstance(Terminal, x) and not x == y: if x.operands() == () and not x == y: return False # Equal terminals and types, a and b must be equal return True def _expr_equals2(a, b): # Cutoff for different type if type(a) != type(b): return False # Cutoff for same object if a is b: return True from ufl.algorithms.traversal import traverse_terminals, traverse_operands # Check for equal terminals for x,y in izip(traverse_terminals(a), traverse_terminals(b)): if x != y: return False # Check for matching operator types for x,y in izip(traverse_operands(a), traverse_operands(b)): if type(x) != type(y): return False # Equal terminals and operands, a and b must be equal return True equalsrecursed = {} equalscalls = {} collisions = {} def print_collisions(): print print "Collision statistics:" keys = sorted(equalscalls.keys(), key=lambda x: collisions.get(x,0)) for k in keys: co = collisions.get(k,0) ca = equalscalls[k] print k, co, ca, int(100.0*co/ca) print "Recursion statistics:" keys = sorted(keys, key=lambda x: equalsrecursed.get(x,0)) for k in keys: r = equalsrecursed.get(k,0) ca = equalscalls[k] print k, r, ca, int(100.0*r/ca) print def _expr_equals3(self, other): # Much faster than the more complex algorithms above! """Checks whether the two expressions are represented the exact same way. This does not check if the expressions are mathematically equal or equivalent! Used by sets and dicts.""" # Code for counting number of equals calls: #equalscalls[type(self)] = equalscalls.get(type(self),0) + 1 # Fast cutoff for common case if type(self) != type(other): return False # TODO: Test how this affects the run time: # Compare hashes if hash is cached # (NB! never access _hash directly, it may be computed on demand in __hash__) if (hasattr(self, "_hash") and hash(self) != hash(other)): return False # Large objects are costly to compare with themselves if self is other: return True if isinstance(self, Operator): # Just let python handle the recursion #equal = self.operands() == other.operands() # Recurse manually to call _expr_equals3 directly without the class EQ overhead! equal = all(_expr_equals3(a, b) for (a,b) in zip(self.operands(), other.operands())) else: # Compare terminal representations to include all terminal data #equal = repr(self) == repr(other) # Compare terminals with custom == to capture subclass overloading of __eq__ equal = self == other # At this point, self and other has the same hash, and equal _should_ be True... # Code for measuring amount of collisions: #if not equal: # collisions[type(self)] = collisions.get(type(self), 0) + 1 # Code for counting number of recursive calls: #equalsrecursed[type(self)] = equalsrecursed.get(type(self),0) + 1 # Debugging check: (has been enabled for a long while without any fails as of nov. 30th 2012 #req = repr(self) == repr(other) #if req != equal: # This may legally fail for test/trial functions from PyDOLFIN # print '\n'*3 # print self # print other # print '\n'*3 # ufl_error("INVALID COMPARISON!") return equal expr_equals = _expr_equals3 ufl-1.3.0/ufl/exproperators.py000066400000000000000000000335631226300046600163670ustar00rootroot00000000000000"""This module attaches special functions to Expr. This way we avoid circular dependencies between e.g. Sum and its superclass Expr.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-08-18 # Last changed: 2013-01-03 from itertools import chain, izip from ufl.log import error from ufl.assertions import ufl_assert from ufl.common import mergedicts, subdict, StackDict from ufl.expr import Expr from ufl.operatorbase import Operator from ufl.constantvalue import Zero, as_ufl, python_scalar_types from ufl.algebra import Sum, Product, Division, Power, Abs from ufl.tensoralgebra import Transposed, Inner from ufl.indexing import MultiIndex, Index, FixedIndex, IndexBase, indices from ufl.indexed import Indexed from ufl.indexsum import IndexSum from ufl.indexutils import repeated_indices, single_indices from ufl.tensors import as_tensor, ComponentTensor from ufl.restriction import PositiveRestricted, NegativeRestricted from ufl.differentiation import Grad #--- Boolean operators --- from ufl.conditional import EQ, NE, LE, GE, LT, GT # AndCondition, OrCondition, NotCondition, Conditional #def _eq(left, right): # "UFL operator: A boolean expresion (left == right) for use with conditional." # return EQ(left, right) #def _ne(left, right): # "UFL operator: A boolean expresion (left != right) for use with conditional." # return NE(left, right) def _le(left, right): "UFL operator: A boolean expresion (left <= right) for use with conditional." return LE(left, right) def _ge(left, right): "UFL operator: A boolean expresion (left >= right) for use with conditional." return GE(left, right) def _lt(left, right): "UFL operator: A boolean expresion (left < right) for use with conditional." return LT(left, right) def _gt(left, right): "UFL operator: A boolean expresion (left > right) for use with conditional." return GT(left, right) # '==' needs to implement comparison of expression representations for use in # hashmaps (dict and set), but the others can be overloaded in the language. # It is possible that we can overload eq as well, but we'll need to fix some # issues first and also check for a possible significant performance hit with # compilation of complex forms. Replacing a==b with equiv(a,b) all over the # code could be one way to reduce such a performance hit, but we cannot do # anything about dict and set calling __eq__... from ufl.exprequals import expr_equals Expr.__eq__ = expr_equals #Expr.__eq__ = _eq # != is used at least by tests, possibly in code as well, and must mean # the opposite of ==, i.e. when evaluated as bool it must mean equal representation. # To keep things simple and consistent we treat it just like ==. #def not_expr_equals(self, other): # return not expr_equals(self, other) #Expr.__ne__ = not_expr_equals #Expr.__ne__ = _ne Expr.__lt__ = _lt Expr.__gt__ = _gt Expr.__le__ = _le Expr.__ge__ = _ge #Expr.__and__ = And #Expr.__or__ = Or #Expr.__xor__ = Xor #--- Helper functions for product handling --- def _mult(a, b): # Discover repeated indices, which results in index sums ai = a.free_indices() bi = b.free_indices() ii = ai + bi ri = repeated_indices(ii) # Pick out valid non-scalar products here (dot products): # - matrix-matrix (A*B, M*grad(u)) => A . B # - matrix-vector (A*v) => A . v s1, s2 = a.shape(), b.shape() r1, r2 = len(s1), len(s2) if r1 == 2 and r2 in (1, 2): ufl_assert(not ri, "Not expecting repeated indices in non-scalar product.") # Check for zero, simplifying early if possible if isinstance(a, Zero) or isinstance(b, Zero): shape = s1[:-1] + s2[1:] fi = single_indices(ii) idims = mergedicts((a.index_dimensions(), b.index_dimensions())) idims = subdict(idims, fi) return Zero(shape, fi, idims) # Return dot product in index notation ai = indices(a.rank()-1) bi = indices(b.rank()-1) k = indices(1) # Create an IndexSum over a Product s = a[ai+k]*b[k+bi] return as_tensor(s, ai+bi) elif not (r1 == 0 and r2 == 0): # Scalar - tensor product if r2 == 0: a, b = b, a s1, s2 = s2, s1 # Check for zero, simplifying early if possible if isinstance(a, Zero) or isinstance(b, Zero): shape = s2 fi = single_indices(ii) idims = mergedicts((a.index_dimensions(), b.index_dimensions())) idims = subdict(idims, fi) return Zero(shape, fi, idims) # Repeated indices are allowed, like in: #v[i]*M[i,:] # Apply product to scalar components ii = indices(b.rank()) p = Product(a, b[ii]) # Wrap as tensor again p = as_tensor(p, ii) # TODO: Should we apply IndexSum or as_tensor first? # Apply index sums for i in ri: p = IndexSum(p, i) return p # Scalar products use Product and IndexSum for implicit sums: p = Product(a, b) for i in ri: p = IndexSum(p, i) return p #--- Extend Expr with algebraic operators --- _valid_types = (Expr,) + python_scalar_types def _mul(self, o): if not isinstance(o, _valid_types): return NotImplemented o = as_ufl(o) return _mult(self, o) Expr.__mul__ = _mul def _rmul(self, o): if not isinstance(o, _valid_types): return NotImplemented o = as_ufl(o) return _mult(o, self) Expr.__rmul__ = _rmul def _add(self, o): if not isinstance(o, _valid_types): return NotImplemented return Sum(self, o) Expr.__add__ = _add def _radd(self, o): if not isinstance(o, _valid_types): return NotImplemented return Sum(o, self) Expr.__radd__ = _radd def _sub(self, o): if not isinstance(o, _valid_types): return NotImplemented return Sum(self, -o) Expr.__sub__ = _sub def _rsub(self, o): if not isinstance(o, _valid_types): return NotImplemented return Sum(o, -self) Expr.__rsub__ = _rsub def _div(self, o): if not isinstance(o, _valid_types): return NotImplemented sh = self.shape() if sh: ii = indices(len(sh)) d = Division(self[ii], o) return as_tensor(d, ii) return Division(self, o) Expr.__div__ = _div Expr.__truediv__ = _div def _rdiv(self, o): if not isinstance(o, _valid_types): return NotImplemented return Division(o, self) Expr.__rdiv__ = _rdiv Expr.__rtruediv__ = _rdiv def _pow(self, o): if not isinstance(o, _valid_types): return NotImplemented if self.shape() and o == 2: return Inner(self, self) return Power(self, o) Expr.__pow__ = _pow def _rpow(self, o): if not isinstance(o, _valid_types): return NotImplemented return Power(o, self) Expr.__rpow__ = _rpow # TODO: Add Negated class for this? Might simplify reductions in Add. def _neg(self): return -1*self Expr.__neg__ = _neg def _abs(self): return Abs(self) Expr.__abs__ = _abs #--- Extend Expr with restiction operators a("+"), a("-") --- def _restrict(self, side): if side == "+": return PositiveRestricted(self) if side == "-": return NegativeRestricted(self) error("Invalid side %r in restriction operator." % side) def _eval(self, coord, mapping=None, component=()): # Evaluate expression at this particular coordinate, # with provided values for other terminals in mapping # Try to infer dimension from given x argument if coord is None: cell = self.cell() dim = None if cell is None else cell.geometric_dimension() elif isinstance(coord, (tuple, list)): dim = len(coord) else: # No type checking here, assuming a scalar x value... dim = 1 # Evaluate derivatives first from ufl.algorithms import expand_derivatives f = expand_derivatives(self, dim) # Evaluate recursively if mapping is None: mapping = {} index_values = StackDict() return f.evaluate(coord, mapping, component, index_values) def _call(self, arg, mapping=None, component=()): # Taking the restriction or evaluating depending on argument if arg in ("+", "-"): ufl_assert(mapping is None, "Not expecting a mapping when taking restriction.") return _restrict(self, arg) else: return _eval(self, arg, mapping, component) Expr.__call__ = _call #--- Extend Expr with the transpose operation A.T --- def _transpose(self): """Transposed a rank two tensor expression. For more general transpose operations of higher order tensor expressions, use indexing and Tensor.""" return Transposed(self) Expr.T = property(_transpose) #--- Extend Expr with indexing operator a[i] --- def analyse_key(ii, rank): """Takes something the user might input as an index tuple inside [], which could include complete slices (:) and ellipsis (...), and returns tuples of actual UFL index objects. The return value is a tuple (indices, axis_indices), each being a tuple of IndexBase instances. The return value 'indices' corresponds to all input objects of these types: - Index - FixedIndex - int => Wrapped in FixedIndex The return value 'axis_indices' corresponds to all input objects of these types: - Complete slice (:) => Replaced by a single new index - Ellipsis (...) => Replaced by multiple new indices """ # Wrap in tuple if not isinstance(ii, (tuple, MultiIndex)): ii = (ii,) else: # Flatten nested tuples, happens with f[...,ii] where ii is a tuple of indices jj = [] for j in ii: if isinstance(j, (tuple, MultiIndex)): jj.extend(j) else: jj.append(j) ii = tuple(jj) # Convert all indices to Index or FixedIndex objects. # If there is an ellipsis, split the indices into before and after. axis_indices = set() pre = [] post = [] indexlist = pre for i in ii: if i == Ellipsis: # Switch from pre to post list when an ellipsis is encountered ufl_assert(indexlist is pre, "Found duplicate ellipsis.") indexlist = post else: # Convert index to a proper type if isinstance(i, int): idx = FixedIndex(i) elif isinstance(i, IndexBase): idx = i elif isinstance(i, slice): if i == slice(None): idx = Index() axis_indices.add(idx) else: # TODO: Use ListTensor to support partial slices? error("Partial slices not implemented, only complete slices like [:]") else: print '\n', '='*60 print Index, id(Index) print type(i), id(type(i)) print str(i) print repr(i) print type(i).__module__ print Index.__module__ print '\n', '='*60 error("Can't convert this object to index: %r" % i) # Store index in pre or post list indexlist.append(idx) # Handle ellipsis as a number of complete slices, # that is create a number of new axis indices num_axis = rank - len(pre) - len(post) if indexlist is post: ellipsis_indices = indices(num_axis) axis_indices.update(ellipsis_indices) else: ellipsis_indices = () # Construct final tuples to return all_indices = tuple(chain(pre, ellipsis_indices, post)) axis_indices = tuple(i for i in all_indices if i in axis_indices) return all_indices, axis_indices def _getitem(self, key): # Analyse key, getting rid of slices and the ellipsis r = self.rank() indices, axis_indices = analyse_key(key, r) # Special case for foo[...] => foo if len(indices) == len(axis_indices): return self # Special case for simplifying ({ai}_i)[i] -> ai if isinstance(self, ComponentTensor): if tuple(indices) == tuple(self._indices): return self._expression # Index self, yielding scalar valued expressions a = Indexed(self, indices) # Make a tensor from components designated by axis indices if axis_indices: a = as_tensor(a, axis_indices) # TODO: Should we apply IndexSum or as_tensor first? # Apply sum for each repeated index ri = repeated_indices(self.free_indices() + indices) for i in ri: a = IndexSum(a, i) # Check for zero (last so we can get indices etc from a) if isinstance(self, Zero): shape = a.shape() fi = a.free_indices() idims = subdict(a.index_dimensions(), fi) a = Zero(shape, fi, idims) return a Expr.__getitem__ = _getitem #--- Extend Expr with spatial differentiation operator a.dx(i) --- def _dx(self, *ii): "Return the partial derivative with respect to spatial variable number i." d = self # Apply all derivatives for i in ii: d = Grad(d) # Take all components, applying repeated index sums in the [] operation return d[...,ii] Expr.dx = _dx #def _d(self, v): # "Return the partial derivative with respect to variable v." # # TODO: Maybe v can be an Indexed of a Variable, in which case we can use indexing to extract the right component? # return VariableDerivative(self, v) #Expr.d = _d ufl-1.3.0/ufl/finiteelement/000077500000000000000000000000001226300046600157165ustar00rootroot00000000000000ufl-1.3.0/ufl/finiteelement/__init__.py000066400000000000000000000025541226300046600200350ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-08-16 from ufl.finiteelement.finiteelementbase import FiniteElementBase from ufl.finiteelement.finiteelement import FiniteElement from ufl.finiteelement.mixedelement import MixedElement from ufl.finiteelement.mixedelement import VectorElement from ufl.finiteelement.mixedelement import TensorElement from ufl.finiteelement.enrichedelement import EnrichedElement from ufl.finiteelement.restrictedelement import RestrictedElement from ufl.finiteelement.tensorproductelement import TensorProductElement ufl-1.3.0/ufl/finiteelement/elementlist.py000066400000000000000000000126661226300046600206300ustar00rootroot00000000000000"""This module provides an extensive list of predefined finite element families. Users or more likely, form compilers, may register new elements by calling the function register_element.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Marie E. Rognes , 2010 # # First added: 2008-03-03 # Last changed: 2011-06-06 from ufl.assertions import ufl_assert from ufl.finiteelement.feec import FEEC_aliases # List of valid elements ufl_elements = {} # Aliases: aliases[name] (...) -> (standard_name, ...) aliases = {} # Function for registering new elements def register_element(family, short_name, value_rank, degree_range, cellnames): "Register new finite element family" ufl_assert(family not in ufl_elements, 'Finite element \"%s\" has already been registered.' % family) ufl_elements[family] = (family, short_name, value_rank, degree_range, cellnames) ufl_elements[short_name] = (family, short_name, value_rank, degree_range, cellnames) def register_alias(alias, to): aliases[alias] = to def show_elements(): print "Showing all registered elements:" for k in sorted(ufl_elements.keys()): (family, short_name, value_rank, degree_range, cellnames) = ufl_elements[k] print print "Finite Element Family: %s, %s" % (repr(family), repr(short_name)) print "Value rank: ", value_rank print "Degree range: ", degree_range print "Defined on cellnames:" , cellnames # TODO: Any more of these elements valid for generic nD cells? # Standard elements register_element("Argyris", "ARG", 0, (1, None), ("triangle", "tetrahedron")) register_element("Arnold-Winther", "AW", 0, None, ("triangle",)) register_element("Brezzi-Douglas-Fortin-Marini", "BDFM", 1, (1, None), ("triangle", "tetrahedron")) register_element("Brezzi-Douglas-Marini", "BDM", 1, (1, None), ("triangle", "tetrahedron")) register_element("Crouzeix-Raviart", "CR", 0, (1, 1), ("triangle", "tetrahedron")) register_element("Discontinuous Lagrange", "DG", 0, (0, None), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Hermite", "HER", 0, None, ("triangle", "tetrahedron")) register_element("Lagrange", "CG", 0, (1, None), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Mardal-Tai-Winther", "MTW", 0, None, ("triangle",)) register_element("Morley", "MOR", 0, None, ("triangle",)) register_element("Nedelec 1st kind H(curl)", "N1curl", 1, (1, None), ("triangle", "tetrahedron")) register_element("Nedelec 2nd kind H(curl)", "N2curl", 1, (1, None), ("triangle", "tetrahedron")) register_element("Raviart-Thomas", "RT", 1, (1, None), ("triangle", "tetrahedron")) # Special elements register_element("Boundary Quadrature", "BQ", 0, (0, None), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Bubble", "B", 0, (2, None), ("interval", "triangle", "tetrahedron")) register_element("Quadrature", "Q", 0, (0, None), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Real", "R", 0, (0, 0), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Undefined", "U", 0, (0, None), (None, "cell1D", "cell2D", "cell3D", "interval", "triangle", "tetrahedron", "quadrilateral", "hexahedron")) register_element("Lobatto", "Lob", 0, (1, None), ("interval",)) register_element("Radau", "Rad", 0, (0, None), ("interval",)) # Let Nedelec H(div) elements be aliases to BDMs/RTs register_alias("Nedelec 1st kind H(div)", lambda family, cell, order, degree: ("Raviart-Thomas", cell, order)) register_alias("N1div", lambda family, cell, order, degree: ("Raviart-Thomas", cell, order)) register_alias("Nedelec 2nd kind H(div)", lambda family, cell, order, degree: ("Brezzi-Douglas-Marini", cell, order)) register_alias("N2div", lambda family, cell, order, degree: ("Brezzi-Douglas-Marini", cell, order)) # Use P Lambda/P- Lambda aliases register_alias("P Lambda", lambda *foo: FEEC_aliases(*foo)) register_alias("P- Lambda", lambda *foo: FEEC_aliases(*foo)) ufl-1.3.0/ufl/finiteelement/enrichedelement.py000066400000000000000000000065151226300046600214320ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-08-16 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.finiteelementbase import FiniteElementBase class EnrichedElement(FiniteElementBase): """The vector sum of two finite element spaces: EnrichedElement(V, Q) = {v + q | v in V, q in Q}. """ def __init__(self, *elements): self._elements = elements domain = elements[0].domain() ufl_assert(all(e.domain() == domain for e in elements), "Domain mismatch for sub elements of enriched element.") degree = max(e.degree() for e in elements) # We can allow the scheme not to be defined, but all defined should be equal quad_schemes = [e.quadrature_scheme() for e in elements] quad_schemes = [qs for qs in quad_schemes if qs is not None] quad_scheme = quad_schemes[0] if quad_schemes else None ufl_assert(all(qs == quad_scheme for qs in quad_schemes),\ "Quadrature scheme mismatch.") value_shape = elements[0].value_shape() ufl_assert(all(e.value_shape() == value_shape for e in elements), "Element value shape mismatch.") # Initialize element data super(EnrichedElement, self).__init__("EnrichedElement", domain, degree, quad_scheme, value_shape) # Cache repr string self._repr = "EnrichedElement(%s)" % ", ".join(repr(e) for e in self._elements) def reconstruct(self, **kwargs): """Construct a new EnrichedElement object with some properties replaced with new values.""" elements = [e.reconstruct(**kwargs) for e in self._elements] if all(a == b for (a,b) in izip(elements, self._elements)): return self return EnrichedElement(*elements) def is_cellwise_constant(self): """Return whether the basis functions of this element is spatially constant over each cell.""" return all(e.is_cellwise_constant() for e in self._elements) def __str__(self): "Format as string for pretty printing." return "<%s>" % " + ".join(str(e) for e in self._elements) def shortstr(self): "Format as string for pretty printing." return "<%s>" % " + ".join(e.shortstr() for e in self._elements) ufl-1.3.0/ufl/finiteelement/feec.py000066400000000000000000000041321226300046600171720ustar00rootroot00000000000000# Copyright (C) 2010-2013 Marie E. Rognes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . from ufl.assertions import ufl_assert def FEEC_aliases(name, cell, r, k): """ FEEC_aliases(name, cell, r, k): name: "P Lambda" or "P- Lambda" cell: "interval", "triangle", "tetrahedron" r (polynomial degree): 1 <= r < ... k (form degree): 0 <= k <= n where n is the topological dimension of the cell. The families P_r Lambda^k P-_r Lambda^k map to H^1/H(curl)/H(div)/L^2 conforming finite element spaces based on the notation used in"Finite element exterior calculus, homological techniques and applications,", Arnold, Falk and Winther, Acta Numerica, 2006, Table 5.1 and 5.2 (p. 60) """ ufl_assert(cell is not None, "Cannot get dimension from undefined cell.") tdim = cell.topological_dimension() ufl_assert(k in set(range(0, tdim+1)),\ "k-forms only defined for k in [0, n]") if k == 0: family = "CG" elif k == tdim: family = "DG" if name == "P- Lambda": r = r - 1 elif k == 1: if name == "P Lambda": family = "Nedelec 2nd kind H(curl)" elif name == "P- Lambda": family = "Nedelec 1st kind H(curl)" elif k == tdim - 1: if name == "P Lambda": family = "Nedelec 2nd kind H(div)" elif name == "P- Lambda": family = "Nedelec 1st kind H(div)" return (family, cell, r) ufl-1.3.0/ufl/finiteelement/finiteelement.py000066400000000000000000000132431226300046600211230ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-08-16 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.geometry import as_cell, cellname2facetname, ProductCell from ufl.domains import as_domain from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.elementlist import ufl_elements, aliases from ufl.finiteelement.finiteelementbase import FiniteElementBase class FiniteElement(FiniteElementBase): "The basic finite element class for all simple finite elements" def __init__(self, family, domain=None, degree=None, quad_scheme=None, form_degree=None): """Create finite element *Arguments* family (string) The finite element family domain The geometric domain degree (int) The polynomial degree (optional) quad_scheme The quadrature scheme (optional) form_degree (int) The form degree (FEEC notation, used when field is viewed as k-form) """ if domain is None: cell = None else: domain = as_domain(domain) cell = domain.cell() ufl_assert(cell is not None, "Missing cell in given domain.") # Check whether this family is an alias for something else if family in aliases: (name, cell, r) = aliases[family](family, cell, degree, form_degree) #info_blue("%s, is an alias for %s " % ( # (family, cell, degree, form_degree), # (name, cell, r))) # FIXME: Need to init here with domain instead of using cell from aliases, is that ok? ufl_assert(cell == domain.cell(), "Breaking assumption in element alias mapping.") self.__init__(name, domain, r, quad_scheme) return # Check that the element family exists ufl_assert(family in ufl_elements, 'Unknown finite element "%s".' % family) # Check that element data is valid (and also get common family name) (family, self._short_name, value_rank, krange, cellnames) =\ ufl_elements[family] # Validate cellname if a valid cell is specified cellname = None if cell is None else cell.cellname() ufl_assert(cellname in cellnames, 'Cellname "%s" invalid for "%s" finite element.' % (cellname, family)) # Validate degree if specified if degree is not None: ufl_assert(krange is not None, 'Degree "%s" invalid for "%s" finite element, '\ 'should be None.' % (degree, family)) kmin, kmax = krange ufl_assert(kmin is None or degree >= kmin, 'Degree "%s" invalid for "%s" finite element.' %\ (degree, family)) ufl_assert(kmax is None or degree <= kmax, 'Degree "%s" invalid for "%s" finite element.' %\ (istr(degree), family)) # Set value dimension (default to using geometric dimension in each axis) if value_rank == 0: value_shape = () else: ufl_assert(domain is not None, "Cannot infer shape of element without a domain with geometric dimension.") dim = domain.geometric_dimension() value_shape = (dim,)*value_rank # Initialize element data super(FiniteElement, self).__init__(family, domain, degree, quad_scheme, value_shape) # Cache repr string self._repr = "FiniteElement(%r, %r, %r, %r)" % ( self.family(), self.domain(), self.degree(), self.quadrature_scheme()) def reconstruct(self, **kwargs): """Construct a new FiniteElement object with some properties replaced with new values.""" kwargs["family"] = kwargs.get("family", self.family()) kwargs["domain"] = kwargs.get("domain", self.domain()) kwargs["degree"] = kwargs.get("degree", self.degree()) kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme()) return FiniteElement(**kwargs) def __str__(self): "Format as string for pretty printing." qs = self.quadrature_scheme() qs = "" if qs is None else "(%s)" % qs return "<%s%s%s on a %s>" % (self._short_name, istr(self.degree()),\ qs, self.domain()) def shortstr(self): "Format as string for pretty printing." return "%s%s(%s)" % (self._short_name, istr(self.degree()), istr(self.quadrature_scheme())) ufl-1.3.0/ufl/finiteelement/finiteelementbase.py000066400000000000000000000163661226300046600217670ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2013-01-10 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.geometry import Cell, as_cell, cellname2facetname, ProductCell from ufl.domains import as_domain, DomainDescription from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.elementlist import ufl_elements, aliases class FiniteElementBase(object): "Base class for all finite elements" __slots__ = ("_cell", "_domain", "_family", "_degree", "_quad_scheme", "_value_shape", "_repr",) def __init__(self, family, domain, degree, quad_scheme, value_shape): "Initialize basic finite element data" ufl_assert(isinstance(family, str), "Invalid family type.") ufl_assert(isinstance(degree, int) or degree is None, "Invalid degree type.") ufl_assert(isinstance(value_shape, tuple), "Invalid value_shape type.") if domain is None: self._domain = None self._cell = None else: self._domain = as_domain(domain) self._cell = self._domain.cell() ufl_assert(isinstance(self._domain, DomainDescription), "Invalid domain type.") ufl_assert(isinstance(self._cell, Cell), "Invalid cell type.") self._family = family self._degree = degree self._value_shape = value_shape self._quad_scheme = quad_scheme def __repr__(self): "Format as string for evaluation as Python object." return self._repr def __hash__(self): "Compute hash code for insertion in hashmaps." return hash(repr(self)) def __eq__(self, other): "Compute element equality for insertion in hashmaps." return type(self) == type(other) and repr(self) == repr(other) def __lt__(self, other): "Compare elements by repr, to give a natural stable sorting." return repr(self) < repr(other) def domain(self, component=None): "Return the domain on which this element is defined." return self._domain def regions(self): "Return the regions referenced by this element and its subelements." return [self._domain] # FIXME def cell_restriction(self): "Return the cell type onto which the element is restricted." return None # Overloaded by RestrictedElement def cell(self): "Return cell of finite element" return self._cell def family(self): # FIXME: Undefined for base? "Return finite element family" return self._family def degree(self, component=None): "Return polynomial degree of finite element" # FIXME: Consider embedded_degree concept for more accurate degree, see blueprint return self._degree def quadrature_scheme(self): "Return quadrature scheme of finite element" return self._quad_scheme def is_cellwise_constant(self, component=None): """Return whether the basis functions of this element is spatially constant over each cell.""" return self.family() == "Real" or self.degree() == 0 def value_shape(self): "Return the shape of the value space" return self._value_shape def symmetry(self): # FIXME: different approach """Return the symmetry dict, which is a mapping c0 -> c1 meaning that component c0 is represented by component c1.""" return EmptyDict def unique_basic_elements(self): # FIXME: Return list of unique basic elements for all subclasses return [] def basic_element_instances(self): # FIXME: Return list of non-unique basic elements for all subclasses, # or list of indices into unique_basic_elements? return [] def _check_component(self, i): "Check that component index i is valid" sh = self.value_shape() r = len(sh) if not (len(i) == r and all(j < k for (j,k) in izip(i, sh))): error(("Illegal component index '%r' (value rank %d)" + \ "for element (value rank %d).") % (i, len(i), r)) def extract_subelement_component(self, i): """Extract direct subelement index and subelement relative component index for a given component index""" if isinstance(i, int): i = (i,) self._check_component(i) return (None, i) def extract_component(self, i): """Recursively extract component index relative to a (simple) element and that element for given value component index""" if isinstance(i, int): i = (i,) self._check_component(i) return (i, self) def num_sub_elements(self): "Return number of sub elements" return 0 def sub_elements(self): # FIXME: Replace with alternative variants "Return list of sub elements" return [] def __add__(self, other): "Add two elements, creating an enriched element" ufl_assert(isinstance(other, FiniteElementBase), "Can't add element and %s." % other.__class__) warning_blue("WARNING: Creating an EnrichedElement,\n " +\ "if you intended to create a MixedElement use '*' instead of '+'.") from ufl.finiteelement import EnrichedElement return EnrichedElement(self, other) def __mul__(self, other): "Multiply two elements, creating a mixed element" ufl_assert(isinstance(other, FiniteElementBase), "Can't multiply element and %s." % other.__class__) from ufl.finiteelement import MixedElement return MixedElement(self, other) def __getitem__(self, index): "Restrict finite element to a subdomain, subcomponent or topology (cell)." # NOTE: RestrictedElement will not be used to represent restriction # to subdomains, as that is represented by the element having # a domain property that is a Region. # NOTE: Implementing restriction to subdomains with [] should not be # done, as V[1] is ambiguously similar to both indexing expressions # and obtaining a subdomain, such as myexpr[1] and mydomain[1]. if isinstance(index, Cell) or index == "facet": from ufl.finiteelement import RestrictedElement return RestrictedElement(self, index) return NotImplemented ufl-1.3.0/ufl/finiteelement/mixedelement.py000066400000000000000000000447461226300046600207670ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-10-18 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.domains import as_domain from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.finiteelementbase import FiniteElementBase from ufl.finiteelement.finiteelement import FiniteElement class MixedElement(FiniteElementBase): "A finite element composed of a nested hierarchy of mixed or simple elements" __slots__ = ("_sub_elements",) def __init__(self, *elements, **kwargs): "Create mixed finite element from given list of elements" # Un-nest arguments if we get a single argument with a list of elements if len(elements) == 1 and isinstance(elements[0], (tuple, list)): elements = elements[0] # Interpret nested tuples as sub-mixedelements recursively elements = [MixedElement(e) if isinstance(e, (tuple,list)) else e for e in elements] self._sub_elements = elements # TODO: Figure out proper checks of domain consistency # FIXME: Do something if elements are defined on different regions domains = set(element.domain() for element in elements) - set((None,)) top_domains = set(domain.top_domain() for domain in domains) if len(top_domains) == 0: domain = None elif len(top_domains) == 1: domain = list(top_domains)[0] else: # Require that all elements are defined on the same top-level domain # TODO: is this too strict? disallows mixed elements on different meshes error("Sub elements must live in the same top level domain.") # Check that domains have same geometric dimension if domain is not None: gdim = domain.geometric_dimension() ufl_assert(all(domain.geometric_dimension() == gdim for domain in domains), "Sub elements must live in the same geometric dimension.") # Check that all elements use the same quadrature scheme # TODO: We can allow the scheme not to be defined. quad_scheme = elements[0].quadrature_scheme() ufl_assert(all(e.quadrature_scheme() == quad_scheme for e in elements),\ "Quadrature scheme mismatch for sub elements of mixed element.") # Compute value shape value_size_sum = sum(product(s.value_shape()) for s in self._sub_elements) # Default value dimension: Treated simply as all subelement # values unpacked in a vector. value_shape = kwargs.get('value_shape', (value_size_sum,)) # Validate value_shape if type(self) is MixedElement: # This is not valid for tensor elements with symmetries, # assume subclasses deal with their own validation ufl_assert(product(value_shape) == value_size_sum, "Provided value_shape doesn't match the total "\ "value size of all subelements.") # Initialize element data degree = max(e.degree() for e in self._sub_elements) super(MixedElement, self).__init__("Mixed", domain, degree, quad_scheme, value_shape) # Cache repr string self._repr = "MixedElement(*%r, **{'value_shape': %r })" %\ (self._sub_elements, self._value_shape) def reconstruct(self, **kwargs): """Construct a new MixedElement object with some properties replaced with new values.""" elements = [e.reconstruct(**kwargs) for e in self._sub_elements] # Value shape cannot be changed, or at # least we have no valid use case for it. # Reconstructing an expression with a reconstructed # coefficient with a different value shape would # be way into undefined behaviour territory... ufl_assert("value_shape" not in kwargs, "Cannot change value_shape in reconstruct.") return self.reconstruct_from_elements(*elements) def reconstruct_from_elements(self, *elements): "Reconstruct a mixed element from new subelements." if all(a == b for (a,b) in izip(elements, self._sub_elements)): return self ufl_assert(all(a.value_shape() == b.value_shape() for (a,b) in izip(elements, self._sub_elements)), "Expecting new elements to have same value shape as old ones.") return MixedElement(*elements, value_shape=self.value_shape()) def symmetry(self): """Return the symmetry dict, which is a mapping c0 -> c1 meaning that component c0 is represented by component c1. A component is a tuple of one or more ints.""" # Build symmetry map from symmetries of subelements sm = {} # Base index of the current subelement into mixed value j = 0 for e in self._sub_elements: sh = e.value_shape() # Map symmetries of subelement into index space of this element for c0, c1 in e.symmetry().iteritems(): j0 = component_to_index(c0, sh) + j j1 = component_to_index(c1, sh) + j sm[(j0,)] = (j1,) # Update base index for next element j += product(sh) ufl_assert(j == product(self.value_shape()), "Size mismatch in symmetry algorithm.") return sm or EmptyDict def num_sub_elements(self): "Return number of sub elements." return len(self._sub_elements) def sub_elements(self): "Return list of sub elements." return self._sub_elements def num_mixed_sub_elements(self): "Return number of mixed sub elements." # FIXME: Use this where intended, for disambiguation # w.r.t. different sub_elements meanings. return len(self._sub_elements) def mixed_sub_elements(self): "Return list of mixed sub elements." # FIXME: Use this where intended, for disambiguation # w.r.t. different sub_elements meanings. return self._sub_elements def extract_subelement_component(self, i): """Extract direct subelement index and subelement relative component index for a given component index""" if isinstance(i, int): i = (i,) self._check_component(i) # Select between indexing modes if len(self.value_shape()) == 1: # Indexing into a long vector of flattened subelement shapes j, = i # Find subelement for this index for k, e in enumerate(self._sub_elements): sh = e.value_shape() si = product(sh) if j < si: break j -= si ufl_assert(j >= 0, "Moved past last value component!") # Convert index into a shape tuple j = index_to_component(j, sh) else: # Indexing into a multidimensional tensor # where subelement index is first axis k = i[0] ufl_assert(k < len(self._sub_elements), "Illegal component index (dimension %d)." % k) j = i[1:] return (k, j) def extract_component(self, i): """Recursively extract component index relative to a (simple) element and that element for given value component index""" k, j = self.extract_subelement_component(i) return self._sub_elements[k].extract_component(j) def is_cellwise_constant(self, component=None): """Return whether the basis functions of this element is spatially constant over each cell.""" if component is None: return all(e.is_cellwise_constant() for e in self.sub_elements()) else: i, e = self.extract_component(component) return e.is_cellwise_constant() def domain(self, component=None): "Return the domain on which this element is defined." if component is None: return self._domain # FIXME: Remove this elif False and component is None: domains = [e.domain() for e in self.sub_elements()] domain = domains[0] for d in domains[1:]: domain = intersection(domain, d) # FIXME: Need intersection to make this work return domain else: i, e = self.extract_component(component) return e.domain() def regions(self): "Return the regions referenced by this element and its subelements." regions = set() for e in self.sub_elements(): regions.update(e.regions()) return sorted(regions) def degree(self, component=None): "Return polynomial degree of finite element" if component is None: return self._degree # from FiniteElementBase, computed as max of subelements in __init__ else: i, e = self.extract_component(component) return e.degree() def __str__(self): "Format as string for pretty printing." tmp = ", ".join(str(element) for element in self._sub_elements) return "" def shortstr(self): "Format as string for pretty printing." tmp = ", ".join(element.shortstr() for element in self._sub_elements) return "Mixed<" + tmp + ">" class VectorElement(MixedElement): "A special case of a mixed finite element where all elements are equal" def __init__(self, family, domain, degree, dim=None, quad_scheme=None, form_degree=None): """ Create vector element (repeated mixed element) *Arguments* family (string) The finite element family domain The geometric domain degree (int) The polynomial degree dim (int) The value dimension of the element (optional) quad_scheme The quadrature scheme (optional) form_degree (int) The form degree (FEEC notation, used when field is viewed as k-form) """ if domain is not None: domain = as_domain(domain) # Set default size if not specified if dim is None: ufl_assert(domain is not None, "Cannot infer vector dimension without a domain.") dim = domain.geometric_dimension() # Create mixed element from list of finite elements sub_element = FiniteElement(family, domain, degree, quad_scheme, form_degree) sub_elements = [sub_element]*dim # Get common family name (checked in FiniteElement.__init__) family = sub_element.family() # Compute value shape shape = (dim,) value_shape = shape + sub_element.value_shape() # Initialize element data super(VectorElement, self).__init__(sub_elements, value_shape=value_shape) self._family = family self._degree = degree self._sub_element = sub_element # Cache repr string self._repr = "VectorElement(%r, %r, %r, %d, %r)" % \ (self._family, self._domain, self._degree, len(self._sub_elements), quad_scheme) def reconstruct(self, **kwargs): kwargs["family"] = kwargs.get("family", self.family()) kwargs["domain"] = kwargs.get("domain", self.domain()) kwargs["degree"] = kwargs.get("degree", self.degree()) ufl_assert("dim" not in kwargs, "Cannot change dim in reconstruct.") kwargs["dim"] = len(self._sub_elements) kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme()) return VectorElement(**kwargs) def __str__(self): "Format as string for pretty printing." return "<%s vector element of degree %s on a %s: %d x %s>" % \ (self.family(), istr(self.degree()), self.domain(), len(self._sub_elements), self._sub_element) def shortstr(self): "Format as string for pretty printing." return "Vector<%d x %s>" % (len(self._sub_elements), self._sub_element.shortstr()) class TensorElement(MixedElement): "A special case of a mixed finite element where all elements are equal" __slots__ = ("_sub_element", "_shape", "_symmetry", "_sub_element_mapping",) def __init__(self, family, domain, degree, shape=None, symmetry=None, quad_scheme=None): "Create tensor element (repeated mixed element with optional symmetries)" if domain is not None: domain = as_domain(domain) # Set default shape if not specified if shape is None: ufl_assert(domain is not None, "Cannot infer vector dimension without a domain.") dim = domain.geometric_dimension() shape = (dim, dim) # Construct default symmetry for matrix elements if symmetry == True: ufl_assert(len(shape) == 2 and shape[0] == shape[1], "Cannot set automatic symmetry for non-square tensor.") symmetry = dict( ((i,j), (j,i)) for i in range(shape[0]) for j in range(shape[1]) if i > j ) # Validate indices in symmetry dict if isinstance(symmetry, dict): for i,j in symmetry.iteritems(): ufl_assert(len(i) == len(j), "Non-matching length of symmetry index tuples.") for k in range(len(i)): ufl_assert(i[k] >= 0 and j[k] >= 0 and i[k] < shape[k] and j[k] < shape[k], "Symmetry dimensions out of bounds.") else: ufl_assert(symmetry is None, "Expecting symmetry to be None, True, or dict.") # Compute all index combinations for given shape indices = compute_indices(shape) # Compute sub elements and mapping from indices # to sub elements, accounting for symmetry sub_element = FiniteElement(family, domain, degree, quad_scheme) sub_elements = [] sub_element_mapping = {} for index in indices: if symmetry and index in symmetry: continue sub_element_mapping[index] = len(sub_elements) sub_elements += [sub_element] # Update mapping for symmetry for index in indices: if symmetry and index in symmetry: sub_element_mapping[index] = sub_element_mapping[symmetry[index]] # Get common family name (checked in FiniteElement.__init__) family = sub_element.family() # Compute value shape value_shape = shape + sub_element.value_shape() # Initialize element data super(TensorElement, self).__init__(sub_elements, value_shape=value_shape) self._family = family self._degree = degree self._sub_element = sub_element self._shape = shape self._symmetry = symmetry self._sub_element_mapping = sub_element_mapping # Cache repr string self._repr = "TensorElement(%r, %r, %r, %r, %r, %r)" % \ (self._family, self._domain, self._degree, self._shape, self._symmetry, quad_scheme) def reconstruct(self, **kwargs): kwargs["family"] = kwargs.get("family", self.family()) kwargs["domain"] = kwargs.get("domain", self.domain()) kwargs["degree"] = kwargs.get("degree", self.degree()) ufl_assert("shape" not in kwargs, "Cannot change shape in reconstruct.") kwargs["shape"] = self.value_shape() # Must use same shape as self! # Not sure about symmetry, but no use case I can see ufl_assert("symmetry" not in kwargs, "Cannot change symmetry in reconstruct.") kwargs["symmetry"] = self.symmetry() kwargs["quad_scheme"] = kwargs.get("quad_scheme", self.quadrature_scheme()) return TensorElement(**kwargs) def extract_subelement_component(self, i): """Extract direct subelement index and subelement relative component index for a given component index""" if isinstance(i, int): i = (i,) self._check_component(i) i = self.symmetry().get(i, i) l = len(self._shape) ii = i[:l] jj = i[l:] ufl_assert(ii in self._sub_element_mapping, "Illegal component index %s." % repr(i)) k = self._sub_element_mapping[ii] return (k, jj) def symmetry(self): """Return the symmetry dict, which is a mapping c0 -> c1 meaning that component c0 is represented by component c1.""" return self._symmetry or EmptyDict def __str__(self): "Format as string for pretty printing." sym = "" if isinstance(self._symmetry, dict): tmp = ", ".join("%s -> %s" % (a,b) for (a,b) in self._symmetry.iteritems()) sym = " with symmetries (%s)" % tmp elif self._symmetry: sym = " with symmetry" return "<%s tensor element of degree %s and shape %s on a %s%s>" % \ (self.family(), istr(self.degree()), self.value_shape(), self.domain(), sym) def shortstr(self): "Format as string for pretty printing." sym = "" if isinstance(self._symmetry, dict): tmp = ", ".join("%s -> %s" % (a,b) for (a,b) in self._symmetry.iteritems()) sym = " with symmetries (%s)" % tmp elif self._symmetry: sym = " with symmetry" return "Tensor<%s x %s%s>" % (self.value_shape(), self._sub_element.shortstr(), sym) ufl-1.3.0/ufl/finiteelement/restrictedelement.py000066400000000000000000000107331226300046600220160ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-08-16 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.geometry import Cell, as_cell, cellname2facetname from ufl.domains import Domain from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.finiteelementbase import FiniteElementBase class RestrictedElement(FiniteElementBase): "Represents the restriction of a finite element to a type of cell entity." def __init__(self, element, cell_restriction): ufl_assert(isinstance(element, FiniteElementBase), "Expecting a finite element instance.") ufl_assert(isinstance(cell_restriction, Cell) or cell_restriction == "facet", "Expecting a Cell instance, or the string 'facet'.") super(RestrictedElement, self).__init__("RestrictedElement", element.cell(), element.degree(), element.quadrature_scheme(), element.value_shape()) self._element = element if isinstance(cell_restriction, Cell): # Just attach cell_restriction if it is a Cell self._cell_restriction = cell_restriction elif cell_restriction == "facet": # Create a cell cell = element.cell() self._cell_restriction = Cell(cellname2facetname[cell.cellname()], geometric_dimension=cell.geometric_dimension()) # Cache repr string self._repr = "RestrictedElement(%r, %r)" % (self._element, self._cell_restriction) def reconstruct(self, **kwargs): """Construct a new RestrictedElement object with some properties replaced with new values.""" element = self._element.reconstruct(**kwargs) cell_restriction = kwargs.get("cell_restriction", self.cell_restriction()) return RestrictedElement(element=element, cell_restriction=cell_restriction) def is_cellwise_constant(self): """Return whether the basis functions of this element is spatially constant over each cell.""" return self._element.is_cellwise_constant() def element(self): "Return the element which is restricted." return self._element def cell_restriction(self): "Return the domain onto which the element is restricted." return self._cell_restriction def __str__(self): "Format as string for pretty printing." return "<%s>|_{%s}" % (self._element, self._cell_restriction) def shortstr(self): "Format as string for pretty printing." return "<%s>|_{%s}" % (self._element.shortstr(), self._cell_restriction) def symmetry(self): """Return the symmetry dict, which is a mapping c0 -> c1 meaning that component c0 is represented by component c1.""" return self._element.symmetry() def num_sub_elements(self): "Return number of sub elements" return self._element.num_sub_elements() #return 1 def sub_elements(self): "Return list of sub elements" return self._element.sub_elements() #return [self._element] def num_restricted_sub_elements(self): # FIXME: Use this where intended, for disambiguation # w.r.t. different sub_elements meanings. "Return number of restricted sub elements." return 1 def restricted_sub_elements(self): # FIXME: Use this where intended, for disambiguation # w.r.t. different sub_elements meanings. "Return list of restricted sub elements." return (self._element,) ufl-1.3.0/ufl/finiteelement/tensorproductelement.py000066400000000000000000000111571226300046600225620ustar00rootroot00000000000000"This module defines the UFL finite element classes." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard # Modified by Marie E. Rognes 2010, 2012 # # First added: 2008-03-03 # Last changed: 2012-08-16 from itertools import izip from ufl.assertions import ufl_assert from ufl.permutation import compute_indices from ufl.common import product, index_to_component, component_to_index, istr, EmptyDict from ufl.geometry import as_cell, ProductCell from ufl.domains import as_domain from ufl.log import info_blue, warning, warning_blue, error from ufl.finiteelement.finiteelementbase import FiniteElementBase class TensorProductElement(FiniteElementBase): r"""The tensor product of d element spaces: .. math:: V = V_0 \otimes V_1 \otimes ... \otimes V_d Given bases {phi_i} for V_i for i = 1, ...., d, { phi_0 * phi_1 * ... * phi_d } forms a basis for V. """ __slots__ = ("_sub_elements",) def __init__(self, *elements): "Create TensorProductElement from a given list of elements." self._sub_elements = list(elements) ufl_assert(len(self._sub_elements) > 0, "Cannot create TensorProductElement from empty list.") self._repr = "TensorProductElement(*%r)" % self._sub_elements family = "TensorProductElement" # Define cell as the product of each subcell cell = ProductCell(*[e.cell() for e in self._sub_elements]) domain = as_domain(cell) # FIXME: figure out what this is supposed to mean :) # Define polynomial degree as the maximal of each subelement degree = max(e.degree() for e in self._sub_elements) # No quadrature scheme defined quad_scheme = None # For now, check that all subelements have the same value # shape, and use this. # TODO: Not sure if this makes sense, what kind of product is used to build the basis? value_shape = self._sub_elements[0].value_shape() ufl_assert(all(e.value_shape() == value_shape for e in self._sub_elements), "All subelements in must have same value shape") super(TensorProductElement, self).__init__(family, domain, degree, quad_scheme, value_shape) def num_sub_elements(self): "Return number of subelements." return len(self._sub_elements) def sub_elements(self): "Return subelements (factors)." # TODO: I don't think the concept of sub elements is quite well defined across # all the current element types. Need to investigate how sub_elements is used in # existing code, and eventually redesign here, hopefully just adding another function. # Summary of different behaviours: # - In MixedElement and subclasses each subelement corresponds to different value components. # - In EnrichedElement sub_elements returns [] even though it contains multiple "children". # - In RestrictedElement it returns the sub elements of its single children. # - Here in TensorProductElement it returns sub elements that correspond to factors, not components. return self._sub_elements def num_tensorproduct_sub_elements(self): # FIXME: Use this where intended, for disambiguation w.r.t. different sub_elements meanings. "Return number of tensorproduct sub elements." return len(self._sub_elements) def tensorproduct_sub_elements(self): # FIXME: Use this where intended, for disambiguation w.r.t. different sub_elements meanings. "Return list of tensorproduct sub elements." return self._sub_elements def __str__(self): "Pretty-print." return "TensorProductElement(%s)" \ % str([str(e) for e in self.sub_elements()]) def shortstr(self): "Short pretty-print." return "TensorProductElement(%s)" \ % str([e.shortstr() for e in self.sub_elements()]) def __repr__(self): return self._repr ufl-1.3.0/ufl/form.py000066400000000000000000000210431226300046600144030ustar00rootroot00000000000000"The Form class." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2011. # # First added: 2008-03-14 # Last changed: 2013-01-02 import hashlib from itertools import chain from ufl.log import error from ufl.assertions import ufl_assert from ufl.integral import Integral, Measure, is_scalar_constant_expression from ufl.equation import Equation from ufl.expr import Expr from ufl.constantvalue import Zero # --- The Form class, representing a complete variational form or functional --- def integral_sequence_to_dict(integrals): "Map a sequence of Integral objects to a dictionary of lists of Integrals keyed by domain type." d = {} for itg in integrals: k = itg.domain_type() if k not in d: d[k] = [itg] else: d[k].append(itg) return d def integral_dict_to_sequence(integrals): "Map a dictionary of lists of Integrals keyed by domain type into a sequence of Integral objects ." return tuple(itg for dt in Measure._domain_types_tuple for itg in integrals.get(dt, ())) def join_dintegrals(aintegrals, bintegrals): # Store integrals from two forms in a canonical sorting return integral_sequence_to_dict(chain(integral_dict_to_sequence(aintegrals), integral_dict_to_sequence(bintegrals))) class Form(object): """Description of a weak form consisting of a sum of integrals over subdomains.""" __slots__ = ("_dintegrals", # Dict of one integral list per domain type "_hash", # Hash code for use in dicts, including incidental numbering of indices etc. "_signature", # Signature for use with jit cache, independent of incidental numbering of indices etc. "_form_data", # Cache of preprocess result applied to this form "_is_preprocessed", # Set to true if this form is the result of a preprocess of another form #"_domain_data", # TODO: Make domain data a property of the form instead of the integral? ) def __init__(self, integrals): self._dintegrals = integral_sequence_to_dict(integrals) ufl_assert(all(isinstance(itg, Integral) for itg in integrals), "Expecting list of integrals.") self._signature = None self._hash = None self._form_data = None self._is_preprocessed = False def cell(self): # TODO: DEPRECATE #from ufl.log import deprecate #deprecate("Form.cell is not well defined and will be removed.") for itg in self.integrals(): cell = itg.integrand().cell() if cell is not None: return cell return None def integral_groups(self): """Return a dict, which is a mapping from domain types to integrals. In particular, the keys are domain_type strings, and the values are lists of Integral instances. The Integrals in each list share the same domain type (the key), but have potentially different domain ids and metadata.""" return self._dintegrals def integrals(self, domain_type=None): if domain_type is None: return integral_dict_to_sequence(self.integral_groups()) else: return self.integral_groups().get(domain_type,[]) def is_preprocessed(self): "Return true if this form is the result of a preprocessing of another form." return self._is_preprocessed def form_data(self): "Return form metadata (None if form has not been preprocessed)" return self._form_data def compute_form_data(self, object_names=None, common_cell=None, element_mapping=None): "Compute and return form metadata" # TODO: We should get rid of the form data caching, but need to # figure out how to do that and keep pydolfin working properly # Only compute form data once, and never on an already processed form ufl_assert(not self.is_preprocessed(), "You can not preprocess forms twice.") if self._form_data is None: from ufl.algorithms.preprocess import preprocess self._form_data = preprocess(self, object_names=object_names, common_cell=common_cell, element_mapping=element_mapping) # Always validate arguments, keeping sure that the validation works self._form_data.validate(object_names=object_names, common_cell=common_cell, element_mapping=element_mapping) return self.form_data() def __eq__(self, other): return Equation(self, other) def __radd__(self, other): # Ordering of form additions make no difference return self.__add__(other) def __add__(self, other): if isinstance(other, Form): # Add integrands of integrals with the same measure return Form(integral_dict_to_sequence(join_dintegrals(self.integral_groups(), other.integral_groups()))) elif isinstance(other, (int,float)) and other == 0: # Allow adding 0 or 0.0 as a no-op, needed for sum([a,b]) return self elif isinstance(other, Zero) and not (other.shape() or other.free_indices()): # Allow adding ufl Zero as a no-op, needed for sum([a,b]) return self else: # Let python protocols do their job if we don't handle it return NotImplemented def __sub__(self, other): "Subtract other form from this one." return self + (-other) def __neg__(self): """Negate all integrals in form. This enables the handy "-form" syntax for e.g. the linearized system (J, -F) from a nonlinear form F.""" return Form([-itg for itg in self.integrals()]) def __rmul__(self, scalar): "Multiply all integrals in form with constant scalar value." # This enables the handy "0*form" or "dt*form" syntax if is_scalar_constant_expression(scalar): return Form([scalar*itg for itg in self.integrals()]) return NotImplemented def __mul__(self, coefficient): "UFL form operator: Take the action of this form on the given coefficient." if isinstance(coefficient, Expr): #Coefficient): # TODO: Check whatever makes sense from ufl.formoperators import action return action(self, coefficient) return NotImplemented def __str__(self): # TODO: Add warning here to check if anyone actually calls it in libraries s = "\n + ".join(str(itg) for itg in self.integrals()) return s or "" def __repr__(self): # TODO: Add warning here to check if anyone actually calls it in libraries # Not caching this because it can be huge r = "Form([%s])" % ", ".join(repr(itg) for itg in self.integrals()) return r def __hash__(self): if self._hash is None: hashdata = tuple(hash(itg) for itg in self.integrals()) self._hash = hash(hashdata) return self._hash def deprecated_signature(self, function_replace_map=None): if self._signature is None: from ufl.algorithms.signature import compute_form_signature self._signature = compute_form_signature(self, function_replace_map) assert self._signature return self._signature def x_repr_latex_(self): # TODO: This works, but enable when form latex rendering is fixed from ufl.algorithms import ufl2latex return "$$%s$$" % ufl2latex(self) def x_repr_png_(self): # TODO: This works, but enable when form latex rendering is fixed from IPython.lib.latextools import latex_to_png return latex_to_png(self._repr_latex_()) ufl-1.3.0/ufl/formoperators.py000066400000000000000000000240451226300046600163470ustar00rootroot00000000000000"Various high level ways to transform a complete Form into a new Form." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009 # # First added: 2008-03-14 # Last changed: 2013-01-02 from itertools import izip from ufl.log import error from ufl.assertions import ufl_assert from ufl.form import Form from ufl.expr import Expr from ufl.split_functions import split from ufl.operatorbase import Tuple from ufl.variable import Variable from ufl.finiteelement import MixedElement from ufl.argument import Argument from ufl.coefficient import Coefficient from ufl.differentiation import CoefficientDerivative from ufl.constantvalue import is_true_ufl_scalar from ufl.indexed import Indexed from ufl.indexing import FixedIndex, MultiIndex from ufl.tensors import as_tensor # An exception to the rule that ufl.* does not depend on ufl.algorithms.* ... from ufl.algorithms import compute_form_adjoint, \ compute_form_action, \ compute_energy_norm, \ compute_form_lhs, \ compute_form_rhs, \ compute_form_functional, \ expand_derivatives, \ as_form # Part of the external interface from ufl.algorithms import replace def lhs(form): """UFL form operator: Given a combined bilinear and linear form, extract the left hand side (bilinear form part). Example: a = u*v*dx + f*v*dx a = lhs(a) -> u*v*dx """ form = as_form(form) form = expand_derivatives(form) return compute_form_lhs(form) def rhs(form): """UFL form operator: Given a combined bilinear and linear form, extract the right hand side (negated linear form part). Example: a = u*v*dx + f*v*dx L = rhs(a) -> -f*v*dx """ form = as_form(form) form = expand_derivatives(form) return compute_form_rhs(form) def system(form): "UFL form operator: Split a form into the left hand side and right hand side, see lhs and rhs." return lhs(form), rhs(form) def functional(form): # TODO: Does this make sense for anything other than testing? "UFL form operator: Extract the functional part of form." form = as_form(form) form = expand_derivatives(form) return compute_form_functional(form) def action(form, coefficient=None): """UFL form operator: Given a bilinear form, return a linear form with an additional coefficient, representing the action of the form on the coefficient. This can be used for matrix-free methods.""" form = as_form(form) form = expand_derivatives(form) return compute_form_action(form, coefficient) def energy_norm(form, coefficient=None): """UFL form operator: Given a bilinear form, return a linear form with an additional coefficient, representing the action of the form on the coefficient. This can be used for matrix-free methods.""" form = as_form(form) form = expand_derivatives(form) return compute_energy_norm(form, coefficient) def adjoint(form, reordered_arguments=None): """UFL form operator: Given a combined bilinear form, compute the adjoint form by changing the ordering (count) of the test and trial functions. By default, new Argument objects will be created with opposite ordering. However, if the adjoint form is to be added to other forms later, their arguments must match. In that case, the user must provide a tuple reordered_arguments=(u2,v2). """ form = as_form(form) form = expand_derivatives(form) return compute_form_adjoint(form, reordered_arguments) def zero_lists(shape): ufl_assert(len(shape) > 0, "Invalid shape.") if len(shape) == 1: return [0]*shape[0] else: return [zero_lists(shape[1:]) for i in range(shape[0])] def set_list_item(li, i, v): # Get to the innermost list if len(i) > 1: for j in i[:-1]: li = li[j] # Set item in innermost list li[i[-1]] = v def _handle_derivative_arguments(coefficient, argument): # Wrap single coefficient in tuple for uniform treatment below if isinstance(coefficient, (list,tuple)): coefficients = tuple(coefficient) else: coefficients = (coefficient,) if argument is None: # Try to create argument if not provided if not all(isinstance(c, Coefficient) for c in coefficients): error("Can only create arguments automatically for non-indexed coefficients.") elements = [c.element() for c in coefficients] if len(elements) > 1: elm = MixedElement(*elements) arguments = split(Argument(elm)) else: elm, = elements arguments = (Argument(elm),) else: # Wrap single argument in tuple for uniform treatment below if isinstance(argument, (list,tuple)): arguments = tuple(argument) else: n = len(coefficients) if n == 1: arguments = (argument,) else: if argument.shape() == (n,): arguments = tuple(argument[i] for i in range(n)) else: arguments = split(argument) # Build mapping from coefficient to argument m = {} for (c, a) in izip(coefficients, arguments): ufl_assert(c.shape() == a.shape(), "Coefficient and argument shapes do not match!") if isinstance(c, Coefficient): m[c] = a else: ufl_assert(isinstance(c, Indexed), "Invalid coefficient type for %s" % repr(c)) f, i = c.operands() ufl_assert(isinstance(f, Coefficient), "Expecting an indexed coefficient, not %s" % repr(f)) ufl_assert(isinstance(i, MultiIndex) and all(isinstance(j, FixedIndex) for j in i), "Expecting one or more fixed indices, not %s" % repr(i)) i = tuple(int(j) for j in i) if f not in m: m[f] = {} m[f][i] = a # Merge coefficient derivatives (arguments) based on indices for c, p in m.iteritems(): if isinstance(p, dict): a = zero_lists(c.shape()) for i, g in p.iteritems(): set_list_item(a, i, g) m[c] = as_tensor(a) # Wrap and return generic tuples items = sorted(m.items(), key=lambda x: x[0].count()) coefficients = Tuple(*[item[0] for item in items]) arguments = Tuple(*[item[1] for item in items]) return coefficients, arguments def derivative(form, coefficient, argument=None, coefficient_derivatives=None): """UFL form operator: Given any form, compute the linearization of the form with respect to the given Coefficient. The resulting form has one additional Argument in the same finite element space as the coefficient. A tuple of Coefficients may be provided in place of a single Coefficient, in which case the new Argument argument is based on a MixedElement created from this tuple.""" coefficients, arguments = _handle_derivative_arguments(coefficient, argument) coefficient_derivatives = coefficient_derivatives or {} # Got a form? Apply derivatives to the integrands in turn. if isinstance(form, Form): integrals = [] for itg in form.integrals(): fd = CoefficientDerivative(itg.integrand(), coefficients, arguments, coefficient_derivatives) integrals.append(itg.reconstruct(fd)) return Form(integrals) elif isinstance(form, Expr): # What we got was in fact an integrand return CoefficientDerivative(form, coefficients, arguments, coefficient_derivatives) error("Invalid argument type %s." % str(type(form))) def sensitivity_rhs(a, u, L, v): """UFL form operator: Compute the right hand side for a sensitivity calculation system. The derivation behind this computation is as follows. Assume a, L to be bilinear and linear forms corresponding to the assembled linear system Ax = b. Where x is the vector of the discrete function corresponding to u. Let v be some scalar variable this equation depends on. Then we can write 0 = d/dv[Ax-b] = dA/dv x + A dx/dv - db/dv, A dx/dv = db/dv - dA/dv x, and solve this system for dx/dv, using the same bilinear form a and matrix A from the original system. Assume the forms are written v = variable(v_expression) L = IL(v)*dx a = Ia(v)*dx where IL and Ia are integrand expressions. Define a Coefficient u representing the solution to the equations. Then we can compute db/dv and dA/dv from the forms da = diff(a, v) dL = diff(L, v) and the action of da on u by dau = action(da, u) In total, we can build the right hand side of the system to compute du/dv with the single line dL = diff(L, v) - action(diff(a, v), u) or, using this function dL = sensitivity_rhs(a, u, L, v) """ msg = "Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable)." ufl_assert(isinstance(a, Form), msg) ufl_assert(isinstance(u, Coefficient), msg) ufl_assert(isinstance(L, Form), msg) ufl_assert(isinstance(v, Variable), msg) ufl_assert(is_true_ufl_scalar(v), "Expecting scalar variable.") from ufl.operators import diff return diff(L, v) - action(diff(a, v), u) ufl-1.3.0/ufl/geometry.py000066400000000000000000000446001226300046600152770ustar00rootroot00000000000000"Types for quantities computed from cell geometry." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009. # Modified by Kristian B. Oelgaard, 2009 # Modified by Marie E. Rognes 2012 # # First added: 2008-03-14 # Last changed: 2013-04-29 from ufl.log import warning, error from ufl.assertions import ufl_assert from ufl.common import istr from ufl.terminal import Terminal # --- Expression node types # Mapping from cell name to dimension cellname2dim = {"cell1D": 1, "cell2D": 2, "cell3D": 3, "vertex": 0, "interval": 1, "triangle": 2, "tetrahedron": 3, "quadrilateral": 2, "hexahedron": 3} # Mapping from cell name to facet name cellname2facetname = {"cell1D": "vertex", "cell2D": "cell1D", "cell3D": "cell2D", "interval": "vertex", "triangle": "interval", "tetrahedron": "triangle", "quadrilateral": "interval", "hexahedron": "quadrilateral"} # Valid UFL cellnames ufl_cellnames = tuple(sorted(cellname2dim.keys())) # FIXME DOMAIN: Figure out which quantities to make available from Domain. # Need deprecation warnings for a while from the cell. class GeometricQuantity(Terminal): __slots__ = ("_cell",) def __init__(self, cell): Terminal.__init__(self) self._cell = as_cell(cell) def cell(self): return self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return True # NB! Assuming all geometric quantities in here are are cellwise constant by default! def __eq__(self, other): return isinstance(other, self._uflclass) and other._cell == self._cell class SpatialCoordinate(GeometricQuantity): "Representation of a spatial coordinate." __slots__ = ("_repr",) def __init__(self, cell): GeometricQuantity.__init__(self, cell) self._repr = "SpatialCoordinate(%r)" % self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return False def shape(self): return (self._cell.geometric_dimension(),) def evaluate(self, x, mapping, component, index_values): if component == (): if isinstance(x, (tuple,list)): return float(x[0]) else: return float(x) else: return float(x[component[0]]) def __str__(self): return "x" def __repr__(self): return self._repr class LocalCoordinate(GeometricQuantity): "(EXPERIMENTAL) Representation of a local coordinate on the reference cell." __slots__ = ("_repr",) def __init__(self, cell): GeometricQuantity.__init__(self, cell) self._repr = "LocalCoordinate(%r)" % self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return False def shape(self): return (self._cell.geometric_dimension(),) def evaluate(self, x, mapping, component, index_values): ufl_error("Symbolic evaluation of local coordinate not available.") def __str__(self): return "xi" def __repr__(self): return self._repr class GeometryJacobi(GeometricQuantity): "(EXPERIMENTAL) Representation of the Jacobi of the mapping from local to global coordinates." __slots__ = ("_repr",) def __init__(self, cell): GeometricQuantity.__init__(self, cell) self._repr = "GeometryJacobi(%r)" % self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return True # False # FIXME: True for affine mappings, not for other mappings when we add support for them def shape(self): return (self._cell.geometric_dimension(), self._cell.topological_dimension()) def evaluate(self, x, mapping, component, index_values): ufl_error("Symbolic evaluation of geometry jacobi not available.") def __str__(self): return "J" def __repr__(self): return self._repr class GeometryJacobiDeterminant(GeometricQuantity): "(EXPERIMENTAL) Representation of the determinant of the Jacobi of the mapping from local to global coordinates." __slots__ = ("_repr",) def __init__(self, cell): GeometricQuantity.__init__(self, cell) self._repr = "GeometryJacobiDeterminant(%r)" % self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return True # False # FIXME: True for affine mappings, not for other mappings when we add support for them def shape(self): return () def evaluate(self, x, mapping, component, index_values): ufl_error("Symbolic evaluation of geometry jacobi determinant not available.") def __str__(self): return "detJ" def __repr__(self): return self._repr class InverseGeometryJacobi(GeometricQuantity): "(EXPERIMENTAL) Representation of the (pseudo-)inverse of the Jacobi of the mapping from local to global coordinates." __slots__ = ("_repr",) def __init__(self, cell): GeometricQuantity.__init__(self, cell) self._repr = "InverseGeometryJacobi(%r)" % self._cell def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return True # False # FIXME: True for affine mappings, not for other mappings when we add support for them def shape(self): return (self._cell.topological_dimension(), self._cell.geometric_dimension()) def evaluate(self, x, mapping, component, index_values): ufl_error("Symbolic evaluation of inverse geometry jacobi not available.") def __str__(self): return "K" def __repr__(self): return self._repr class FacetNormal(GeometricQuantity): "Representation of a facet normal." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return (self._cell.geometric_dimension(),) def __str__(self): return "n" def __repr__(self): return "FacetNormal(%r)" % self._cell class CellVolume(GeometricQuantity): "Representation of a cell volume." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "volume" def __repr__(self): return "CellVolume(%r)" % self._cell class Circumradius(GeometricQuantity): "Representation of the circumradius of a cell." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "circumradius" def __repr__(self): return "Circumradius(%r)" % self._cell class CellSurfaceArea(GeometricQuantity): "Representation of the total surface area of a cell." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "surfacearea" def __repr__(self): return "CellSurfaceArea(%r)" % self._cell class FacetArea(GeometricQuantity): "Representation of the area of a cell facet." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "facetarea" def __repr__(self): return "FacetArea(%r)" % self._cell class FacetDiameter(GeometricQuantity): """(EXPERIMENTAL) Representation of the diameter of a facet. This is not yet defined. """ __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "facetdiameter" def __repr__(self): return "FacetDiameter(%r)" % self._cell class MinFacetEdgeLength(GeometricQuantity): "Representation of the minimum edge length of a facet." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "minfacetedgelength" def __repr__(self): return "MinFacetEdgeLength(%r)" % self._cell class MaxFacetEdgeLength(GeometricQuantity): "Representation of the maximum edge length of a facet." __slots__ = () def __init__(self, cell): GeometricQuantity.__init__(self, cell) def shape(self): return () def __str__(self): return "maxfacetedgelength" def __repr__(self): return "MaxFacetEdgeLength(%r)" % self._cell # TODO: If we include this here, we must define exactly what is meant by the mesh size, possibly adding multiple kinds of mesh sizes (hmin, hmax, havg, ?) #class MeshSize(GeometricQuantity): # __slots__ = () # def __init__(self, cell): # GeometricQuantity.__init__(self, cell) # # def shape(self): # return () # # def __str__(self): # return "h" # # def __repr__(self): # return "MeshSize(%r)" % self._cell # --- Basic cell representation classes class Cell(object): "Representation of a finite element cell." __slots__ = (# Strings "_cellname", "_repr", # Dimensions "_geometric_dimension", "_topological_dimension", # Global geometric quantities "_x", "_n", # Cell and facet sizes "_volume", "_circumradius", "_cellsurfacearea", "_facetarea", "_minfacetedgelength", "_maxfacetedgelength", "_facetdiameter", # Cell local coordinates and mapping "_xi", "_J", "_Jinv", "_detJ", ) def __init__(self, cellname, geometric_dimension=None): "Initialize basic cell description." # The cellname must be one of the predefined names ufl_assert(cellname in cellname2dim, "Invalid cellname %s." % (cellname,)) self._cellname = cellname # The topological dimension is defined by the cell type self._topological_dimension = cellname2dim[self._cellname] # The geometric dimension defaults to equal # the topological dimension if undefined ufl_assert(geometric_dimension is None or isinstance(geometric_dimension, int), "Expecting an integer dimension, not '%r'" % (geometric_dimension,)) self._geometric_dimension = geometric_dimension or self._topological_dimension # Check for consistency in dimensions. # NB! Note that the distinction between topological # and geometric dimensions has yet to be used in # practice, so don't trust it too much :) ufl_assert(self._topological_dimension <= self._geometric_dimension, "Cannot embed a %sD cell in %sD" %\ (istr(self._topological_dimension), istr(self._geometric_dimension))) # Cache repr string self._repr = "Cell(%r, %r)" % (self._cellname, self._geometric_dimension) # Attach expression nodes derived from this cell TODO: Derive these from domain instead self._n = FacetNormal(self) self._x = SpatialCoordinate(self) self._xi = LocalCoordinate(self) self._J = GeometryJacobi(self) self._Jinv = InverseGeometryJacobi(self) self._detJ = GeometryJacobiDeterminant(self) self._volume = CellVolume(self) self._circumradius = Circumradius(self) self._cellsurfacearea = CellSurfaceArea(self) self._facetarea = FacetArea(self) self._minfacetedgelength = MinFacetEdgeLength(self) self._maxfacetedgelength = MaxFacetEdgeLength(self) self._facetdiameter = FacetDiameter(self) #self._h = MeshSize(self) #self._hmin = MeshSizeMin(self) #self._hmax = MeshSizeMax(self) @property def x(self): "UFL geometry value: The global spatial coordinates." return self._x @property def xi(self): "UFL geometry value: The local spatial coordinates." return self._xi @property def J(self): "UFL geometry value: The Jacobi of the local to global coordinate mapping." return self._J @property def detJ(self): "UFL geometry value: The determinant of the Jacobi of the local to global coordinate mapping." return self._detJ @property def Jinv(self): "UFL geometry value: The inverse of the Jacobi of the local to global coordinate mapping." return self._Jinv @property def n(self): "UFL geometry value: The facet normal on the cell boundary." return self._n @property def volume(self): "UFL geometry value: The volume of the cell." return self._volume @property def circumradius(self): "UFL geometry value: The circumradius of the cell." return self._circumradius @property def facet_diameter(self): "UFL geometry value: The diameter of a facet of the cell." return self._facetdiameter @property def min_facet_edge_length(self): "UFL geometry value: The minimum edge length of a facet of the cell." return self._minfacetedgelength @property def max_facet_edge_length(self): "UFL geometry value: The maximum edge length of a facet of the cell." return self._maxfacetedgelength @property def facet_area(self): "UFL geometry value: The area of a facet of the cell." return self._facetarea @property def surface_area(self): "UFL geometry value: The total surface area of the cell." return self._cellsurfacearea def is_undefined(self): """Return whether this cell is undefined, in which case no dimensions are available.""" warning("cell.is_undefined() is deprecated, undefined cells are no longer allowed.") return False def domain(self): warning("Cell.domain() is deprecated, use cell.cellname() instead.") return self.cellname() def cellname(self): "Return the cellname of the cell." return self._cellname def facet_cellname(self): "Return the cellname of the facet of this cell." return cellname2facetname[self._cellname] def geometric_dimension(self): "Return the dimension of the space this cell is embedded in." return self._geometric_dimension def topological_dimension(self): "Return the dimension of the topology of this cell." return self._topological_dimension @property def d(self): """The dimension of the cell. Only valid if the geometric and topological dimensions are the same.""" ufl_assert(self._topological_dimension == self._geometric_dimension, "Cell.d is undefined when geometric and"+\ "topological dimensions are not the same.") return self._geometric_dimension def __eq__(self, other): return isinstance(other, Cell) and repr(self) == repr(other) def __ne__(self, other): return not self == other def __lt__(self, other): return repr(self) < repr(other) def __hash__(self): return hash(repr(self)) def __str__(self): return "<%s cell in %sD>" % (istr(self._cellname), istr(self._geometric_dimension)) def __repr__(self): return self._repr def _repr_svg_(self): n = self.cellname() svg = '\n\n' if n == "interval": svg = svg % '0,0, 200,0' elif n == "triangle": svg = svg % '0,200 200,200 0,0 0,200' elif n == "quadrilateral": svg = svg % '0,200 200,200 200,0 0,0 0,200' else: svg = None return svg class ProductCell(Cell): """Representation of a cell formed by Cartesian products of other cells.""" __slots__ = ("_cells",) def __init__(self, *cells): "Create a ProductCell from a given list of cells." self._cells = cells ufl_assert(len(self._cells) > 0, "Expecting at least one cell") self._cellname = self._cells[0].cellname()#" x ".join([c.cellname() for c in cells]) self._topological_dimension = sum(c.topological_dimension() for c in cells) self._geometric_dimension = sum(c.geometric_dimension() for c in cells) self._repr = "ProductCell(*%r)" % list(self._cells) self._n = None self._x = SpatialCoordinate(self) # For now self._xi = None # ? self._J = None # ? self._Jinv = None # ? self._detJ = None # ? self._volume = None self._circumradius = None self._cellsurfacearea = None self._facetarea = None # Not defined self._facetdiameter = None # Not defined def sub_cells(self): "Return list of cell factors." return self._cells # --- Utility conversion functions def as_cell(cell): """Convert any valid object to a Cell (in particular, cellname string), or return cell if it is already a Cell.""" if isinstance(cell, Cell): return cell elif isinstance(cell, str): # Create cell from string return Cell(cell) else: error("Invalid cell %s." % cell) ufl-1.3.0/ufl/indexed.py000066400000000000000000000065771226300046600150770ustar00rootroot00000000000000"""This module defines the Indexed class.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-01-28 # Last changed: 2011-06-17 from itertools import izip from ufl.log import error from ufl.expr import Expr from ufl.operatorbase import WrapperType from ufl.indexing import Index, FixedIndex, as_multi_index from ufl.indexutils import unique_indices from ufl.precedence import parstr from ufl.common import EmptyDict #--- Indexed expression --- class Indexed(WrapperType): __slots__ = ("_expression", "_indices", "_free_indices", "_index_dimensions",) def __init__(self, expression, indices): WrapperType.__init__(self) if not isinstance(expression, Expr): error("Expecting Expr instance, not %s." % repr(expression)) self._expression = expression shape = expression.shape() if len(shape) != len(indices): error("Invalid number of indices (%d) for tensor "\ "expression of rank %d:\n\t%r\n"\ % (len(indices), expression.rank(), expression)) self._indices = as_multi_index(indices, shape) for si, di in izip(shape, self._indices): if isinstance(di, FixedIndex) and int(di) >= int(si): error("Fixed index out of range!") idims = dict((i, s) for (i, s) in izip(self._indices._indices, shape) if isinstance(i, Index)) idims.update(expression.index_dimensions()) fi = unique_indices(expression.free_indices() + self._indices._indices) self._free_indices = fi self._index_dimensions = idims or EmptyDict def operands(self): return (self._expression, self._indices) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return () def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return self._expression.is_cellwise_constant() def evaluate(self, x, mapping, component, index_values, derivatives=()): A, ii = self.operands() component = ii.evaluate(x, mapping, None, index_values) if derivatives: return A.evaluate(x, mapping, component, index_values, derivatives) else: return A.evaluate(x, mapping, component, index_values) def __str__(self): return "%s[%s]" % (parstr(self._expression, self), self._indices) def __repr__(self): return "Indexed(%r, %r)" % (self._expression, self._indices) def __getitem__(self, key): error("Attempting to index with %r, but object is already indexed: %r" % (key, self)) def __getnewargs__(self): return () ufl-1.3.0/ufl/indexing.py000066400000000000000000000170331226300046600152510ustar00rootroot00000000000000"""This module defines the single index types and some internal index utilities.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-14 # Last changed: 2011-06-22 from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.common import counted_init, EmptyDict from ufl.terminal import UtilityType from itertools import izip #--- Index types --- class IndexBase(object): __slots__ = () def __init__(self): pass class Index(IndexBase): """UFL value: An index with no value assigned. Used to represent free indices in Einstein indexing notation.""" __slots__ = ("_count",) _globalcount = 0 def __init__(self, count=None): IndexBase.__init__(self) counted_init(self, count, Index) def count(self): return self._count def __eq__(self, other): return isinstance(other, Index) and (self._count == other._count) def __str__(self): c = str(self._count) if len(c) > 1: c = "{%s}" % c return "i_%s" % c def __repr__(self): return "Index(%d)" % self._count def __hash__(self): return hash(repr(self)) class FixedIndex(IndexBase): """UFL value: An index with a specific value assigned.""" __slots__ = ("_value", "_hash") _cache = {} def __new__(cls, value): self = FixedIndex._cache.get(value) if self is None: if not isinstance(value, int): error("Expecting integer value for fixed index.") self = IndexBase.__new__(cls) FixedIndex._cache[value] = self return self def __getnewargs__(self): return (self._value,) def __init__(self, value): if not hasattr(self, "_value"): IndexBase.__init__(self) self._value = value self._hash = hash(repr(self)) def __eq__(self, other): if isinstance(other, FixedIndex): return self._value == other._value elif isinstance(other, int): # Allow scalar comparison return self._value == other return False def __int__(self): return self._value def __str__(self): return "%d" % self._value def __repr__(self): return "FixedIndex(%d)" % self._value def __hash__(self): return self._hash class MultiIndex(UtilityType): "Represents a sequence of indices, either fixed or free." __slots__ = ("_indices", "_idims",) _cache = {} def __new__(cls, ii, idims): # Convert ii to proper type and check if input is cache-able if isinstance(ii, FixedIndex): key = (ii._value,) ii = (ii,) elif isinstance(ii, Index): key = None # Not cachable ii = (ii,) elif isinstance(ii, int): key = (ii,) ii = (FixedIndex(ii),) elif isinstance(ii, tuple): ii = tuple(as_index(j) for j in ii) if all(isinstance(jj, FixedIndex) for jj in ii): key = tuple(jj._value for jj in ii) else: key = None else: error("Expecting tuple of UFL indices, not %s." % (ii,)) if key is not None: # Lookup in cache if we have a key self = MultiIndex._cache.get(key) if self is not None: return self self = UtilityType.__new__(cls) MultiIndex._cache[key] = self else: # Or skip cache for other cases self = UtilityType.__new__(cls) # Initialize here to avoid repeating the checks on ii from above in __init__ self._init(ii, idims) return self def __getnewargs__(self): return (self._indices, self._idims) def __init__(self, ii, idims): pass def _init(self, ii, idims): UtilityType.__init__(self) self._indices = ii self._idims = dict(idims) if idims else EmptyDict if any(not isinstance(idims.get(k,0), int) for k in ii if isinstance(k, Index)): error("Missing index or invalid dimension in provided idims.") def evaluate(self, x, mapping, component, index_values): # Build component from index values component = [] for i in self._indices: if isinstance(i, FixedIndex): component.append(i._value) elif isinstance(i, Index): component.append(index_values[i]) return tuple(component) def free_indices(self): return tuple(i for i in set(self._indices) if isinstance(i, Index)) def index_dimensions(self): return self._idims def __add__(self, other): sid = self.index_dimensions() oid = other.index_dimensions() if sid or oid: idims = dict(sid) idims.update(oid) else: idims = EmptyDict if isinstance(other, tuple): return MultiIndex(self._indices + other, idims) elif isinstance(other, MultiIndex): return MultiIndex(self._indices + other._indices, idims) return NotImplemented def __radd__(self, other): sid = self.index_dimensions() oid = other.index_dimensions() if sid or oid: idims = dict(sid) idims.update(oid) else: idims = EmptyDict if isinstance(other, tuple): return MultiIndex(other + self._indices, idims) elif isinstance(other, MultiIndex): return MultiIndex(other._indices + self._indices, idims) return NotImplemented def __str__(self): return ", ".join(str(i) for i in self._indices) def __repr__(self): return "MultiIndex(%r, %r)" % (self._indices, self._idims) def __len__(self): return len(self._indices) def __getitem__(self, i): return self._indices[i] def __iter__(self): return iter(self._indices) def __eq__(self, other): return isinstance(other, MultiIndex) and \ self._indices == other._indices def as_index(i): if isinstance(i, IndexBase): return i elif isinstance(i, int): return FixedIndex(i) else: error("Invalid object %s to create index from." % repr(i)) def _make_idims(ii, shape): if shape is None: return None else: return dict((j,d) for (j,d) in izip(ii, shape) if isinstance(j, Index)) def as_multi_index(ii, shape=None): if isinstance(ii, MultiIndex): if ii._idims: return ii else: ii = ii._indices elif not isinstance(ii, tuple): ii = (ii,) return MultiIndex(ii, _make_idims(ii, shape)) def indices(n): "UFL value: Return a tuple of n new Index objects." return tuple(Index() for _i in range(n)) # TODO: Fix imports everywhere else instead from ufl.indexutils import complete_shape from ufl.indexed import Indexed from ufl.indexsum import IndexSum ufl-1.3.0/ufl/indexsum.py000066400000000000000000000073541226300046600153050ustar00rootroot00000000000000"""This module defines the IndexSum class.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-01-28 # Last changed: 2011-06-17 from ufl.log import error from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.operatorbase import AlgebraOperator from ufl.indexing import Index, MultiIndex, as_multi_index from ufl.precedence import parstr from ufl.common import EmptyDict #--- Sum over an index --- class IndexSum(AlgebraOperator): __slots__ = ("_summand", "_index", "_dimension", "_free_indices", "_index_dimensions") def __new__(cls, summand, index): if not isinstance(summand, Expr): error("Expecting Expr instance, not %s." % repr(summand)) from ufl.constantvalue import Zero if isinstance(summand, Zero): sh = summand.shape() if isinstance(index, Index): j = index elif isinstance(index, MultiIndex): if len(index) != 1: error("Expecting a single Index only.") j, = index fi = tuple(i for i in summand.free_indices() if not i == j) idims = dict(summand.index_dimensions()) del idims[j] return Zero(sh, fi, idims) return AlgebraOperator.__new__(cls) def __init__(self, summand, index): AlgebraOperator.__init__(self) if isinstance(index, Index): j = index elif isinstance(index, MultiIndex): if len(index) != 1: error("Expecting a single Index only.") j, = index self._summand = summand self._index_dimensions = dict(summand.index_dimensions()) self._free_indices = tuple(i for i in summand.free_indices() if not i == j) d = self._index_dimensions[j] self._index = as_multi_index(index, (d,)) ufl_assert(isinstance(self._index, MultiIndex), "Error in initialization of index sum.") self._dimension = d del self._index_dimensions[j] if not self._index_dimensions: self._index_dimensions = EmptyDict def index(self): return self._index[0] def dimension(self): return self._dimension def operands(self): return (self._summand, self._index) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return self._summand.shape() def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return self._summand.is_cellwise_constant() def evaluate(self, x, mapping, component, index_values): i, = self._index tmp = 0 for k in range(self._dimension): index_values.push(i, k) tmp += self._summand.evaluate(x, mapping, component, index_values) index_values.pop() return tmp def __str__(self): return "sum_{%s} %s " % (str(self._index), parstr(self._summand, self)) def __repr__(self): return "IndexSum(%r, %r)" % (self._summand, self._index) ufl-1.3.0/ufl/indexutils.py000066400000000000000000000041651226300046600156360ustar00rootroot00000000000000"Some utilities for working with index tuples." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-14 # Last changed: 2011-06-02 from collections import defaultdict from ufl.indexing import Index #--- Utility functions --- def complete_shape(shape, default_dim): "Complete shape tuple by replacing non-integers with a default dimension." return tuple((s if isinstance(s, int) else default_dim) for s in shape) def unique_indices(indices): """Return a tuple of all indices from input, with no single index occuring more than once in output.""" s = set() newindices = [] for i in indices: if isinstance(i, Index): if not i in s: s.add(i) newindices.append(i) return tuple(newindices) def repeated_indices(indices): "Return tuple of indices occuring more than once in input." ri = [] s = set() for i in indices: if isinstance(i, Index): if i in s: ri.append(i) else: s.add(i) return tuple(ri) def shared_indices(ai, bi): "Return a tuple of indices occuring in both index tuples ai and bi." bis = set(bi) return tuple(i for i in unique_indices(ai) if i in bis) def single_indices(indices): "Return a tuple of all indices occuring exactly once in input." count = defaultdict(int) for i in indices: count[i] += 1 return tuple(i for i in unique_indices(indices) if count[i] == 1) ufl-1.3.0/ufl/integral.py000066400000000000000000000523051226300046600152520ustar00rootroot00000000000000"""The Integral class.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008-2009 # # First added: 2008-03-14 # Last changed: 2011-08-25 from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.constantvalue import is_true_ufl_scalar, is_python_scalar from ufl.expr import Expr from ufl.domains import DomainDescription, Domain, Region, extract_top_domains, extract_domains, as_domain # TODO: Move these somewhere more suitable? def is_globally_constant(expr): """Check if an expression is globally constant, which includes spatially independent constant coefficients that are not known before assembly time.""" from ufl.algorithms.traversal import traverse_terminals from ufl.argument import Argument from ufl.coefficient import Coefficient for e in traverse_terminals(expr): if isinstance(e, Argument): return False if isinstance(e, Coefficient) and e.element().family() != "Real": return False if not e.is_cellwise_constant(): return False # All terminals passed constant check return True # TODO: Move these somewhere more suitable? def is_scalar_constant_expression(expr): """Check if an expression is a globally constant scalar expression.""" if is_python_scalar(expr): return True if expr.shape() != (): return False return is_globally_constant(expr) # TODO: Define some defaults as to how metadata should represent integration data here? # quadrature_degree, quadrature_rule, ... def register_domain_type(domain_type, measure_name): domain_type = domain_type.replace(" ", "_") if domain_type in Measure._domain_types: ufl_assert(Measure._domain_types[domain_type] == measure_name, "Domain type already added with different measure name!") # Nothing to do else: ufl_assert(measure_name not in Measure._domain_types.values(), "Measure name already used for another domain type!") Measure._domain_types[domain_type] = measure_name class MeasureSum(object): """Notational intermediate object to translate the notation 'f*(ds(1)+ds(3))' into 'f*ds(1) + f*ds(3)'. Note that MeasureSum objects will never actually be part of forms. """ __slots__ = ("_measures",) def __init__(self, *measures): self._measures = measures def __rmul__(self, other): integrals = [other*m for m in self._measures] return sum(integrals) def __add__(self, other): if isinstance(other, Measure): return MeasureSum(*(self._measures + (other,))) elif isinstance(other, MeasureSum): return MeasureSum(*(self._measures + other._measures)) return NotImplemented def __str__(self): return "{\n " + "\n + ".join(map(str,self._measures)) + "\n}" def as_domain_type(domain_type): _domain_type = domain_type.replace(" ", "_") # Map short domain type name to long automatically if not _domain_type in Measure._domain_types: for k, v in Measure._domain_types.iteritems(): if v == domain_type: _domain_type = k break # In the end, did we find a valid domain type? if not _domain_type in Measure._domain_types: error("Invalid domain type %s." % domain_type) return _domain_type class Measure(object): """A measure for integration.""" __slots__ = ("_domain_type", "_domain_description", "_metadata", "_domain_data", "_repr",) # Enumeration of special domain ids DOMAIN_ID_UNDEFINED = "undefined" DOMAIN_ID_UNIQUE = "unique" DOMAIN_ID_EVERYWHERE = "everywhere" DOMAIN_ID_OTHERWISE = "otherwise" DOMAIN_ID_DEFAULT = DOMAIN_ID_EVERYWHERE # The one used by dx,ds,dS,etc. #DOMAIN_ID_DEFAULT = DOMAIN_ID_UNIQUE # The one used by dx,ds,dS,etc. DOMAIN_ID_CONSTANTS = (DOMAIN_ID_UNDEFINED, DOMAIN_ID_UNIQUE, DOMAIN_ID_EVERYWHERE, DOMAIN_ID_OTHERWISE, ) # Enumeration of valid domain types CELL = "cell" EXTERIOR_FACET = "exterior_facet" INTERIOR_FACET = "interior_facet" POINT = "point" MACRO_CELL = "macro_cell" SURFACE = "surface" _domain_types = { \ CELL: "dx", EXTERIOR_FACET: "ds", INTERIOR_FACET: "dS", POINT: "dP", MACRO_CELL: "dE", SURFACE: "dc" } _domain_types_tuple = (CELL, EXTERIOR_FACET, INTERIOR_FACET, POINT, MACRO_CELL, SURFACE) def __init__(self, domain_type, domain_id=None, metadata=None, domain_data=None): # Allow long domain type names with ' ' or '_' self._domain_type = as_domain_type(domain_type) # Can't use this constant as default value if domain_id is None: self._domain_description = Measure.DOMAIN_ID_DEFAULT else: self._domain_description = domain_id # Data for form compiler self._metadata = metadata # Data for problem solving environment self._domain_data = domain_data # TODO: Is repr of measure used anywhere anymore? Maybe we can fix this now: # NB! Deliberately ignoring domain data in repr, since it causes a bug # in the cache mechanisms in PyDOLFIN. I don't believe this is the # last word in this case, but it should fix all known problems for now. self._repr = "Measure(%r, %r, %r)" % (self._domain_type, self._domain_description, self._metadata) #self._repr = "Measure(%r, %r, %r, %r)" % (self._domain_type, self._domain_description, # self._metadata, self._domain_data) def reconstruct(self, domain_id=None, metadata=None, domain_data=None): """Construct a new Measure object with some properties replaced with new values. Example: b = dm.reconstruct(domain_id=2) c = dm.reconstruct(metadata={ "quadrature_degree": 3 }) Used by the call operator, so this is equivalent: b = dm(2) c = dm(0, { "quadrature_degree": 3 }) """ if domain_id is None: domain_id = self._domain_description if metadata is None: metadata = self._metadata if domain_data is None: domain_data = self._domain_data return Measure(self._domain_type, domain_id, metadata, domain_data) def domain_type(self): 'Return the domain type, one of "cell", "exterior_facet", "interior_facet", etc.' return self._domain_type def domain_description(self): """Return the domain description of this measure. NB! Can be one of many types, this is work in progress!""" return self._domain_description def domain_id(self): "Return the domain id of this measure (integer)." #ufl_assert(isinstance(self._domain_description, int), "Measure does not have an integer domain id.") # TODO: Enable this return self._domain_description def metadata(self): """Return the integral metadata. This data is not interpreted by UFL. It is passed to the form compiler which can ignore it or use it to compile each integral of a form in a different way.""" return self._metadata def domain_data(self): """Return the integral domain_data. This data is not interpreted by UFL. Its intension is to give a context in which the domain id is interpreted.""" return self._domain_data # TODO: Figure out if we should keep this / how long to keep it / how to deprecate def __getitem__(self, domain_data): """Return a new Measure for same integration type with an attached context for interpreting domain ids. The default ID of this new Measure is undefined, and thus it must be qualified with a domain id to use in an integral. Example: dx = dx[boundaries]; L = f*v*dx(0) + g*v+dx(1).""" return self.reconstruct(domain_id=Measure.DOMAIN_ID_UNDEFINED, domain_data=domain_data) def __call__(self, domain_id=None, metadata=None): """Return integral of same type on given sub domain, optionally with some metadata attached.""" if domain_id is None and metadata is None: # Let syntax dx() mean integral over everywhere return self.reconstruct(domain_id=Measure.DOMAIN_ID_EVERYWHERE) else: # If we get keyword metadata, attatch it, if we get a domain id, use it return self.reconstruct(domain_id=domain_id, metadata=metadata) def __add__(self, other): if isinstance(other, Measure): # Let dx(1) + dx(2) equal dx((1,2)) return MeasureSum(self, other) else: # Can only add Measures return NotImplemented def __mul__(self, other): if isinstance(other, Measure): # Tensor product measure support return ProductMeasure(self, other) else: # Can't multiply Measure from the right with non-Measure type return NotImplemented def __rmul__(self, integrand): # Let other types implement multiplication with Measure # if they want to (to support the dolfin-adjoint TimeMeasure) if not isinstance(integrand, Expr): return NotImplemented # Allow only scalar integrands if not is_true_ufl_scalar(integrand): error("Trying to integrate expression of rank %d with free indices %r." \ % (integrand.rank(), integrand.free_indices())) # Is the measure in a state where multiplication is not allowed? if self._domain_description == Measure.DOMAIN_ID_UNDEFINED: error("Missing domain id. You need to select a subdomain, " +\ "e.g. M = f*dx(0) for subdomain 0.") #else: # TODO: Do it this way instead, and move all logic below into preprocess: # # Return a one-integral form: # from ufl.form import Form # return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] ) # # or if we move domain data into Form instead: # integrals = [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata())] # domain_data = { self.domain_type(): self.domain_data() } # return Form(integrals, domain_data) # TODO: How to represent all kinds of domain descriptions is still a bit unclear # Create form if we have a sufficient domain description elif (# We have a complete measure with domain description isinstance(self._domain_description, DomainDescription) # Is the measure in a basic state 'foo*dx'? or self._domain_description == Measure.DOMAIN_ID_UNIQUE # Is the measure over everywhere? or self._domain_description == Measure.DOMAIN_ID_EVERYWHERE # Is the measure in a state not allowed prior to preprocessing? or self._domain_description == Measure.DOMAIN_ID_OTHERWISE ): # Create and return a one-integral form from ufl.form import Form return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] ) # Did we get several ids? elif isinstance(self._domain_description, tuple): # FIXME: Leave this analysis to preprocessing return sum(integrand*self.reconstruct(domain_id=d) for d in self._domain_description) # Did we get a name? elif isinstance(self._domain_description, str): # FIXME: Leave this analysis to preprocessing # Get all domains and regions from integrand to analyse domains = extract_domains(integrand) # Get domain or region with this name from integrand, error if multiple found name = self._domain_description candidates = set() for TD in domains: if TD.name() == name: candidates.add(TD) ufl_assert(len(candidates) > 0, "Found no domain with name '%s' in integrand." % name) ufl_assert(len(candidates) == 1, "Multiple distinct domains with same name encountered in integrand.") D, = candidates # Reconstruct measure with the found named domain or region measure = self.reconstruct(domain_id=D) return integrand*measure # Did we get a number? elif isinstance(self._domain_description, int): # FIXME: Leave this analysis to preprocessing # Get all top level domains from integrand to analyse domains = extract_top_domains(integrand) # Get domain from integrand, error if multiple found if len(domains) == 0: # This is the partially defined integral from dolfin expression mess cell = integrand.cell() D = None if cell is None else as_domain(cell) elif len(domains) == 1: D, = domains else: error("Ambiguous reference to integer subdomain with multiple top domains in integrand.") if D is None: # We have a number but not a domain? Leave it to preprocess... # This is the case with badly formed forms which can occur from dolfin # Create and return a one-integral form from ufl.form import Form return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] ) else: # Reconstruct measure with the found numbered subdomain measure = self.reconstruct(domain_id=D[self._domain_description]) return integrand*measure # Provide error to user else: error("Invalid domain id %s." % str(self._domain_description)) def __str__(self): d = Measure._domain_types[self._domain_type] metastring = "" if self._metadata is None else ("<%s>" % repr(self._metadata)) return "%s%s%s" % (d, self._domain_description, metastring) def __repr__(self): return self._repr def __hash__(self): return hash(repr(self)) def __eq__(self, other): return repr(self) == repr(other) class ProductMeasure(Measure): "Representation of a product measure." __slots__ = ("_measures",) def __init__(self, *measures): "Create ProductMeasure from given list of measures." self._measures = list(measures) ufl_assert(len(self._measures) > 0, "Expecting at least one measure") # FIXME: MER: The below is clearly wrong, but preprocess pose some # pretty hard restrictions. To be dealt with later. # self._domain_type = tuple(m.domain_type() for m in self._measures) # self._domain_description = tuple(m.domain_id() for m in self._measures) self._domain_type = measures[0].domain_type() self._domain_description = measures[0].domain_id() self._metadata = None self._domain_data = None self._repr = "ProductMeasure(*%r)" % self._measures def __mul__(self, other): "Flatten multiplication of product measures." if isinstance(other, Measure): measures = self.sub_measures() + [other] return ProductMeasure(*measures) #error("Can't multiply ProductMeasure from the right (with %r)." % (other,)) return NotImplemented def __rmul__(self, integrand): error("TODO: Implement ProductMeasure.__rmul__ to construct integral and form somehow.") def sub_measures(self): "Return submeasures." return self._measures # Transitional helper until we decide to change Integral permanently def Integral2(integrand, domain_type, domain_desc, compiler_data, domain_data): return Integral(integrand, domain_type, domain_desc, compiler_data, domain_data) class Integral(object): "An integral over a single domain." __slots__ = ("_integrand", "_domain_type", "_domain_description", "_compiler_data", "_domain_data", # TODO: Make this part of Form instead? "_measure", # TODO: Remove when not used anywhere ) def __init__(self, integrand, domain_type, domain_description, compiler_data, domain_data): ufl_assert(isinstance(integrand, Expr), "Expecting integrand to be an Expr instance.") self._integrand = integrand self._domain_type = domain_type self._domain_description = domain_description self._compiler_data = compiler_data self._domain_data = domain_data # TODO: Remove this, kept for a transitional period: self._measure = Measure(domain_type, domain_description, compiler_data, domain_data) def reconstruct(self, integrand=None, domain_type=None, domain_description=None, compiler_data=None, domain_data=None): """Construct a new Integral object with some properties replaced with new values. Example: b = a.reconstruct(expand_compounds(a.integrand())) c = a.reconstruct(compiler_data={'quadrature_degree':2}) """ if integrand is None: integrand = self.integrand() if domain_type is None: domain_type = self.domain_type() if domain_description is None: domain_description = self.domain_description() if compiler_data is None: compiler_data = self.compiler_data() if domain_data is None: domain_data = self.domain_data() return Integral(integrand, domain_type, domain_description, compiler_data, domain_data) def integrand(self): "Return the integrand expression, which is an Expr instance." return self._integrand def domain_type(self): "Return the domain type of this integral." return self._domain_type def domain_description(self): """Return the domain description of this integral. NB! Can be one of many types, this is work in progress!""" return self._domain_description def domain_id(self): "Return the domain id of this integral." #ufl_assert(isinstance(self._domain_description, int), "Integral does not have an integer domain id.") # TODO: Enable this return self._domain_description def compiler_data(self): "Return the compiler metadata this integral has been annotated with." return self._compiler_data def domain_data(self): # TODO: Move to Form? "Return the assembler metadata this integral has been annotated with." return self._domain_data def measure(self): # TODO: Remove this "Return the measure associated with this integral." return self._measure def __neg__(self): return self.reconstruct(-self._integrand) def __mul__(self, scalar): ufl_assert(is_python_scalar(scalar), "Cannot multiply an integral with non-constant values.") return self.reconstruct(scalar*self._integrand) def __rmul__(self, scalar): ufl_assert(is_scalar_constant_expression(scalar), "An integral can only be multiplied by a " "globally constant scalar expression.") return self.reconstruct(scalar*self._integrand) def __str__(self): return "{ %s } * %s" % (self._integrand, self._measure) # FIXME def __repr__(self): return "Integral(%r, %r, %r, %r, %r)" % (self._integrand, self._domain_type, self._domain_description, self._compiler_data, self._domain_data) def __eq__(self, other): return (self._domain_type == other._domain_type and self._domain_description == other._domain_description and self._integrand == other._integrand and self._compiler_data == other._compiler_data and self._domain_data == other._domain_data ) def __hash__(self): data = (hash(self._integrand), self._domain_type, self._domain_description) # Assuming we can get away with few collisions by ignoring metadata: # hash(self._compiler_data), hash(self._domain_data) return hash(data) ufl-1.3.0/ufl/log.py000066400000000000000000000167761226300046600142420ustar00rootroot00000000000000"""This module provides functions used by the UFL implementation to output messages. These may be redirected by the user of UFL.""" # Copyright (C) 2005-2013 Anders Logg and Martin Sandve Alnaes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Johan Hake, 2009. # # First added: 2005-02-04 # Last changed: 2011-06-08 import sys import types import logging log_functions = ["log", "debug", "info", "deprecate", "warning", "error", "begin", "end", "set_level", "push_level", "pop_level", "set_indent", "add_indent", "set_handler", "get_handler", "get_logger", "add_logfile", "set_prefix", "info_red", "info_green", "info_blue", "warning_red", "warning_green", "warning_blue"] __all__ = log_functions + ["DEBUG", "INFO", "DEPRECATE", "WARNING", "ERROR", "CRITICAL", "Logger", "log_functions"] # Import default log levels from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL DEPRECATE = (INFO + WARNING) // 2 # This is used to override emit() in StreamHandler for printing without newline def emit(self, record): message = self.format(record) format_string = "%s" if getattr(record, "continued", False) else "%s\n" self.stream.write(format_string % message) self.flush() # Colors RED = "\033[1;37;31m%s\033[0m" BLUE = "\033[1;37;34m%s\033[0m" GREEN = "\033[1;37;32m%s\033[0m" # Logger class class Logger: def __init__(self, name, exception_type=Exception): "Create logger instance." self._name = name self._exception_type = exception_type # Set up handler h = logging.StreamHandler(sys.stdout) h.setLevel(WARNING) # Override emit() in handler for indentation h.emit = types.MethodType(emit, h, h.__class__) self._handler = h # Set up logger self._log = logging.getLogger(name) assert len(self._log.handlers) == 0 self._log.addHandler(h) self._log.setLevel(DEBUG) self._logfiles = {} # Set initial indentation level self._indent_level = 0 # Setup stack with default logging level self._level_stack = [DEBUG] # Set prefix self._prefix = "" def add_logfile(self, filename=None, mode="a", level=DEBUG): if filename is None: filename = "%s.log" % self._name if filename in self._logfiles: self.warning("Trying to add logfile %s multiple times." % filename) return h = logging.FileHandler(filename, mode) h.emit = types.MethodType(emit, h, h.__class__) h.setLevel(level) self._log.addHandler(h) self._logfiles[filename] = h return h def get_logfile_handler(self, filename): return self._logfiles[filename] def log(self, level, *message): "Write a log message on given log level" text = self._format_raw(*message) if len(text) >= 3 and text[-3:] == "...": self._log.log(level, self._format(*message), extra={"continued": True}) else: self._log.log(level, self._format(*message)) def debug(self, *message): "Write debug message." self.log(DEBUG, *message) def info(self, *message): "Write info message." self.log(INFO, *message) def info_red(self, *message): "Write info message in red." self.log(INFO, RED % self._format_raw(*message)) def info_green(self, *message): "Write info message in green." self.log(INFO, GREEN % self._format_raw(*message)) def info_blue(self, *message): "Write info message in blue." self.log(INFO, BLUE % self._format_raw(*message)) def deprecate(self, *message): "Write deprecation message." self.log(DEPRECATE, RED % self._format_raw(*message)) def warning(self, *message): "Write warning message." self._log.warning(self._format(*message)) def warning_red(self, *message): "Write warning message in red." self._log.warning(RED % self._format(*message)) def warning_green(self, *message): "Write warning message in green." self._log.warning(GREEN % self._format(*message)) def warning_blue(self, *message): "Write warning message in blue." self._log.warning(BLUE % self._format(*message)) def error(self, *message): "Write error message and raise an exception." self._log.error(*message) raise self._exception_type(self._format_raw(*message)) def begin(self, *message): "Begin task: write message and increase indentation level." self.info(*message) self.info("-"*len(self._format_raw(*message))) self.add_indent() def end(self): "End task: write a newline and decrease indentation level." self.info("") self.add_indent(-1) def push_level(self, level): "Push a log level on the level stack." self._level_stack.append(level) self.set_level(level) def pop_level(self): "Pop log level from the level stack, reverting to before the last push_level." self._level_stack.pop() level = self._level_stack[-1] self.set_level(level) def set_level(self, level): "Set log level." self._level_stack[-1] = level #self._log.setLevel(level) self._handler.setLevel(level) def set_indent(self, level): "Set indentation level." self._indent_level = level def add_indent(self, increment=1): "Add to indentation level." self._indent_level += increment def get_handler(self): "Get handler for logging." return self._handler def set_handler(self, handler): """Replace handler for logging. To add additional handlers instead of replacing the existing, use log.get_logger().addHandler(myhandler). See the logging module for more details. """ self._log.removeHandler(self._handler) self._log.addHandler(handler) self._handler = handler handler.emit = types.MethodType(emit, self._handler, self._handler.__class__) def get_logger(self): "Return message logger." return self._log def set_prefix(self, prefix): "Set prefix for log messages." self._prefix = prefix def _format(self, *message): "Format message including indentation." indent = self._prefix + 2*self._indent_level*" " return "\n".join([indent + line for line in (message[0] % message[1:]).split("\n")]) def _format_raw(self, *message): "Format message without indentation." return message[0] % message[1:] #--- Set up global log functions --- # Base class for UFL exceptions class UFLException(Exception): "Base class for UFL exceptions" pass ufl_logger = Logger("UFL", UFLException) for foo in log_functions: exec("%s = ufl_logger.%s" % (foo, foo)) ufl-1.3.0/ufl/mathfunctions.py000066400000000000000000000265741226300046600163400ustar00rootroot00000000000000"""This module provides basic mathematical functions.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # Modified by Kristian B. Oelgaard, 2011 # # First added: 2008-03-14 # Last changed: 2013-03-15 import math from ufl.log import warning, error from ufl.assertions import ufl_assert from ufl.operatorbase import Operator from ufl.constantvalue import is_true_ufl_scalar, ScalarValue, Zero, FloatValue, IntValue, as_ufl from ufl.common import EmptyDict """ TODO: Include additional functions available in (need derivatives as well): Exponential and logarithmic functions: log10 Compute common logarithm (function) TODO: Any other useful special functions? About bessel functions: http://en.wikipedia.org/wiki/Bessel_function Portable implementations of bessel functions: http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/main_overview/tr1.html Implementation in C++ std::tr1:: or boost::math::tr1:: - BesselK: cyl_bessel_k(nu, x) - BesselI: cyl_bessel_i(nu, x) - BesselJ: cyl_bessel_j(nu, x) - BesselY: cyl_neumann(nu, x) """ #--- Function representations --- class MathFunction(Operator): "Base class for all math functions" # Freeze member variables for objects in this class __slots__ = ("_name", "_argument",) def __init__(self, name, argument): Operator.__init__(self) ufl_assert(is_true_ufl_scalar(argument), "Expecting scalar argument.") self._name = name self._argument = argument def operands(self): return (self._argument,) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return () def evaluate(self, x, mapping, component, index_values): a = self._argument.evaluate(x, mapping, component, index_values) try: res = getattr(math, self._name)(a) except ValueError: warning('Value error in evaluation of function %s with argument %s.' % (self._name, a)) raise return res def __str__(self): return "%s(%s)" % (self._name, self._argument) def __repr__(self): return "%s(%r)" % (self._name, self._argument) class Sqrt(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.sqrt(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "sqrt", argument) class Exp(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.exp(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "exp", argument) class Ln(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.log(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "ln", argument) def evaluate(self, x, mapping, component, index_values): a = self._argument.evaluate(x, mapping, component, index_values) return math.log(a) class Cos(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.cos(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "cos", argument) class Sin(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.sin(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "sin", argument) class Tan(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.tan(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "tan", argument) class Cosh(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.cosh(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "cosh", argument) class Sinh(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.sinh(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "sinh", argument) class Tanh(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.tanh(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "tanh", argument) class Acos(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.acos(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "acos", argument) class Asin(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.asin(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "asin", argument) class Atan(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): return FloatValue(math.atan(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "atan", argument) class Atan2(Operator): __slots__ = ("_name", "_arg1", "_arg2", "_classname") def __new__(cls, arg1, arg2): if isinstance(arg1, (ScalarValue, Zero)) and isinstance(arg2, (ScalarValue, Zero)): return FloatValue(math.atan2(float(arg1), float(arg2))) return Operator.__new__(cls) def __init__(self, arg1, arg2): Operator.__init__(self) ufl_assert(is_true_ufl_scalar(arg1), "Expecting scalar argument 1.") ufl_assert(is_true_ufl_scalar(arg2), "Expecting scalar argument 2.") self._name = "atan_2" self._arg1 = arg1 self._arg2 = arg2 def operands(self): return (self._arg1, self._arg2) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return () def evaluate(self, x, mapping, component, index_values): a = self._arg1.evaluate(x, mapping, component, index_values) b = self._arg2.evaluate(x, mapping, component, index_values) try: res = math.atan2(a,b) except ValueError: warning('Value error in evaluation of function %s with arguments %s, %s.' % (self._name, a,b)) raise return res def __str__(self): return "%s(%s,%s)" % (self._name, self._arg1, self._arg2) def __repr__(self): return "%s(%s,%s)" % (self._name, self._arg1, self._arg2) def _find_erf(): import math if hasattr(math, 'erf'): return math.erf import scipy.special if hasattr(scipy.special, 'erf'): return scipy.special.erf return None class Erf(MathFunction): __slots__ = () def __new__(cls, argument): if isinstance(argument, (ScalarValue, Zero)): erf = _find_erf() if erf is not None: return FloatValue(erf(float(argument))) return MathFunction.__new__(cls) def __init__(self, argument): MathFunction.__init__(self, "erf", argument) def evaluate(self, x, mapping, component, index_values): a = self._argument.evaluate(x, mapping, component, index_values) erf = _find_erf() if erf is None: error("No python implementation of erf available on this system, cannot evaluate. Upgrade python or install scipy.") return erf(a) class BesselFunction(Operator): "Base class for all bessel functions" # Freeze member variables for objects in this class __slots__ = ("_name", "_nu", "_argument", "_classname") def __init__(self, name, classname, nu, argument): Operator.__init__(self) ufl_assert(is_true_ufl_scalar(nu), "Expecting scalar nu.") ufl_assert(is_true_ufl_scalar(argument), "Expecting scalar argument.") fnu = float(nu) inu = int(nu) if fnu == inu: nu = as_ufl(inu) else: nu = as_ufl(fnu) self._classname = classname self._name = name self._nu = nu self._argument = argument def operands(self): return (self._nu, self._argument) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return () def evaluate(self, x, mapping, component, index_values): a = self._argument.evaluate(x, mapping, component, index_values) try: import scipy.special except: error("You must have scipy installed to evaluate bessel functions in python.") name = self._name[-1] if isinstance(self._nu, IntValue): nu = int(self._nu) functype = 'n' if name != 'i' else 'v' else: nu = self._nu.evaluate(x, mapping, component, index_values) functype = 'v' func = getattr(scipy.special, name + functype) return func(nu, a) def __str__(self): return "%s(%s, %s)" % (self._name, self._nu, self._argument) def __repr__(self): return "%s(%r, %r)" % (self._classname, self._nu, self._argument) class BesselJ(BesselFunction): __slots__ = () def __init__(self, nu, argument): BesselFunction.__init__(self, "cyl_bessel_j", "BesselJ", nu, argument) class BesselY(BesselFunction): __slots__ = () def __init__(self, nu, argument): BesselFunction.__init__(self, "cyl_bessel_y", "BesselY", nu, argument) class BesselI(BesselFunction): __slots__ = () def __init__(self, nu, argument): BesselFunction.__init__(self, "cyl_bessel_i", "BesselI", nu, argument) class BesselK(BesselFunction): __slots__ = () def __init__(self, nu, argument): BesselFunction.__init__(self, "cyl_bessel_k", "BesselK", nu, argument) ufl-1.3.0/ufl/objects.py000066400000000000000000000035321226300046600150740ustar00rootroot00000000000000"Utility objects for pretty syntax in user code." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # Modified by Kristian Oelgaard, 2009 # # First added: 2008-03-14 # Last changed: 2013-01-11 from ufl.indexing import indices from ufl.integral import Measure from ufl.geometry import Cell # Default indices i, j, k, l = indices(4) p, q, r, s = indices(4) # Default measures for integration dx = Measure(Measure.CELL, Measure.DOMAIN_ID_DEFAULT) ds = Measure(Measure.EXTERIOR_FACET, Measure.DOMAIN_ID_DEFAULT) dS = Measure(Measure.INTERIOR_FACET, Measure.DOMAIN_ID_DEFAULT) dP = Measure(Measure.POINT, Measure.DOMAIN_ID_DEFAULT) dE = Measure(Measure.MACRO_CELL, Measure.DOMAIN_ID_DEFAULT) dc = Measure(Measure.SURFACE, Measure.DOMAIN_ID_DEFAULT) # Cell types cell1D = Cell("cell1D", 1) cell2D = Cell("cell2D", 2) cell3D = Cell("cell3D", 3) vertex = Cell("vertex", 0) interval = Cell("interval", 1) triangle = Cell("triangle", 2) tetrahedron = Cell("tetrahedron", 3) quadrilateral = Cell("quadrilateral", 2) hexahedron = Cell("hexahedron", 3) # Facet is just a dummy declaration for RestrictedElement facet = "facet" ufl-1.3.0/ufl/operatorbase.py000066400000000000000000000121601226300046600161260ustar00rootroot00000000000000"Base class for all operators, i.e. non-terminal expr types." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-03-14 # Last changed: 2012-05-23 from itertools import imap from ufl.expr import Expr from ufl.log import error # Modified from ufl.algorithms.traveral to avoid circular dependencies... def traverse_terminals2(expr): input = [expr] while input: e = input.pop() ops = e.operands() if ops: input.extend(ops) else: yield e def typetuple(e): return tuple(type(o) for o in e.operands()) def compute_hash1(expr): # Crap return hash((type(expr), tuple((type(o), typetuple(o)) for o in expr.operands()))) def compute_hash2(expr): # Best so far hashdata = ( (expr.__class__._uflclass,) + tuple(hash(o) for o in expr.operands()) ) return hash(str(hashdata)) def compute_hash3(expr): # Crap h = (type(expr).__name__,) + expr.operands() return hash(h) def get_some_terminals(expr): some = [] for t in traverse_terminals2(expr): some.append(t) if len(some) == 5: return some return some def compute_hash4(expr): # Not as good as 2 h = ( (type(expr).__name__,) + tuple(type(o).__name__ for o in expr.operands()) + tuple(imap(repr, get_some_terminals(expr))) ) return hash(str(h)) def compute_hash5(expr): # Just as good as 2, with additional work hashdata = ((type(expr).__name__,) + tuple(hash(o) for o in expr.operands()) + tuple(repr(t) for t in get_some_terminals(expr))) return hash(hashdata) #import md5 # use hashlib instead if we need this #def compute_hash6(expr): # Exactly as good as 2, with additional work # hashdata = ( (expr.__class__._uflclass,) # + tuple(hash(o) for o in expr.operands()) ) # return hash(md5.md5(str(hashdata)).digest()) _hashes = set() _hashes_added = 0 def compute_hash_with_stats(expr): global _hashes, _hashes_added h = compute_hash2(expr) #h = compute_hash6(expr) _hashes.add(h) _hashes_added += 1 if _hashes_added % 10000 == 0: print "HASHRATIO", len(_hashes)/float(_hashes_added) return h # This seems to be the best of the above compute_hash = compute_hash2 #compute_hash = compute_hash_with_stats #--- Base class for operator objects --- class Operator(Expr): __slots__ = ("_hash",) def __init__(self): Expr.__init__(self) self._hash = None def signature_data(self): return self._classid def __hash__(self): "Compute a hash code for this expression. Used by sets and dicts." if self._hash is None: self._hash = compute_hash(self) return self._hash #return compute_hash(self) # REPR TODO: Cache or not? def __getnewargs__(self): return self.operands() def reconstruct(self, *operands): "Return a new object of the same type with new operands." return self.__class__._uflclass(*operands) def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return all(o.is_cellwise_constant() for o in self.operands()) #--- Subgroups of terminals --- class AlgebraOperator(Operator): __slots__ = () def __init__(self): Operator.__init__(self) class WrapperType(Operator): __slots__ = () def __init__(self): Operator.__init__(self) #--- Non-tensor types --- class Tuple(WrapperType): "For internal use, never to be created by users." __slots__ = ("_items",) def __init__(self, *items): WrapperType.__init__(self) if not all(isinstance(i, Expr) for i in items): error("Got non-Expr in Tuple, is this intended? If so, remove this error.") self._items = tuple(items) def operands(self): return self._items def shape(self): error("Calling shape on a utility type is an error.") def free_indices(self): error("Calling free_indices on a utility type is an error.") def index_dimensions(self): error("Calling free_indices on a utility type is an error.") def __getitem__(self, i): return self._items[i] def __len__(self): return len(self._items) def __iter__(self): return iter(self._items) def __str__(self): return "Tuple(*(%s,))" % ", ".join(str(i) for i in self._items) def __repr__(self): return "Tuple(*%s)" % repr(self._items) ufl-1.3.0/ufl/operators.py000066400000000000000000000441531226300046600154650ustar00rootroot00000000000000"""This module extends the form language with free function operators, which are either already available as member functions on UFL objects or defined as compound operators involving basic operations on the UFL objects.""" # Copyright (C) 2008-2013 Martin Sandve Alnes and Anders Logg # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Kristian B. Oelgaard, 2011 # # First added: 2008-04-09 # Last changed: 2013-03-15 import operator from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.form import Form from ufl.constantvalue import Zero, ScalarValue, as_ufl from ufl.differentiation import VariableDerivative, Grad, Div, Curl, NablaGrad, NablaDiv from ufl.tensoralgebra import Transposed, Inner, Outer, Dot, Cross, \ Determinant, Inverse, Cofactor, Trace, Deviatoric, Skew, Sym from ufl.variable import Variable from ufl.tensors import as_tensor, as_matrix, as_vector, ListTensor from ufl.conditional import EQ, NE, LE, GE, LT, GT, \ AndCondition, OrCondition, NotCondition, Conditional from ufl.mathfunctions import Sqrt, Exp, Ln, Erf,\ Cos, Sin, Tan, Cosh, Sinh, Tanh, Acos, Asin, Atan, Atan2,\ BesselJ, BesselY, BesselI, BesselK from ufl.restriction import CellAvg, FacetAvg from ufl.indexing import indices from ufl.indexed import Indexed from ufl.geometry import SpatialCoordinate #--- Basic operators --- def rank(f): "UFL operator: The rank of f." f = as_ufl(f) return len(f.shape()) def shape(f): "UFL operator: The shape of f." f = as_ufl(f) return f.shape() #--- Elementwise tensor operators --- def elem_op_items(op_ind, indices, *args): sh = args[0].shape() n = sh[len(indices)] def extind(ii): return tuple(list(indices) + [ii]) if len(sh) == len(indices)+1: return [op_ind(extind(i), *args) for i in range(n)] else: return [elem_op_items(op_ind, extind(i), *args) for i in range(n)] def elem_op(op, *args): "UFL operator: Take the elementwise application of operator op on scalar values from one or more tensor arguments." args = map(as_ufl, args) sh = args[0].shape() ufl_assert(all(sh == x.shape() for x in args), "Cannot take elementwise operation with different shapes.") if sh == (): return op(*args) def op_ind(ind, *args): return op(*[x[ind] for x in args]) return as_tensor(elem_op_items(op_ind, (), *args)) def elem_mult(A, B): "UFL operator: Take the elementwise multiplication of the tensors A and B with the same shape." return elem_op(operator.mul, A, B) def elem_div(A, B): "UFL operator: Take the elementwise division of the tensors A and B with the same shape." return elem_op(operator.div, A, B) def elem_pow(A, B): "UFL operator: Take the elementwise power of the tensors A and B with the same shape." return elem_op(operator.pow, A, B) #--- Tensor operators --- def transpose(A): "UFL operator: Take the transposed of tensor A." A = as_ufl(A) if A.shape() == (): return A return Transposed(A) def outer(*operands): "UFL operator: Take the outer product of two or more operands." n = len(operands) if n == 1: return operands[0] elif n == 2: a, b = operands else: a = outer(*operands[:-1]) b = operands[-1] a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Outer(a, b) def inner(a, b): "UFL operator: Take the inner product of a and b." a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Inner(a, b) # TODO: Something like this would be useful in some cases, # but should inner just support a.rank() != b.rank() instead? def _partial_inner(a, b): "UFL operator: Take the partial inner product of a and b." ar, br = a.rank(), b.rank() n = min(ar, br) return contraction(a, range(n-ar, n-ar+n), b, range(n)) def dot(a, b): "UFL operator: Take the dot product of a and b." a = as_ufl(a) b = as_ufl(b) if a.shape() == () and b.shape() == (): return a*b return Dot(a, b) #return contraction(a, (a.rank()-1,), b, (b.rank()-1,)) def contraction(a, a_axes, b, b_axes): "UFL operator: Take the contraction of a and b over given axes." ai, bi = a_axes, b_axes ufl_assert(len(ai) == len(bi), "Contraction must be over the same number of axes.") ash = a.shape() bsh = b.shape() aii = indices(a.rank()) bii = indices(b.rank()) cii = indices(len(ai)) shape = [None]*len(ai) for i,j in enumerate(ai): aii[j] = cii[i] shape[i] = ash[j] for i,j in enumerate(bi): bii[j] = cii[i] ufl_assert(shape[i] == bsh[j], "Shape mismatch in contraction.") s = a[aii]*b[bii] cii = set(cii) ii = tuple(i for i in (aii + bii) if not i in cii) return as_tensor(s, ii) def perp(v): "UFL operator: Take the perp of v, i.e. (-v1, +v0)." v = as_ufl(v) ufl_assert(v.shape() == (2,), "Expecting a 2D vector expression.") return as_vector((-v[1],v[0])) def cross(a, b): "UFL operator: Take the cross product of a and b." a = as_ufl(a) b = as_ufl(b) #ufl_assert(a.shape() == (3,) and b.shape() == (3,), # "Expecting 3D vectors in cross product.") return Cross(a, b) def det(A): "UFL operator: Take the determinant of A." A = as_ufl(A) if A.shape() == (): return A return Determinant(A) def inv(A): "UFL operator: Take the inverse of A." A = as_ufl(A) if A.shape() == (): return 1 / A return Inverse(A) def cofac(A): "UFL operator: Take the cofactor of A." A = as_ufl(A) return Cofactor(A) def tr(A): "UFL operator: Take the trace of A." A = as_ufl(A) return Trace(A) def diag(A): """UFL operator: Take the diagonal part of rank 2 tensor A _or_ make a diagonal rank 2 tensor from a rank 1 tensor. Always returns a rank 2 tensor. See also diag_vector.""" # TODO: Make a compound type or two for this operator # Get and check dimensions r = A.rank() if r == 1: n, = A.shape() elif r == 2: m, n = A.shape() ufl_assert(m == n, "Can only take diagonal of square tensors.") else: error("Expecting rank 1 or 2 tensor.") # Build matrix row by row rows = [] for i in range(n): row = [0]*n row[i] = A[i] if r == 1 else A[i,i] rows.append(row) return as_matrix(rows) def diag_vector(A): """UFL operator: Take the diagonal part of rank 2 tensor A and return as a vector. See also diag.""" # TODO: Make a compound type for this operator # Get and check dimensions ufl_assert(A.rank() == 2, "Expecting rank 2 tensor.") m, n = A.shape() ufl_assert(m == n, "Can only take diagonal of square tensors.") # Return diagonal vector return as_vector([A[i,i] for i in range(n)]) def dev(A): "UFL operator: Take the deviatoric part of A." A = as_ufl(A) return Deviatoric(A) def skew(A): "UFL operator: Take the skew symmetric part of A." A = as_ufl(A) return Skew(A) def sym(A): "UFL operator: Take the symmetric part of A." A = as_ufl(A) return Sym(A) #--- Differential operators def Dx(f, *i): """UFL operator: Take the partial derivative of f with respect to spatial variable number i. Equivalent to f.dx(\*i).""" f = as_ufl(f) return f.dx(*i) def Dt(f): "UFL operator: The partial derivative of f with respect to time." #return TimeDerivative(f) # TODO: Add class raise NotImplementedError def Dn(f): """UFL operator: Take the directional derivative of f in the facet normal direction, Dn(f) := dot(grad(f), n).""" f = as_ufl(f) cell = f.cell() if cell is None: # FIXME: Rather if f.is_cellwise_constant()? return Zero(f.shape(), f.free_indices(), f.index_dimensions()) return dot(grad(f), cell.n) def diff(f, v): """UFL operator: Take the derivative of f with respect to the variable v. If f is a form, diff is applied to each integrand. """ if isinstance(f, Form): from ufl.algorithms.transformer import transform_integrands return transform_integrands(f, lambda e: diff(e, v)) else: f = as_ufl(f) if isinstance(v, SpatialCoordinate): return grad(f) # TODO: Allow this? Must be tested well! #elif (isinstance(v, Indexed) # and isinstance(v.operands()[0], SpatialCoordinate)): # return grad(f)[...,v.operands()[1]] elif isinstance(v, Variable): return VariableDerivative(f, v) else: error("Expecting a Variable or SpatialCoordinate in diff.") def grad(f): """UFL operator: Take the gradient of f. This operator follows the grad convention where grad(s)[i] = s.dx(j) grad(v)[i,j] = v[i].dx(j) grad(T)[:,i] = T[:].dx(i) for scalar expressions s, vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return Grad(f) def div(f): """UFL operator: Take the divergence of f. This operator follows the div convention where div(v) = v[i].dx(i) div(T)[:] = T[:,i].dx(i) for vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return Div(f) def nabla_grad(f): """UFL operator: Take the gradient of f. This operator follows the grad convention where nabla_grad(s)[i] = s.dx(j) nabla_grad(v)[i,j] = v[j].dx(i) nabla_grad(T)[i,:] = T[:].dx(i) for scalar expressions s, vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return NablaGrad(f) def nabla_div(f): """UFL operator: Take the divergence of f. This operator follows the div convention where nabla_div(v) = v[i].dx(i) nabla_div(T)[:] = T[i,:].dx(i) for vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return NablaDiv(f) def curl(f): "UFL operator: Take the curl of f." f = as_ufl(f) return Curl(f) rot = curl #--- DG operators --- def jump(v, n=None): "UFL operator: Take the jump of v across a facet." v = as_ufl(v) cell = v.cell() if cell is None: warning("TODO: Not all expressions have a cell. Is it right to return zero from jump then?") # TODO: Is this right? If v has no cell, it doesn't depend on # anything spatially variable or any form arguments, and thus # the jump is zero. In other words, I'm assuming that # "v.cell() is None" is equivalent with "v is a constant". return Zero(v.shape(), v.free_indices(), v.index_dimensions()) else: if n is None: return v('+') - v('-') r = v.rank() if r == 0: return v('+')*n('+') + v('-')*n('-') else: return dot(v('+'), n('+')) + dot(v('-'), n('-')) def avg(v): "UFL operator: Take the average of v across a facet." v = as_ufl(v) return 0.5*(v('+') + v('-')) def cell_avg(f): "UFL operator: Take the average of v over a cell." #ufl_assert((isinstance(f, Restricted) and isinstance(f.operands()[0], FormArgument)) or # isinstance(f, FormArgument), "Can only take the cell average of a (optionally restricted) Coefficient or Argument.") return CellAvg(f) def facet_avg(f): "UFL operator: Take the average of v over a facet." #ufl_assert((isinstance(f, Restricted) and isinstance(f.operands()[0], FormArgument)) or # isinstance(f, FormArgument), "Can only take the cell average of a (optionally restricted) Coefficient or Argument.") return FacetAvg(f) #--- Other operators --- def variable(e): "UFL operator: Define a variable representing the given expression, see also diff()." e = as_ufl(e) return Variable(e) #--- Conditional expressions --- def conditional(condition, true_value, false_value): """UFL operator: A conditional expression, taking the value of true_value when condition evaluates to true and false_value otherwise.""" return Conditional(condition, true_value, false_value) def eq(left, right): "UFL operator: A boolean expresion (left == right) for use with conditional." return EQ(left, right) def ne(left, right): "UFL operator: A boolean expresion (left != right) for use with conditional." return NE(left, right) def le(left, right): "UFL operator: A boolean expresion (left <= right) for use with conditional." return as_ufl(left) <= as_ufl(right) def ge(left, right): "UFL operator: A boolean expresion (left >= right) for use with conditional." return as_ufl(left) >= as_ufl(right) def lt(left, right): "UFL operator: A boolean expresion (left < right) for use with conditional." return as_ufl(left) < as_ufl(right) def gt(left, right): "UFL operator: A boolean expresion (left > right) for use with conditional." return as_ufl(left) > as_ufl(right) def And(left, right): "UFL operator: A boolean expresion (left and right) for use with conditional." return AndCondition(left, right) def Or(left, right): "UFL operator: A boolean expresion (left or right) for use with conditional." return OrCondition(left, right) def Not(condition): "UFL operator: A boolean expresion (not condition) for use with conditional." return NotCondition(condition) def sign(x): "UFL operator: Take the sign (+1 or -1) of x." # TODO: Add a Sign type for this? return conditional(eq(x, 0), 0, conditional(lt(x, 0), -1, +1)) def Max(x, y): "UFL operator: Take the maximum of x and y." # TODO: Add a Maximum type for this? return conditional(gt(x, y), x, y) def Min(x, y): "UFL operator: Take the minimum of x and y." # TODO: Add a Minimum type for this? return conditional(lt(x, y), x, y) #--- Math functions --- def _mathfunction(f, cls): f = as_ufl(f) r = cls(f) if isinstance(r, (ScalarValue, Zero, int, float)): return float(r) return r def sqrt(f): "UFL operator: Take the square root of f." return _mathfunction(f, Sqrt) def exp(f): "UFL operator: Take the exponential of f." return _mathfunction(f, Exp) def ln(f): "UFL operator: Take the natural logarithm of f." return _mathfunction(f, Ln) def cos(f): "UFL operator: Take the cosinus of f." return _mathfunction(f, Cos) def sin(f): "UFL operator: Take the sinus of f." return _mathfunction(f, Sin) def tan(f): "UFL operator: Take the tangent of f." return _mathfunction(f, Tan) def cosh(f): "UFL operator: Take the cosinus hyperbolicus of f." return _mathfunction(f, Cosh) def sinh(f): "UFL operator: Take the sinus hyperbolicus of f." return _mathfunction(f, Sinh) def tanh(f): "UFL operator: Take the tangent hyperbolicus of f." return _mathfunction(f, Tanh) def acos(f): "UFL operator: Take the inverse cosinus of f." return _mathfunction(f, Acos) def asin(f): "UFL operator: Take the inverse sinus of f." return _mathfunction(f, Asin) def atan(f): "UFL operator: Take the inverse tangent of f." return _mathfunction(f, Atan) def atan_2(f1,f2): "UFL operator: Take the inverse tangent of f." f1 = as_ufl(f1) f2 = as_ufl(f2) r = Atan2(f1, f2) if isinstance(r, (ScalarValue, Zero, int, float)): return float(r) return r def erf(f): "UFL operator: Take the error function of f." return _mathfunction(f, Erf) def bessel_J(nu, f): """UFL operator: cylindrical Bessel function of the first kind.""" nu = as_ufl(nu) f = as_ufl(f) return BesselJ(nu, f) def bessel_Y(nu, f): """UFL operator: cylindrical Bessel function of the second kind.""" nu = as_ufl(nu) f = as_ufl(f) return BesselY(nu, f) def bessel_I(nu, f): """UFL operator: regular modified cylindrical Bessel function.""" nu = as_ufl(nu) f = as_ufl(f) return BesselI(nu, f) def bessel_K(nu, f): """UFL operator: irregular modified cylindrical Bessel function.""" nu = as_ufl(nu) f = as_ufl(f) return BesselK(nu, f) #--- Special function for exterior_derivative def exterior_derivative(f): """UFL operator: Take the exterior derivative of f. The exterior derivative uses the element family to determine whether id, grad, curl or div should be used. Note that this uses the 'grad' and 'div' operators, as opposed to 'nabla_grad' and 'nabla_div'. """ # Extract the element from the input f if isinstance(f, Indexed): if len(f._indices) > 1: raise NotImplementedError index = int(f._indices[0]) element = f._expression.element() element = element.extract_component(index)[1] elif isinstance(f, ListTensor): if len(f._expressions[0]._indices) > 1: raise NotImplementedError index = int(f._expressions[0]._indices[0]) element = f._expressions[0]._expression.element() element = element.extract_component(index)[1] else: try: element = f.element() except: error("Unable to determine element from %s" % f) # Extract the family and the family = element.family() gdim = element.cell().geometric_dimension() # L^2 elements: if "Disc" in family: return f # H^1 elements: if "Lagrange" in family: if gdim == 1: return grad(f)[0] # Special-case 1D vectors as scalars return grad(f) # H(curl) elements: if "curl" in family: return curl(f) # H(div) elements: if "Brezzi" in family or "Raviart" in family: return div(f) error("Unable to determine exterior_derivative. Family is '%s'" % family) ufl-1.3.0/ufl/permutation.py000066400000000000000000000077451226300046600160240ustar00rootroot00000000000000"""This module provides utility functions for computing permutations and generating index lists.""" # Copyright (C) 2008-2013 Anders Logg and Kent-Andre Mardal # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Martin Alnes 2009 # # First added: 2008-05-22 # Last changed: 2011-06-08 def compute_indices(shape): "Compute all index combinations for given shape" if len(shape) == 0: return ((),) sub_indices = compute_indices(shape[1:]) indices = [] for i in xrange(shape[0]): for sub_index in sub_indices: indices.append((i,) + sub_index) return tuple(indices) # functional version: def compute_indices2(shape): "Compute all index combinations for given shape" return ((),) if len(shape) == 0 else tuple((i,) + sub_index for i in xrange(shape[0]) for sub_index in compute_indices2(shape[1:])) def build_component_numbering(shape, symmetry): """Build a numbering of components within the given value shape, taking into consideration a symmetry mapping which leaves the mapping noncontiguous. Returns a dict { component -> numbering } and an ordered list of components [ numbering -> component ]. The dict contains all components while the list only contains the ones not mapped by the symmetry mapping.""" vi2si, si2vi = {}, [] indices = compute_indices(shape) # Number components not in symmetry mapping for c in indices: if c not in symmetry: vi2si[c] = len(si2vi) si2vi.append(c) # Copy numbering to mapped components for c in indices: if c in symmetry: vi2si[c] = vi2si[symmetry[c]] # Validate for k, c in enumerate(si2vi): assert vi2si[c] == k return vi2si, si2vi def compute_permutations(k, n, skip = None): """Compute all permutations of k elements from (0, n) in rising order. Any elements that are contained in the list skip are not included.""" if k == 0: return [] if skip is None: skip = [] if k == 1: return [(i,) for i in xrange(n) if not i in skip] pp = compute_permutations(k - 1, n, skip) permutations = [] for i in xrange(n): if i in skip: continue for p in pp: if i < p[0]: permutations.append((i,) + p) return permutations def compute_permutation_pairs(j, k): """Compute all permutations of j + k elements from (0, j + k) in rising order within (0, j) and (j, j + k) respectively.""" permutations = [] pp0 = compute_permutations(j, j + k) for p0 in pp0: pp1 = compute_permutations(k, j + k, p0) for p1 in pp1: permutations.append((p0, p1)) return permutations def compute_sign(permutation): "Compute sign by sorting." sign = 1 n = len(permutation) p = [p for p in permutation] for i in xrange(n - 1): for j in xrange(n - 1): if p[j] > p[j + 1]: (p[j], p[j + 1]) = (p[j + 1], p[j]) sign = -sign elif p[j] == p[j + 1]: return 0 return sign def compute_order_tuples(k, n): "Compute all tuples of n integers such that the sum is k" if n == 1: return ((k,),) order_tuples = [] for i in xrange(k + 1): for order_tuple in compute_order_tuples(k - i, n - 1): order_tuples.append(order_tuple + (i,)) return tuple(order_tuples) ufl-1.3.0/ufl/precedence.py000066400000000000000000000070661226300046600155460ustar00rootroot00000000000000"Precedence handling." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2009-03-27 # Last changed: 2011-06-07 from ufl.log import warning def parstr(child, parent, pre="(", post=")", format=str): # Execute when needed instead of on import, # which leads to all kinds of circular trouble. # Fixing this could be an optimization of str(expr) though. if not hasattr(parent, '_precedence'): assign_precedences(build_precedence_list()) # We want child to be evaluated fully first, # and if the parent has higher precedence # we later wrap in (). s = format(child) # Operators where operands are always parenthesized if parent._precedence == 0: return pre + s + post # If parent operator binds stronger than child, must parenthesize child # FIXME: Is this correct for all possible positions of () in a + b + c? if parent._precedence > child._precedence: return pre + s + post # Nothing needed return s def build_precedence_list(): from ufl.classes import Operator, Terminal, Sum, IndexSum, Product, Division, Power, MathFunction, Abs # TODO: Fill in other types... #Power <= Indexed #Power <= Transposed precedence_list = [] # Default behaviour: should always add parentheses precedence_list.append((Operator,)) precedence_list.append((Sum,)) # sum_i a + b = (sum_i a) + b != sum_i (a + b), sum_i binds stronger than +, but weaker than product precedence_list.append((IndexSum,)) precedence_list.append((Product, Division,)) # NB! Depends on language! precedence_list.append((Power, MathFunction, Abs)) precedence_list.append((Terminal,)) return precedence_list def build_precedence_mapping(precedence_list): """Given a precedence list, build a dict with class->int mappings. Utility function used by some external code. """ from ufl.classes import Expr, all_ufl_classes, abstract_classes pm = {} missing = set() # Assign integer values for each precedence level k = 0 for p in precedence_list: for c in p: pm[c] = k k += 1 # Check for missing classes, fill in subclasses for c in all_ufl_classes: if c not in abstract_classes and c not in pm: b = c.__bases__[0] while b is not Expr: if b in pm: pm[c] = pm[b] break b = b.__bases__[0] if c not in pm: missing.add(c) return pm, missing def assign_precedences(precedence_list): "Given a precedence list, assign ints to class._precedence." pm, missing = build_precedence_mapping(precedence_list) for c, p in sorted(pm.iteritems(), key=lambda x: x[0].__name__): c._precedence = p if missing: msg = "Missing precedence levels for classes:\n" +\ "\n".join(' %s' % c for c in sorted(missing)) warning(msg) ufl-1.3.0/ufl/restriction.py000066400000000000000000000071111226300046600160050ustar00rootroot00000000000000"""Restriction operations.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-06-08 # Last changed: 2011-06-02 from ufl.log import error from ufl.operatorbase import Operator from ufl.precedence import parstr from ufl.common import EmptyDict #--- Restriction operators --- class Restricted(Operator): __slots__ = ("_f", "_side") # TODO: Add __new__ operator here, e.g. restricted(literal) == literal def __init__(self, f, side): Operator.__init__(self) self._f = f self._side = side def shape(self): return self._f.shape() def operands(self): return (self._f,) def free_indices(self): return self._f.free_indices() def index_dimensions(self): return self._f.index_dimensions() def evaluate(self, x, mapping, component, index_values): return self._f.evaluate(x, mapping, component, index_values) def __str__(self): return "%s('%s')" % (parstr(self._f, self), self._side) class PositiveRestricted(Restricted): __slots__ = () def __init__(self, f): Restricted.__init__(self, f, "+") def __repr__(self): return "PositiveRestricted(%r)" % self._f class NegativeRestricted(Restricted): __slots__ = () def __init__(self, f): Restricted.__init__(self, f, "-") def __repr__(self): return "NegativeRestricted(%r)" % self._f # TODO: Place in a better file? class CellAvg(Operator): __slots__ = ("_f",) # TODO: Add __new__ operator here, e.g. cell_avg(literal) == literal def __init__(self, f): Operator.__init__(self) self._f = f def shape(self): return self._f.shape() def operands(self): return (self._f,) def free_indices(self): return () def index_dimensions(self): return EmptyDict def evaluate(self, x, mapping, component, index_values): "Performs an approximate symbolic evaluation, since we dont have a cell." return self._f.evaluate(x, mapping, component, index_values) def __str__(self): return "cell_avg(%s)" % (self._f,) def __repr__(self): return "CellAvg(%r)" % self._f # TODO: Place in a better file? class FacetAvg(Operator): __slots__ = ("_f",) # TODO: Add __new__ operator here, e.g. facet_avg(literal) == literal def __init__(self, f): Operator.__init__(self) self._f = f def shape(self): return self._f.shape() def operands(self): return (self._f,) def free_indices(self): return () def index_dimensions(self): return EmptyDict def evaluate(self, x, mapping, component, index_values): "Performs an approximate symbolic evaluation, since we dont have a cell." return self._f.evaluate(x, mapping, component, index_values) def __str__(self): return "facet_avg(%s)" % (self._f,) def __repr__(self): return "FacetAvg(%r)" % self._f ufl-1.3.0/ufl/sorting.py000066400000000000000000000114441226300046600151310ustar00rootroot00000000000000"""This module contains a sorting rule for expr objects that is more robust w.r.t. argument numbering than using repr.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2009-2010. # Modified by Johan Hake, 2010. # # First added: 2008-11-26 # Last changed: 2013-01-02 from itertools import izip from ufl.log import error from ufl.terminal import Terminal, FormArgument from ufl.indexing import Index, FixedIndex, MultiIndex from ufl.variable import Label def _cmp3(a, b): "Replacement for cmp(), removed in Python 3." # TODO: Which is faster? return -1 if (a < b) else (+1 if a > b else 0) #return (a > b) - (a < b) def cmp_expr(a, b): "Sorting rule for Expr objects." # First sort quickly by type name c = _cmp3(a._uflclass.__name__, b._uflclass.__name__) if c != 0: return c # Type is the same, but is it a ... # ... a MultiIndex? Careful not to depend on Index.count() here! if isinstance(a, MultiIndex): for i,j in izip(a._indices, b._indices): if isinstance(i, FixedIndex): if isinstance(j, FixedIndex): # Both are FixedIndex, sort by value c = _cmp3(i._value, j._value) if c: return c else: return +1 else: if isinstance(j, FixedIndex): return -1 else: pass # Both are Index, do not depend on count! # Failed to make a decision, return 0 by default return 0 # ... Label object? elif isinstance(a, Label): # Don't compare counts! Causes circular problems when renumbering to get a canonical form. # Therefore, even though a and b are not equal in general (__eq__ won't be True), # but for this sorting they are considered equal and we return 0. return 0 # ... Other Counted object? (Coefficient or Argument) elif isinstance(a, FormArgument): # It's ok to compare relative counts for form arguments, # since their ordering is a property of the form return _cmp3(a._count, b._count) # ... another kind of Terminal object? elif isinstance(a, Terminal): # The cost of repr on a terminal is fairly small, and bounded return _cmp3(repr(a), repr(b)) # Not a terminal, sort by number of children (usually the same) aops = a.operands() bops = b.operands() c = _cmp3(len(aops), len(bops)) if c != 0: return c # Sort by children in natural order for (r, s) in izip(aops, bops): c = cmp_expr(r, s) # Ouch! This becomes worst case O(n) then? FIXME: Perhaps replace with comparison of hash value? Is that stable between runs? if c != 0: return c # All children compare as equal, a and b must be equal return 0 # Not in python 2.6... #from functools import cmp_to_key class ExprKey(object): __slots__ = ('x',) def __init__(self, x): self.x = x def __lt__(self, other): return cmp_expr(self.x, other.x) < 0 def expr_key(expr): return ExprKey(expr) def sorted_expr(seq): return sorted(seq, key=expr_key) def sorted_expr_sum(seq): seq2 = sorted(seq, key=expr_key) s = seq2[0] for e in seq2[1:]: s = s + e return s # TODO: Move this to common.py, does not belong here def topological_sorting(nodes, edges): """ Return a topologically sorted list of the nodes Implemented algorithm from Wikipedia :P No error for cyclic edges... """ L = [] S = nodes[:] for node in nodes: for es in edges.itervalues(): if node in es and node in S: S.remove(node) continue while S: node = S.pop(0) L.append(node) node_edges = edges[node] while node_edges: m = node_edges.pop(0) found = False for es in edges.itervalues(): found = m in es if found: break if not found: S.insert(0,m) return L ufl-1.3.0/ufl/split_functions.py000066400000000000000000000114541226300046600166700ustar00rootroot00000000000000"Algorithm for splitting a Coefficient or Argument into subfunctions." # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-03-14 # Last changed: 2011-06-22 from ufl.log import error from ufl.assertions import ufl_assert from ufl.common import product, EmptyDict from ufl.finiteelement import MixedElement, TensorElement from ufl.tensors import as_vector, as_matrix, as_tensor def split(v): """UFL operator: If v is a Coefficient or Argument in a mixed space, returns a tuple with the function components corresponding to the subelements.""" # Special case: simple element, just return function in a tuple element = v.element() if not isinstance(element, MixedElement): return (v,) if isinstance(element, TensorElement): s = element.symmetry() if s: # FIXME: How should this be defined? Should we return one subfunction # for each value component or only for those not mapped to another? # I think split should ignore the symmetry. error("Split not implemented for symmetric tensor elements.") # Compute value size value_size = product(element.value_shape()) actual_value_size = value_size # Extract sub coefficient offset = 0 sub_functions = [] for i, e in enumerate(element.sub_elements()): shape = e.value_shape() rank = len(shape) if rank == 0: # This subelement is a scalar, always maps to a single value subv = v[offset] offset += 1 elif rank == 1: # This subelement is a vector, always maps to a sequence of values sub_size, = shape components = [v[j] for j in range(offset, offset + sub_size)] subv = as_vector(components) offset += sub_size elif rank == 2: # This subelement is a tensor, possibly with symmetries, slightly more complicated... # Size of this subvalue sub_size = product(shape) # If this subelement is a symmetric element, subtract symmetric components s = None if isinstance(e, TensorElement): s = e.symmetry() s = s or EmptyDict # If we do this, we must fix the size computation in MixedElement.__init__ as well #actual_value_size -= len(s) #sub_size -= len(s) #print s # Build list of lists of value components components = [] for ii in range(shape[0]): row = [] for jj in range(shape[1]): # Map component (i,j) through symmetry mapping c = (ii, jj) c = s.get(c, c) i, j = c # Extract component c of this subvalue from global tensor v if v.rank() == 1: # Mapping into a flattened vector k = offset + i*shape[1] + j component = v[k] #print "k, offset, i, j, shape, component", k, offset, i, j, shape, component elif v.rank() == 2: # Mapping into a concatenated tensor (is this a figment of my imagination?) error("Not implemented.") row_offset, col_offset = 0, 0 # TODO k = (row_offset + i, col_offset + j) component = v[k] row.append(component) components.append(row) # Make a matrix of the components subv = as_matrix(components) offset += sub_size else: # TODO: Handle rank > 2? Or is there such a thing? error("Don't know how to split functions with sub functions of rank %d (yet)." % rank) #for indices in compute_indices(shape): # #k = offset + sum(i*s for (i,s) in izip(indices, shape[1:] + (1,))) # vs.append(v[indices]) sub_functions.append(subv) ufl_assert(actual_value_size == offset, "Logic breach in function splitting.") return tuple(sub_functions) ufl-1.3.0/ufl/tensoralgebra.py000066400000000000000000000404611226300046600162750ustar00rootroot00000000000000"""Compound tensor algebra operations.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-14 # Last changed: 2013-01-02 from ufl.log import warning from ufl.assertions import ufl_assert from ufl.constantvalue import Zero from ufl.algebra import AlgebraOperator from ufl.precedence import parstr from ufl.sorting import sorted_expr from ufl.common import EmptyDict def merge_indices(a, b): ai = a.free_indices() bi = b.free_indices() #free_indices = unique_indices(ai + bi) # if repeated indices are allowed, do this instead of the next three lines ri = set(ai) & set(bi) ufl_assert(not ri, "Not expecting repeated indices.") free_indices = ai+bi aid = a.index_dimensions() bid = b.index_dimensions() index_dimensions = dict(aid) index_dimensions.update(bid) return free_indices, index_dimensions ### Algebraic operations on tensors: # FloatValues: # dot(a,b) = a*b # inner(a,b) = a*b # outer(a,b) = a*b # Vectors: # dot(u,v) = u_i v_i # inner(u,v) = u_i v_i # outer(u,v) = A | A_ij = u_i v_j # Matrices: # dot(A,B) = C | C_ij = A_{ik} B_{kj} # inner(A,B) = A_{ij} B_{ij} # outer(A,B) = C | C_ijkl = A_ij B_kl # Combined: # dot(A,u) = v | v_i = A_{ik} u_k # inner(A,u) = v | v_i = A_{ik} u_k # outer(A,u) = C | C_ijk = B_ij u_k # dot(u,B) = v | v_i = u_k B_{ki} # inner(u,B) = v | v_i = u_k B_{ki} # outer(u,B) = C | C_ijk = u_i B_jk # # Argument requirements: # dot(x,y): last index of x has same dimension as first index of y # inner(x,y): shape of x equals the shape of y # --- Classes representing compound tensor algebra operations --- class CompoundTensorOperator(AlgebraOperator): __slots__ = () def __init__(self): AlgebraOperator.__init__(self) # TODO: Use this and make Sum handle scalars only? # This would simplify some algorithms. The only # problem is we can't use + in many algorithms because # this type should be expanded by expand_compounds. #class TensorSum(CompoundTensorOperator): # "Sum of nonscalar expressions." # pass # TODO: Use this similarly to TensorSum? # This would simplify some algorithms. The only # problem is we can't use / in many algorithms because # this type should be expanded by expand_compounds. #class TensorDivision(CompoundTensorOperator): # "Division of nonscalar expression with a scalar expression." # pass # TODO: Use this similarly to TensorSum? # This would simplify some algorithms. The only # problem is we can't use * in many algorithms because # this type should be expanded by expand_compounds. #class MatrixProduct(CompoundTensorOperator): # "Product of a matrix with a matrix or vector." # pass # TODO: Use this similarly to TensorSum? # This would simplify some algorithms. The only # problem is we can't use abs in many algorithms because # this type should be expanded by expand_compounds. #class TensorAbs(CompoundTensorOperator): # "Absolute value of nonscalar expression." # pass class Transposed(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): if isinstance(A, Zero): a, b = A.shape() return Zero((b, a), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) ufl_assert(A.rank() == 2, "Transposed is only defined for rank 2 tensors.") self._A = A def operands(self): return (self._A,) def free_indices(self): return self._A.free_indices() def index_dimensions(self): return self._A.index_dimensions() def shape(self): s = self._A.shape() return (s[1], s[0]) def __str__(self): return "%s^T" % parstr(self._A, self) def __repr__(self): return "Transposed(%r)" % self._A class Outer(CompoundTensorOperator): __slots__ = ("_a", "_b", "_free_indices", "_index_dimensions") def __new__(cls, a, b): ash, bsh = a.shape(), b.shape() if isinstance(a, Zero) or isinstance(b, Zero): free_indices, index_dimensions = merge_indices(a, b) return Zero(ash + bsh, free_indices, index_dimensions) if ash == () or bsh == (): return a * b return CompoundTensorOperator.__new__(cls) def __init__(self, a, b): CompoundTensorOperator.__init__(self) self._a = a self._b = b self._free_indices, self._index_dimensions = merge_indices(a, b) def operands(self): return (self._a, self._b) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return self._a.shape() + self._b.shape() def __str__(self): return "%s (X) %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Outer(%r, %r)" % (self._a, self._b) class Inner(CompoundTensorOperator): __slots__ = ("_a", "_b", "_free_indices", "_index_dimensions") def __new__(cls, a, b): ash, bsh = a.shape(), b.shape() ufl_assert(ash == bsh, "Shape mismatch.") if isinstance(a, Zero) or isinstance(b, Zero): free_indices, index_dimensions = merge_indices(a, b) return Zero((), free_indices, index_dimensions) if ash == (): return a*b return CompoundTensorOperator.__new__(cls) def __init__(self, a, b): CompoundTensorOperator.__init__(self) # sort operands for unique representation, # must be independent of various counts etc. # as explained in cmp_expr a, b = sorted_expr((a,b)) # old version, slow and unsafe: #a, b = sorted((a,b), key = lambda x: repr(x)) self._a = a self._b = b self._free_indices, self._index_dimensions = merge_indices(a, b) def operands(self): return (self._a, self._b) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return () def __str__(self): return "%s : %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Inner(%r, %r)" % (self._a, self._b) class Dot(CompoundTensorOperator): __slots__ = ("_a", "_b", "_free_indices", "_index_dimensions") def __new__(cls, a, b): ash = a.shape() bsh = b.shape() ar, br = len(ash), len(bsh) scalar = (ar == 0 and br == 0) ufl_assert((ar >= 1 and br >= 1) or scalar, "Dot product requires non-scalar arguments, "\ "got arguments with ranks %d and %d." % \ (ar, br)) ufl_assert(scalar or ash[-1] == bsh[0], "Dimension mismatch in dot product.") if isinstance(a, Zero) or isinstance(b, Zero): shape = ash[:-1] + bsh[1:] free_indices, index_dimensions = merge_indices(a, b) return Zero(shape, free_indices, index_dimensions) if scalar: # TODO: Move this to def dot()? return a * b return CompoundTensorOperator.__new__(cls) def __init__(self, a, b): CompoundTensorOperator.__init__(self) self._a = a self._b = b self._free_indices, self._index_dimensions = merge_indices(a, b) def operands(self): return (self._a, self._b) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return self._a.shape()[:-1] + self._b.shape()[1:] def __str__(self): return "%s . %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Dot(%r, %r)" % (self._a, self._b) class Cross(CompoundTensorOperator): __slots__ = ("_a", "_b", "_free_indices", "_index_dimensions") def __new__(cls, a, b): ash, bsh = a.shape(), b.shape() ufl_assert(len(ash) == 1 and ash == bsh, "Cross product requires arguments of rank 1.") if isinstance(a, Zero) or isinstance(b, Zero): free_indices, index_dimensions = merge_indices(a, b) return Zero(ash, free_indices, index_dimensions) return CompoundTensorOperator.__new__(cls) def __init__(self, a, b): CompoundTensorOperator.__init__(self) self._a = a self._b = b self._free_indices, self._index_dimensions = merge_indices(a, b) def operands(self): return (self._a, self._b) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return (3,) def __str__(self): return "%s x %s" % (parstr(self._a, self), parstr(self._b, self)) def __repr__(self): return "Cross(%r, %r)" % (self._a, self._b) class Trace(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): ufl_assert(A.rank() == 2, "Trace of tensor with rank != 2 is undefined.") if isinstance(A, Zero): return Zero((), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return self._A.free_indices() def index_dimensions(self): return self._A.index_dimensions() def shape(self): return () def __str__(self): return "tr(%s)" % self._A def __repr__(self): return "Trace(%r)" % self._A class Determinant(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): sh = A.shape() r = len(sh) ufl_assert(r == 0 or r == 2, "Determinant of tensor with rank != 2 is undefined.") ufl_assert(r == 0 or sh[0] == sh[1], "Cannot take determinant of rectangular rank 2 tensor.") ufl_assert(not A.free_indices(), "Not expecting free indices in determinant.") if isinstance(A, Zero): return Zero((), A.free_indices(), A.index_dimensions()) if r == 0: return A return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return () def __str__(self): return "det(%s)" % self._A def __repr__(self): return "Determinant(%r)" % self._A # TODO: Drop Inverse and represent it as product of Determinant and Cofactor? class Inverse(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): sh = A.shape() r = len(sh) if A.free_indices(): error("Not expecting free indices in Inverse.") if isinstance(A, Zero): error("Division by zero!") if r == 0: return 1 / A if r != 2: error("Inverse of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take inverse of rectangular matrix with dimensions %s." % repr(sh)) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return self._A.shape() def __str__(self): return "%s^-1" % parstr(self._A, self) def __repr__(self): return "Inverse(%r)" % self._A class Cofactor(CompoundTensorOperator): __slots__ = ("_A",) def __init__(self, A): CompoundTensorOperator.__init__(self) sh = A.shape() ufl_assert(len(sh) == 2, "Cofactor of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take cofactor of rectangular matrix with dimensions %s." % repr(sh)) ufl_assert(not A.free_indices(), "Not expecting free indices in Cofactor.") ufl_assert(not isinstance(A, Zero), "Cannot take cofactor of zero matrix.") self._A = A def operands(self): return (self._A, ) def free_indices(self): return () def index_dimensions(self): return EmptyDict def shape(self): return self._A.shape() def __str__(self): return "cofactor(%s)" % self._A def __repr__(self): return "Cofactor(%r)" % self._A class Deviatoric(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): sh = A.shape() ufl_assert(len(sh) == 2, "Deviatoric part of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take deviatoric part of rectangular matrix with dimensions %s." % repr(sh)) ufl_assert(not A.free_indices(), "Not expecting free indices in Deviatoric.") if isinstance(A, Zero): return Zero(A.shape(), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return self._A.free_indices() def index_dimensions(self): return self._A.index_dimensions() def shape(self): return self._A.shape() def __str__(self): return "dev(%s)" % self._A def __repr__(self): return "Deviatoric(%r)" % self._A class Skew(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): sh = A.shape() ufl_assert(len(sh) == 2, "Skew symmetric part of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take skew part of rectangular matrix with dimensions %s." % repr(sh)) ufl_assert(not A.free_indices(), "Not expecting free indices in Skew.") if isinstance(A, Zero): return Zero(A.shape(), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return self._A.free_indices() def index_dimensions(self): return self._A.index_dimensions() def shape(self): return self._A.shape() def __str__(self): return "skew(%s)" % self._A def __repr__(self): return "Skew(%r)" % self._A class Sym(CompoundTensorOperator): __slots__ = ("_A",) def __new__(cls, A): sh = A.shape() ufl_assert(len(sh) == 2, "Symmetric part of tensor with rank != 2 is undefined.") if sh[0] != sh[1]: error("Cannot take symmetric part of rectangular matrix with dimensions %s." % repr(sh)) ufl_assert(not A.free_indices(), "Not expecting free indices in Sym.") if isinstance(A, Zero): return Zero(A.shape(), A.free_indices(), A.index_dimensions()) return CompoundTensorOperator.__new__(cls) def __init__(self, A): CompoundTensorOperator.__init__(self) self._A = A def operands(self): return (self._A, ) def free_indices(self): return self._A.free_indices() def index_dimensions(self): return self._A.index_dimensions() def shape(self): return self._A.shape() def __str__(self): return "sym(%s)" % self._A def __repr__(self): return "Sym(%r)" % self._A ufl-1.3.0/ufl/tensors.py000066400000000000000000000377661226300046600151600ustar00rootroot00000000000000"""Classes used to group scalar expressions into expressions with rank > 0.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-03-31 # Last changed: 2011-10-17 from itertools import izip from ufl.log import warning, error from ufl.common import subdict, EmptyDict from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.operatorbase import WrapperType from ufl.constantvalue import as_ufl, Zero from ufl.indexing import Index, FixedIndex, MultiIndex, indices from ufl.indexed import Indexed # --- Classes representing tensors of UFL expressions --- class ListTensor(WrapperType): """UFL operator type: Wraps a list of expressions into a tensor valued expression of one higher rank.""" __slots__ = ("_expressions", "_free_indices", "_shape") def __new__(cls, *expressions): # All lists and tuples should already be unwrapped in as_tensor if any(not isinstance(e, Expr) for e in expressions): error("Expecting only UFL expressions in ListTensor constructor.") # Get properties of the first expression e0 = expressions[0] sh = e0.shape() fi = e0.free_indices() idim = e0.index_dimensions() # Obviously, each subextpression must have the same shape if any(sh != e.shape() for e in expressions): error("ListTensor assumption 1 failed, "\ "please report this incident as a potential bug.") # Are these assumptions correct? Need to think # through the listtensor concept and free indices. # Are there any cases where it makes sense to even # have any free indices here? if any(set(fi) - set(e.free_indices()) for e in expressions): error("ListTensor assumption 2 failed, "\ "please report this incident as a potential bug.") if any(idim != e.index_dimensions() for e in expressions): error("ListTensor assumption 3 failed, "\ "please report this incident as a potential bug.") # Simplify to Zero if possible if all(isinstance(e, Zero) for e in expressions): shape = (len(expressions),) + sh return Zero(shape, fi, idim) return WrapperType.__new__(cls) def __init__(self, *expressions): WrapperType.__init__(self) e0 = expressions[0] sh = e0.shape() self._shape = (len(expressions),) + sh self._expressions = tuple(expressions) indexset = set(e0.free_indices()) ufl_assert(all(not (indexset ^ set(e.free_indices())) for e in self._expressions),\ "Can't combine subtensor expressions with different sets of free indices.") def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return all(e.is_cellwise_constant() for e in self.operands()) def operands(self): return self._expressions def free_indices(self): return self._expressions[0].free_indices() def index_dimensions(self): return self._expressions[0].index_dimensions() def shape(self): return self._shape def evaluate(self, x, mapping, component, index_values, derivatives=()): ufl_assert(len(component) == len(self._shape), "Can only evaluate scalars, expecting a component "\ "tuple of length %d, not %s." % (len(self._shape), component)) a = self._expressions[component[0]] component = component[1:] if derivatives: return a.evaluate(x, mapping, component, index_values, derivatives) else: return a.evaluate(x, mapping, component, index_values) def __getitem__(self, key): origkey = key if isinstance(key, MultiIndex): key = key._indices if not isinstance(key, tuple): key = (key,) k = key[0] if isinstance(k, (int, FixedIndex)): sub = self._expressions[int(k)] return sub if len(key) == 1 else sub[key[1:]] return Expr.__getitem__(self, origkey) def __str__(self): def substring(expressions, indent): ind = " "*indent if any(isinstance(e, ListTensor) for e in expressions): substrings = [] for e in expressions: if isinstance(e, ListTensor): substrings.append(substring(e._expressions, indent+2)) else: substrings.append(str(e)) s = (",\n" + ind).join(substrings) return "%s[\n%s%s\n%s]" % (ind, ind, s, ind) else: s = ", ".join(str(e) for e in expressions) return "%s[%s]" % (ind, s) return substring(self._expressions, 0) def __repr__(self): return "ListTensor(%s)" % ", ".join(repr(e) for e in self._expressions) class ComponentTensor(WrapperType): """UFL operator type: Maps the free indices of a scalar valued expression to tensor axes.""" __slots__ = ("_expression", "_indices", "_free_indices", "_index_dimensions", "_shape") def __new__(cls, expression, indices): if isinstance(expression, Zero): if isinstance(indices, MultiIndex): indices = tuple(indices) elif not isinstance(indices, tuple): indices = (indices,) dims = expression.index_dimensions() shape = tuple(dims[i] for i in indices) fi = tuple(set(expression.free_indices()) - set(indices)) idim = dict((i, dims[i]) for i in fi) return Zero(shape, fi, idim) return WrapperType.__new__(cls) def __init__(self, expression, indices): WrapperType.__init__(self) ufl_assert(isinstance(expression, Expr), "Expecting ufl expression.") ufl_assert(expression.shape() == (), "Expecting scalar valued expression.") self._expression = expression ufl_assert(all(isinstance(i, Index) for i in indices), "Expecting sequence of Index objects, not %s." % repr(indices)) dims = expression.index_dimensions() if not isinstance(indices, MultiIndex): # if constructed from repr indices = MultiIndex(indices, subdict(dims, indices)) self._indices = indices eset = set(expression.free_indices()) iset = set(self._indices) freeset = eset - iset self._free_indices = tuple(freeset) missingset = iset - eset if missingset: error("Missing indices %s in expression %s." % (missingset, expression)) self._index_dimensions = dict((i, dims[i]) for i in self._free_indices) or EmptyDict self._shape = tuple(dims[i] for i in self._indices) def is_cellwise_constant(self): "Return whether this expression is spatially constant over each cell." return self._expression.is_cellwise_constant() def reconstruct(self, expressions, indices): # Special case for simplification as_tensor(A[ii], ii) -> A if isinstance(expressions, Indexed): A, ii = expressions.operands() if indices == ii: #print "RETURNING", A, "FROM", expressions, indices, "SELF IS", self return A return WrapperType.reconstruct(self, expressions, indices) def operands(self): return (self._expression, self._indices) def free_indices(self): return self._free_indices def index_dimensions(self): return self._index_dimensions def shape(self): return self._shape def evaluate(self, x, mapping, component, index_values): indices = self._indices a = self._expression ufl_assert(len(indices) == len(component), "Expecting a component matching the indices tuple.") # Map component to indices for i, c in izip(indices, component): index_values.push(i, c) a = a.evaluate(x, mapping, (), index_values) for _ in component: index_values.pop() return a def __str__(self): return "{ A | A_{%s} = %s }" % (self._indices, self._expression) def __repr__(self): return "ComponentTensor(%r, %r)" % (self._expression, self._indices) # --- User-level functions to wrap expressions in the correct way --- def numpy2nestedlists(arr): from numpy import ndarray if not isinstance(arr, ndarray): return arr return [numpy2nestedlists(arr[k]) for k in range(arr.shape[0])] def _as_list_tensor(expressions): if isinstance(expressions, (list, tuple)): expressions = [_as_list_tensor(e) for e in expressions] return ListTensor(*expressions) else: return as_ufl(expressions) def from_numpy_to_lists(expressions): try: import numpy if isinstance(expressions, numpy.ndarray): expressions = numpy2nestedlists(expressions) except: pass return expressions def as_tensor(expressions, indices = None): """UFL operator: Make a tensor valued expression. This works in two different ways, by using indices or lists. 1) Returns A such that A[indices] = expressions. If indices are provided, expressions must be a scalar valued expression with all the provided indices among its free indices. This operator will then map each of these indices to a tensor axis, thereby making a tensor valued expression from a scalar valued expression with free indices. 2) Returns A such that A[k,...] = expressions[k]. If no indices are provided, expressions must be a list or tuple of expressions. The expressions can also consist of recursively nested lists to build higher rank tensors. """ if indices is None: # Allow as_tensor(as_tensor(A)) and as_vector(as_vector(v)) in user code if isinstance(expressions, Expr): return expressions # Support numpy array, but avoid importing numpy if not needed if not isinstance(expressions, (list, tuple)): expressions = from_numpy_to_lists(expressions) # Sanity check if not isinstance(expressions, (list, tuple)): error("Expecting nested list or tuple.") # Recursive conversion from nested lists to nested ListTensor objects return _as_list_tensor(expressions) else: # Make sure we have a tuple of indices if isinstance(indices, list): indices = tuple(indices) elif not isinstance(indices, tuple): indices = (indices,) # Special case for as_tensor(expr, ii) with ii = () if indices == (): return expressions # Special case for simplification as_tensor(A[ii], ii) -> A if isinstance(expressions, Indexed): A, ii = expressions.operands() if indices == ii._indices: return A # Make a tensor from given scalar expression with free indices return ComponentTensor(expressions, indices) def as_matrix(expressions, indices = None): "UFL operator: As as_tensor(), but limited to rank 2 tensors." if indices is None: # Allow as_matrix(as_matrix(A)) in user code if isinstance(expressions, Expr): ufl_assert(expressions.rank() == 2, "Expecting rank 2 tensor.") return expressions # To avoid importing numpy unneeded, it's quite slow... if not isinstance(expressions, (list, tuple)): expressions = from_numpy_to_lists(expressions) # Check for expected list structure ufl_assert(isinstance(expressions, (list, tuple)), "Expecting nested list or tuple of Exprs.") ufl_assert(isinstance(expressions[0], (list, tuple)), "Expecting nested list or tuple of Exprs.") else: ufl_assert(len(indices) == 2, "Expecting exactly two indices.") return as_tensor(expressions, indices) def as_vector(expressions, index = None): "UFL operator: As as_tensor(), but limited to rank 1 tensors." if index is None: # Allow as_vector(as_vector(v)) in user code if isinstance(expressions, Expr): ufl_assert(expressions.rank() == 1, "Expecting rank 1 tensor.") return expressions # To avoid importing numpy unneeded, it's quite slow... if not isinstance(expressions, (list, tuple)): expressions = from_numpy_to_lists(expressions) # Check for expected list structure ufl_assert(isinstance(expressions, (list, tuple)), "Expecting nested list or tuple of Exprs.") else: ufl_assert(isinstance(index, Index), "Expecting a single Index object.") index = (index,) return as_tensor(expressions, index) def as_scalar(expression): """Given a scalar or tensor valued expression A, returns either of the tuples:: (a,b) = (A, ()) (a,b) = (A[indices], indices) such that a is always a scalar valued expression.""" ii = indices(expression.rank()) if ii: expression = expression[ii] return expression, ii def relabel(A, indexmap): "UFL operator: Relabel free indices of A with new indices, using the given mapping." ii = tuple(sorted(indexmap.keys())) jj = tuple(indexmap[i] for i in ii) ufl_assert(all(isinstance(i, Index) for i in ii), "Expecting Index objects.") ufl_assert(all(isinstance(j, Index) for j in jj), "Expecting Index objects.") return as_tensor(A, ii)[jj] # --- Experimental support for dyadic notation: def unit_list(i, n): return [(1 if i == j else 0) for j in xrange(n)] def unit_list2(i, j, n): return [[(1 if (i == i0 and j == j0) else 0) for j0 in xrange(n)] for i0 in xrange(n)] def unit_vector(i, d): "UFL value: A constant unit vector in direction i with dimension d." return as_vector(unit_list(i, d)) def unit_vectors(d): "UFL value: A tuple of constant unit vectors in all directions with dimension d." return tuple(unit_vector(i, d) for i in range(d)) def unit_matrix(i, j, d): "UFL value: A constant unit matrix in direction i,j with dimension d." return as_matrix(unit_list2(i, j, d)) def unit_matrices(d): "UFL value: A tuple of constant unit matrices in all directions with dimension d." return tuple(unit_matrix(i, j, d) for i in range(d) for j in range(d)) def dyad(d, *iota): "TODO: Develop this concept, can e.g. write A[i,j]*dyad(j,i) for the transpose." from ufl.constantvalue import Identity from ufl.operators import outer # a bit of circular dependency issue here I = Identity(d) i = iota[0] e = as_vector(I[i,:], i) for i in iota[1:]: e = outer(e, as_vector(I[i,:], i)) return e def unit_indexed_tensor(shape, component): from ufl.constantvalue import Identity from ufl.operators import outer # a bit of circular dependency issue here r = len(shape) if r == 0: return 0, () jj = indices(r) es = [] for i in xrange(r): s = shape[i] c = component[i] j = jj[i] e = Identity(s)[c,j] es.append(e) E = es[0] for e in es[1:]: E = outer(E, e) return E, jj def unwrap_list_tensor(lt): components = [] sh = lt.shape() subs = lt.operands() if len(sh) == 1: for s in xrange(sh[0]): components.append(((s,),subs[s])) else: for s,sub in enumerate(subs): for c,v in unwrap_list_tensor(sub): components.append(((s,)+c,v)) return components ufl-1.3.0/ufl/terminal.py000066400000000000000000000110011226300046600152440ustar00rootroot00000000000000"""This module defines the Terminal class, the superclass for all types that are terminal nodes in the expression trees.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # Modified by Anders Logg, 2008 # # First added: 2008-03-14 # Last changed: 2011-08-08 from ufl.expr import Expr from ufl.log import error, warning from ufl.assertions import ufl_assert from ufl.common import EmptyDict from ufl.common import counted_init #--- Base class for terminal objects --- class Terminal(Expr): "A terminal node in the UFL expression tree." __slots__ = () def __init__(self): Expr.__init__(self) def reconstruct(self, *operands): "Return self." operands and error("Got call to reconstruct in a terminal with non-empty operands.") return self def operands(self): "A Terminal object never has operands." return () def free_indices(self): "A Terminal object never has free indices." return () def index_dimensions(self): "A Terminal object never has free indices." return EmptyDict def evaluate(self, x, mapping, component, index_values, derivatives=()): "Get self from mapping and return the component asked for." f = mapping.get(self) # No mapping, trying to evaluate self as a constant if f is None: try: f = float(self) if derivatives: f = 0.0 return f except: pass # If it has an ufl_evaluate function, call it if hasattr(self, 'ufl_evaluate'): return self.ufl_evaluate(x, component, derivatives) # Take component if any warning("Couldn't map '%s' to a float, returning ufl object without evaluation." % str(self)) f = self if component: f = f[component] return f # Found a callable in the mapping if callable(f): if derivatives: f = f(x, derivatives) else: f = f(x) else: if derivatives: return 0.0 # Take component if any (expecting nested tuple) for c in component: f = f[c] return f def signature_data(self): return repr(self) def __hash__(self): return hash(repr(self)) def __eq__(self, other): "Default comparison of terminals just compare repr strings." return repr(self) == repr(other) #def __getnewargs__(self): # TODO: Test pickle and copy with this. Must implement differently for Terminal objects though. # "Used for pickle and copy operations." # raise NotImplementedError, "Must reimplement in each Terminal, or?" #--- Subgroups of terminals --- class FormArgument(Terminal): __slots__ = () def __init__(self, count=None, countedclass=None): Terminal.__init__(self) counted_init(self, count, countedclass) def count(self): return self._count class UtilityType(Terminal): __slots__ = () def __init__(self): Terminal.__init__(self) def shape(self): error("Calling shape on a utility type is an error.") def free_indices(self): error("Calling free_indices on a utility type is an error.") def index_dimensions(self): error("Calling index_dimensions on a utility type is an error.") def is_cellwise_constant(self): error("Calling is_cellwise_constant on a utility type is an error.") #--- Non-tensor types --- class Data(UtilityType): "For internal use, never to be created by users." __slots__ = ("_data",) def __init__(self, data): UtilityType.__init__(self) self._data = data def __str__(self): return "Data(%s)" % str(self._data) def __repr__(self): return "Data(%r)" % (self._data,) ufl-1.3.0/ufl/testobjects.py000066400000000000000000000007051226300046600157730ustar00rootroot00000000000000"Some premade objects useful for quick testing." from ufl import * cell = triangle element = FiniteElement("CG", cell, 1) v = TestFunction(element) u = TrialFunction(element) f = Coefficient(element) velement = VectorElement("CG", cell, 1) vv = TestFunction(velement) vu = TrialFunction(velement) vf = Coefficient(velement) telement = TensorElement("CG", cell, 1) tv = TestFunction(telement) tu = TrialFunction(telement) tf = Coefficient(telement) ufl-1.3.0/ufl/variable.py000066400000000000000000000064341226300046600152340ustar00rootroot00000000000000"""Defines the Variable and Label classes, used to label expressions as variables for differentiation.""" # Copyright (C) 2008-2013 Martin Sandve Alnes # # This file is part of UFL. # # UFL 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 3 of the License, or # (at your option) any later version. # # UFL 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 UFL. If not, see . # # First added: 2008-05-20 # Last changed: 2011-06-02 from ufl.common import counted_init from ufl.log import error from ufl.assertions import ufl_assert from ufl.expr import Expr from ufl.terminal import UtilityType from ufl.operatorbase import WrapperType from ufl.constantvalue import as_ufl class Label(UtilityType): __slots__ = ("_count",) _globalcount = 0 def __init__(self, count=None): UtilityType.__init__(self) counted_init(self, count, Label) def count(self): return self._count def __str__(self): return "Label(%d)" % self._count def __repr__(self): return "Label(%d)" % self._count class Variable(WrapperType): """A Variable is a representative for another expression. It will be used by the end-user mainly for defining a quantity to differentiate w.r.t. using diff. Example:: e = <...> e = variable(e) f = exp(e**2) df = diff(f, e) """ __slots__ = ("_expression", "_label",) def __init__(self, expression, label=None): WrapperType.__init__(self) expression = as_ufl(expression) ufl_assert(isinstance(expression, Expr), "Expecting Expr.") self._expression = expression if label is None: label = Label() ufl_assert(isinstance(label, Label), "Expecting a Label.") self._label = label def operands(self): return (self._expression, self._label) def free_indices(self): return self._expression.free_indices() def index_dimensions(self): return self._expression.index_dimensions() def shape(self): return self._expression.shape() def cell(self): return self._expression.cell() def domain(self): return self._expression.domain() def is_cellwise_constant(self): return self._expression.is_cellwise_constant() def evaluate(self, x, mapping, component, index_values): a = self._expression.evaluate(x, mapping, component, index_values) return a def expression(self): return self._expression def label(self): return self._label def __eq__(self, other): return isinstance(other, Variable) and self._label == other._label and self._expression == other._expression def __str__(self): return "var%d(%s)" % (self._label.count(), self._expression) def __repr__(self): return "Variable(%r, %r)" % (self._expression, self._label) def __getnewargs__(self): return ()