diff --git a/contrib/overlap/COPYING b/contrib/overlap/COPYING
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/contrib/overlap/COPYING
@@ -0,0 +1,674 @@
+ 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
+.
diff --git a/contrib/overlap/README.md b/contrib/overlap/README.md
new file mode 100644
index 00000000..d1cac1cc
--- /dev/null
+++ b/contrib/overlap/README.md
@@ -0,0 +1,81 @@
+[![Build Status](https://travis-ci.org/severinstrobl/overlap.svg?branch=master)](https://travis-ci.org/severinstrobl/overlap)
+
+# Exact calculation of the overlap volume and area of spheres and mesh elements
+
+Calculating the intersection or overlapping volume of a sphere and one of the
+typically used mesh elements such as a tetrahedron or hexahedron is
+surprisingly challenging. This header-only library implements a numerically
+robust method to determine this volume.
+
+The mathematical expressions and algorithms used in this code are described in
+S. Strobl et al.: Exact calculation of the overlap volume of spheres and mesh
+elements, Journal of Computational Physics, 2016
+(http://dx.doi.org/10.1016/j.jcp.2016.02.003). So if you use the code in
+projects resulting in any publications, please cite this paper.
+
+Employing the concepts and routines used for the calculation of the overlap
+volume, the intersection or overlap *area* of a sphere and the facets of a mesh
+element can also be calculated with this library.
+
+## Dependencies
+
+The compile-time dependencies of this code are:
+- Eigen3 (http://eigen.tuxfamily.org)
+- C++11 compliant compiler
+
+The software was successfully compiled and tested using the following
+compilers:
+- GNU G++ compiler (versions 4.8.4, 4.9.3, and 5.4.0)
+- Intel C++ compiler (version 15.0.2)
+- Clang C++ compiler (versions 3.6.1, 3.9.1, and 5.0.0)
+
+## Usage
+
+The library is implemented as a pure header-only library written in plain
+C++11. To use it in your code, simply include the header file *overlap.hpp* and
+make sure the Eigen3 headers can be found by your compiler or build system. A
+minimal example calculating the overlap of a hexahedron with a side length of 2
+centered at the origin and a sphere with radius 1 centered at a corner of the
+hexahedron could look something like this:
+```
+vector_t v0{-1, -1, -1};
+vector_t v1{ 1, -1, -1};
+vector_t v2{ 1, 1, -1};
+vector_t v3{-1, 1, -1};
+vector_t v4{-1, -1, 1};
+vector_t v5{ 1, -1, 1};
+vector_t v6{ 1, 1, 1};
+vector_t v7{-1, 1, 1};
+
+Hexahedron hex{v0, v1, v2, v3, v4, v5, v6, v7};
+Sphere s{vector_t::Constant(1), 1};
+
+scalar_t result = overlap(s, hex);
+```
+This code snippet calculates the correct result (pi/6) for this simple
+configuration.
+
+To obtain the overlap area of a sphere and the facets of a tetrahedron, the
+function *overlapArea* can be employed as such:
+```
+vector_t v0{-std::sqrt(3) / 6.0, -1.0 / 2.0, 0};
+vector_t v1{std::sqrt(3) / 3.0, 0, 0};
+vector_t v2{-std::sqrt(3) / 6.0, +1.0 / 2.0, 0};
+vector_t v3{0, 0, std::sqrt(6) / 3.0};
+
+Tetrahedron tet{v0, v1, v2, v3};
+Sphere s{{0, 0, 1.5}, 1.25};
+
+auto result = overlapArea(s, tet);
+
+std::cout << "surface area of sphere intersecting tetrahedron: " <<
+ result[0] << std::endl;
+
+std::cout << "overlap areas per face:" << std::endl;
+// The indices of the faces are NOT zero-based here!
+for(size_t f = 1; f < result.size() - 1; ++f)
+ std::cout << " face #" << (f - 1) << ": " << result[f] << std::endl;
+
+std::cout << "total surface area of tetrahedron intersecting sphere: " <<
+ result.back() << std::endl;
+```
diff --git a/contrib/overlap/overlap.hpp b/contrib/overlap/overlap.hpp
new file mode 100644
index 00000000..9dbeb064
--- /dev/null
+++ b/contrib/overlap/overlap.hpp
@@ -0,0 +1,2204 @@
+/*!
+ * Exact calculation of the overlap volume of spheres and mesh elements.
+ * http://dx.doi.org/10.1016/j.jcp.2016.02.003
+ *
+ * Copyright (C) 2015-2018 Severin Strobl
+ *
+ * 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 .
+ */
+
+#ifndef OVERLAP_HPP
+#define OVERLAP_HPP
+
+// Eigen
+#include
+#include
+
+// C++
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace OVERLAP {
+
+// typedefs
+typedef double scalar_t;
+typedef Eigen::Matrix vector_t;
+typedef Eigen::Matrix vector2_t;
+
+// Pretty-printing of Eigen matrices.
+static const Eigen::IOFormat pretty(Eigen::StreamPrecision,
+ Eigen::DontAlignCols, " ", ";\n", "", "", "[", "]");
+
+// constants
+const scalar_t pi = scalar_t(4) * std::atan(scalar_t(1.0));
+
+namespace detail {
+
+static const scalar_t tinyEpsilon(2 *
+ std::numeric_limits::epsilon());
+
+static const scalar_t mediumEpsilon(1e2 * tinyEpsilon);
+static const scalar_t largeEpsilon(1e-10);
+
+// Robust calculation of the normal vector of a polygon using Newell's method
+// and a pre-calculated center.
+// Ref: Christer Ericson - Real-Time Collision Detection (2005)
+template
+inline vector_t normalNewell(Iterator begin, Iterator end, const vector_t&
+ center) {
+
+ const size_t count = end - begin;
+ vector_t n(vector_t::Zero());
+
+ for(size_t i = 0; i < count; ++i)
+ n += (*(begin + i) - center).cross(*(begin + ((i + 1) % count)) -
+ center);
+
+ scalar_t length = n.stableNorm();
+
+ if(length)
+ return n / length;
+ else
+ return n;
+}
+
+// This implementation of double_prec is based on:
+// T.J. Dekker, A floating-point technique for extending the available
+// precision, http://dx.doi.org/10.1007/BF01397083
+
+template
+struct double_prec_constant;
+
+template<>
+struct double_prec_constant {
+ // Constant used to split double precision values:
+ // 2^(24 - 24/2) + 1 = 2^12 + 1 = 4097
+ static const uint32_t value = 4097;
+};
+
+template<>
+struct double_prec_constant {
+ // Constant used to split double precision values:
+ // 2^(53 - int(53/2)) + 1 = 2^27 + 1 = 134217729
+ static const uint32_t value = 134217729;
+};
+
+// For GCC and Clang an attribute can be used to control the FP precision...
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && \
+ !defined(__INTEL_COMPILER)
+#define ENFORCE_EXACT_FPMATH_ATTR __attribute__((__target__("ieee-fp")))
+#else
+#define ENFORCE_EXACT_FPMATH_ATTR
+#endif
+
+// ... whereas ICC requires a pragma.
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+#define ENFORCE_EXACT_FPMATH_ATTR
+#define USE_EXACT_FPMATH_PRAGMA 1
+#endif
+
+template
+class double_prec;
+
+template
+inline double_prec operator+(const double_prec& lhs, const
+ double_prec& rhs) ENFORCE_EXACT_FPMATH_ATTR;
+
+template
+inline double_prec operator-(const double_prec& lhs, const
+ double_prec& rhs) ENFORCE_EXACT_FPMATH_ATTR;
+
+template
+inline double_prec operator*(const double_prec& lhs, const
+ double_prec& rhs) ENFORCE_EXACT_FPMATH_ATTR;
+
+template
+class double_prec {
+ private:
+ static const uint32_t c = detail::double_prec_constant::value;
+
+ template
+ friend double_prec operator+(const double_prec&, const
+ double_prec&);
+
+ template
+ friend double_prec operator-(const double_prec&, const
+ double_prec&);
+
+ template
+ friend double_prec operator*(const double_prec&, const
+ double_prec&);
+
+ public:
+ inline double_prec() : h_(0), l_(0) {
+ }
+
+ // This constructor requires floating point operations in accordance
+ // with IEEE754 to perform the proper splitting. To allow full
+ // optimization of all other parts of the code, precise floating point
+ // ops are only requested here. Unfortunately the way to do this is
+ // extremely compiler dependent.
+ inline double_prec(const T& val) ENFORCE_EXACT_FPMATH_ATTR : h_(0),
+ l_(0) {
+
+#ifdef USE_EXACT_FPMATH_PRAGMA
+ #pragma float_control(precise, on)
+#endif
+
+ T p = val * T(c);
+ h_ = (val - p) + p;
+ l_ = val - h_;
+ }
+
+ private:
+ inline explicit double_prec(const T& h, const T& l) : h_(h), l_(l) {
+ }
+
+ public:
+ inline const T& high() const {
+ return h_;
+ }
+
+ inline const T& low() const {
+ return l_;
+ }
+
+ inline T value() const {
+ return h_ + l_;
+ }
+
+ template
+ inline TOther convert() const {
+ return TOther(h_) + TOther(l_);
+ }
+
+ private:
+ T h_;
+ T l_;
+};
+
+template
+inline double_prec operator+(const double_prec& lhs, const
+ double_prec& rhs) {
+
+#ifdef USE_EXACT_FPMATH_PRAGMA
+ #pragma float_control(precise, on)
+#endif
+
+ T h = lhs.h_ + rhs.h_;
+ T l = std::abs(lhs.h_) >= std::abs(rhs.h_) ?
+ ((((lhs.h_ - h) + rhs.h_) + lhs.l_) + rhs.l_) :
+ ((((rhs.h_ - h) + lhs.h_) + rhs.l_) + lhs.l_);
+
+ T c = h + l;
+
+ return double_prec(c, (h - c) + l);
+}
+
+template
+inline double_prec operator-(const double_prec& lhs, const
+ double_prec& rhs) {
+
+#ifdef USE_EXACT_FPMATH_PRAGMA
+ #pragma float_control(precise, on)
+#endif
+
+ T h = lhs.h_ - rhs.h_;
+ T l = std::abs(lhs.h_) >= std::abs(rhs.h_) ?
+ ((((lhs.h_ - h) - rhs.h_) - rhs.l_) + lhs.l_) :
+ ((((-rhs.h_ - h) + lhs.h_) + lhs.l_) - rhs.l_);
+
+ T c = h + l;
+
+ return double_prec(c, (h - c) + l);
+}
+
+template
+inline double_prec operator*(const double_prec& lhs, const
+ double_prec& rhs) {
+
+#ifdef USE_EXACT_FPMATH_PRAGMA
+ #pragma float_control(precise, on)
+#endif
+
+ double_prec l(lhs.h_);
+ double_prec r(rhs.h_);
+
+ T p = l.h_ * r.h_;
+ T q = l.h_ * r.l_ + l.l_ * r.h_;
+ T v = p + q;
+
+ double_prec c(v, ((p - v) + q) + l.l_ * r.l_);
+ c.l_ = ((lhs.h_ + lhs.l_) * rhs.l_ + lhs.l_ * rhs.h_) + c.l_;
+ T z = c.value();
+
+ return double_prec(z, (c.h_ - z) + c.l_);
+}
+
+// Ref: J.R. Shewchuk - Lecture Notes on Geometric Robustness
+// http://www.cs.berkeley.edu/~jrs/meshpapers/robnotes.pdf
+inline scalar_t orient2D(const vector2_t& a, const vector2_t& b, const
+ vector2_t& c) {
+
+ typedef double_prec real_t;
+
+ real_t a0(a[0]);
+ real_t a1(a[1]);
+ real_t b0(b[0]);
+ real_t b1(b[1]);
+ real_t c0(c[0]);
+ real_t c1(c[1]);
+
+ real_t result = (a0 - c0) * (b1 - c1) - (a1 - c1) * (b0 - c0);
+
+ return result.convert();
+}
+
+// Numerically robust calculation of the normal of the triangle defined by
+// the points a, b, and c.
+// Ref: J.R. Shewchuk - Lecture Notes on Geometric Robustness
+// http://www.cs.berkeley.edu/~jrs/meshpapers/robnotes.pdf
+inline vector_t triangleNormal(const vector_t& a, const vector_t& b, const
+ vector_t& c) {
+
+ scalar_t xy = orient2D(vector2_t(a[0], a[1]), vector2_t(b[0], b[1]),
+ vector2_t(c[0], c[1]));
+
+ scalar_t yz = orient2D(vector2_t(a[1], a[2]), vector2_t(b[1], b[2]),
+ vector2_t(c[1], c[2]));
+
+ scalar_t zx = orient2D(vector2_t(a[2], a[0]), vector2_t(b[2], b[0]),
+ vector2_t(c[2], c[0]));
+
+ return vector_t(yz, zx, xy).normalized();
+}
+
+// Numerically robust routine to calculate the angle between normalized
+// vectors.
+// Ref: http://www.plunk.org/~hatch/rightway.php
+inline scalar_t angle(const vector_t& v0, const vector_t& v1) {
+ if(v0.dot(v1) < scalar_t(0))
+ return pi - scalar_t(2) * std::asin(scalar_t(0.5) * (v0 +
+ v1).stableNorm());
+ else
+ return scalar_t(2) * std::asin(scalar_t(0.5) * (v0 - v1).stableNorm());
+}
+
+template
+inline std::array gramSchmidt(const Eigen::MatrixBase&
+ arg0, const Eigen::MatrixBase& arg1) {
+
+ vector_t v0(arg0.normalized());
+ vector_t v1(arg1);
+
+ std::array result;
+ result[0] = v0;
+ result[1] = (v1 - v1.dot(v0) * v0).normalized();
+
+ return result;
+}
+
+inline scalar_t clamp(scalar_t value, scalar_t min, scalar_t max, scalar_t
+ limit) {
+
+ assert(min <= max && limit >= scalar_t(0));
+
+ value = (value < min && value > (min - limit)) ? min : value;
+ value = (value > max && value < (max + limit)) ? max : value;
+
+ return value;
+}
+
+} // namespace detail
+
+class Transformation {
+ public:
+ Transformation(const vector_t& t, const scalar_t& s) : translation(t),
+ scaling(s) {
+ }
+
+ vector_t translation;
+ scalar_t scaling;
+};
+
+template
+class Polygon {
+ private:
+ static_assert(VertexCount >= 3 && VertexCount <= 4,
+ "Only triangles and quadrilateral are supported.");
+
+ public:
+ static const size_t vertex_count = VertexCount;
+
+ protected:
+ Polygon() : vertices(), center(), normal(), area() {
+ }
+
+ template
+ Polygon(const vector_t& v0, Types... verts) : vertices{{v0,
+ verts...}}, center(), normal(), area() {
+
+ center = scalar_t(1.0 / vertex_count) *
+ std::accumulate(vertices.begin(), vertices.end(),
+ vector_t::Zero().eval());
+
+ // For a quadrilateral, Newell's method can be simplified
+ // significantly.
+ // Ref: Christer Ericson - Real-Time Collision Detection (2005)
+ if(VertexCount == 4) {
+ normal = ((vertices[2] - vertices[0]).cross(vertices[3] -
+ vertices[1])).normalized();
+ } else {
+ normal = detail::normalNewell(vertices.begin(), vertices.end(),
+ center);
+ }
+ }
+
+ void apply(const Transformation& t) {
+ for(auto& v : vertices)
+ v = t.scaling * (v + t.translation);
+
+ center = t.scaling * (center + t.translation);
+ }
+
+ public:
+ bool isPlanar(const scalar_t epsilon = detail::largeEpsilon) const {
+ if(VertexCount == 3)
+ return true;
+
+ for(auto& v : vertices)
+ if(std::abs(normal.dot(v - center)) > epsilon)
+ return false;
+
+ return true;
+ }
+
+ public:
+ std::array vertices;
+ vector_t center;
+ vector_t normal;
+ scalar_t area;
+};
+
+class Triangle : public Polygon<3> {
+ public:
+ Triangle() : Polygon<3>() {
+ }
+
+ template
+ Triangle(const vector_t& v0, Types... verts) : Polygon<3>(v0,
+ verts...) {
+
+ init();
+ }
+
+ void apply(const Transformation& t) {
+ Polygon<3>::apply(t);
+ init();
+ }
+
+ private:
+ void init() {
+ area = scalar_t(0.5) * ((vertices[1] - vertices[0]).cross(
+ vertices[2] - vertices[0])).stableNorm();
+ }
+};
+
+class Quadrilateral : public Polygon<4> {
+ public:
+ Quadrilateral() : Polygon<4>() {
+ }
+
+ template
+ Quadrilateral(const vector_t& v0, Types... verts) : Polygon<4>(v0,
+ verts...) {
+
+ init();
+ }
+
+ void apply(const Transformation& t) {
+ Polygon<4>::apply(t);
+ init();
+ }
+
+ private:
+ void init() {
+ area = scalar_t(0.5) * (((vertices[1] - vertices[0]).cross(
+ vertices[2] - vertices[0])).stableNorm() +
+ ((vertices[2] - vertices[0]).cross(
+ vertices[3] - vertices[0])).stableNorm());
+ }
+};
+
+// Forward declarations of the mesh elements.
+class Tetrahedron;
+class Wedge;
+class Hexahedron;
+
+namespace detail {
+
+// Some tricks are required to keep this code header-only.
+template
+struct mappings;
+
+template
+struct mappings {
+ // Map edges of a tetrahedron to vertices and faces.
+ static const uint32_t edge_mapping[6][2][2];
+
+ // Map vertices of a tetrahedron to edges and faces.
+ // 0: local IDs of the edges intersecting at this vertex
+ // 1: 0 if the edge is pointing away from the vertex, 1 otherwise
+ // 2: faces joining at the vertex
+ static const uint32_t vertex_mapping[4][3][3];
+
+ // This mapping contains the three sets of the two edges for each of the
+ // faces joining at a vertex. The indices are mapped to the local edge IDs
+ // using the first value field of the 'vertex_mapping' table.
+ static const uint32_t face_mapping[3][2];
+};
+
+template
+const uint32_t mappings::edge_mapping[6][2][2] = {
+ { { 0, 1 }, { 0, 1 } }, { { 1, 2 }, { 0, 2 } }, { { 2, 0 }, { 0, 3 } },
+ { { 0, 3 }, { 1, 3 } }, { { 1, 3 }, { 1, 2 } }, { { 2, 3 }, { 2, 3 } }
+};
+
+template
+const uint32_t mappings::vertex_mapping[4][3][3] = {
+ { { 0, 2, 3 }, { 0, 1, 0 }, { 0, 1, 3 } },
+ { { 0, 1, 4 }, { 1, 0, 0 }, { 0, 1, 2 } },
+ { { 1, 2, 5 }, { 1, 0, 0 }, { 0, 2, 3 } },
+ { { 3, 4, 5 }, { 1, 1, 1 }, { 1, 3, 2 } }
+};
+
+template
+const uint32_t mappings::face_mapping[3][2] = {
+ { 0, 1 }, { 0, 2 }, { 1, 2 }
+};
+
+typedef mappings tet_mappings;
+
+} // namespace detail
+
+class Tetrahedron : public detail::tet_mappings {
+ public:
+ template
+ Tetrahedron(const vector_t& v0, Types... verts) : vertices{{v0,
+ verts...}}, faces(), center(), volume() {
+
+#ifndef NDEBUG
+ // Make sure the ordering of the vertices is correct.
+ assert((vertices[1] - vertices[0]).cross(vertices[2] -
+ vertices[0]).dot(vertices[3] - vertices[0]) >= scalar_t(0));
+#endif // NDEBUG
+
+ init();
+ }
+
+ Tetrahedron(const std::array& verts) : vertices(verts),
+ faces(), center(), volume() {
+
+ init();
+ }
+
+ Tetrahedron() : vertices{{vector_t::Zero(), vector_t::Zero(),
+ vector_t::Zero(), vector_t::Zero()}}, faces(), center(), volume() {
+ }
+
+ void apply(const Transformation& t) {
+ for(auto& v : vertices)
+ v = t.scaling * (v + t.translation);
+
+ for(auto& f : faces)
+ f.apply(t);
+
+ center = scalar_t(0.25) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t surfaceArea() const {
+ scalar_t area(0);
+ for(const auto& f : faces)
+ area += f.area;
+
+ return area;
+ }
+
+ private:
+ void init() {
+ // 0: v2, v1, v0
+ faces[0] = Triangle(vertices[2], vertices[1], vertices[0]);
+
+ // 1: v0, v1, v3
+ faces[1] = Triangle(vertices[0], vertices[1], vertices[3]);
+
+ // 2: v1, v2, v3
+ faces[2] = Triangle(vertices[1], vertices[2], vertices[3]);
+
+ // 3: v2, v0, v3
+ faces[3] = Triangle(vertices[2], vertices[0], vertices[3]);
+
+ center = scalar_t(0.25) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t calcVolume() const {
+ return scalar_t(1.0 / 6.0) * std::abs((vertices[0] -
+ vertices[3]).dot((vertices[1] - vertices[3]).cross(
+ vertices[2] - vertices[3])));
+ }
+
+ public:
+ std::array vertices;
+ std::array faces;
+ vector_t center;
+ scalar_t volume;
+};
+
+namespace detail {
+
+template
+struct mappings {
+ // Map edges of a wedge to vertices and faces.
+ static const uint32_t edge_mapping[9][2][2];
+
+ // Map vertices of a wedge to edges and faces.
+ // 0: local IDs of the edges intersecting at this vertex
+ // 1: 0 if the edge is pointing away from the vertex, 1 otherwise
+ // 2: faces joining at the vertex
+ static const uint32_t vertex_mapping[6][3][3];
+
+ // This mapping contains the three sets of the two edges for each of the
+ // faces joining at a vertex. The indices are mapped to the local edge IDs
+ // using the first value field of the 'vertex_mapping' table.
+ static const uint32_t face_mapping[3][2];
+};
+
+template
+const uint32_t mappings::edge_mapping[9][2][2] = {
+ { { 0, 1 }, { 0, 1 } }, { { 1, 2 }, { 0, 2 } }, { { 2, 0 }, { 0, 3 } },
+ { { 0, 3 }, { 1, 3 } }, { { 1, 4 }, { 1, 2 } }, { { 2, 5 }, { 2, 3 } },
+ { { 3, 4 }, { 1, 4 } }, { { 4, 5 }, { 2, 4 } }, { { 5, 3 }, { 3, 4 } }
+};
+
+template
+const uint32_t mappings::vertex_mapping[6][3][3] = {
+ { { 0, 2, 3 }, { 0, 1, 0 }, { 0, 1, 3 } },
+ { { 0, 1, 4 }, { 1, 0, 0 }, { 0, 1, 2 } },
+ { { 1, 2, 5 }, { 1, 0, 0 }, { 0, 2, 3 } },
+
+ { { 3, 6, 8 }, { 1, 0, 1 }, { 1, 3, 4 } },
+ { { 4, 6, 7 }, { 1, 1, 0 }, { 1, 2, 4 } },
+ { { 5, 7, 8 }, { 1, 1, 0 }, { 2, 3, 4 } }
+};
+
+template
+const uint32_t mappings::face_mapping[3][2] = {
+ { 0, 1 }, { 0, 2 }, { 1, 2 }
+};
+
+typedef mappings wedge_mappings;
+
+} // namespace detail
+
+class Wedge : public detail::wedge_mappings {
+ public:
+ template
+ Wedge(const vector_t& v0, Types... verts) : vertices{{v0,
+ verts...}}, faces(), center(), volume() {
+
+ init();
+ }
+
+ Wedge(const std::array& verts) : vertices(verts),
+ faces(), center(), volume() {
+
+ init();
+ }
+
+ Wedge() : vertices{{vector_t::Zero(), vector_t::Zero(),
+ vector_t::Zero(), vector_t::Zero(), vector_t::Zero(),
+ vector_t::Zero()}}, faces(), center(), volume() {
+ }
+
+ void apply(const Transformation& t) {
+ for(auto& v : vertices)
+ v = t.scaling * (v + t.translation);
+
+ for(auto& f : faces)
+ f.apply(t);
+
+ center = scalar_t(1.0 / 6.0) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t surfaceArea() const {
+ scalar_t area(0);
+ for(const auto& f : faces)
+ area += f.area;
+
+ return area;
+ }
+
+ private:
+ void init() {
+ // All faces of the wedge are stored as quadrilaterals, so an
+ // additional point is inserted between v0 and v1.
+ // 0: v2, v1, v0, v02
+ faces[0] = Quadrilateral(vertices[2], vertices[1], vertices[0],
+ scalar_t(0.5) * (vertices[0] + vertices[2]));
+
+ // 1: v0, v1, v4, v3
+ faces[1] = Quadrilateral(vertices[0], vertices[1], vertices[4],
+ vertices[3]);
+
+ // 2: v1, v2, v5, v4
+ faces[2] = Quadrilateral(vertices[1], vertices[2], vertices[5],
+ vertices[4]);
+
+ // 3: v2, v0, v3, v5
+ faces[3] = Quadrilateral(vertices[2], vertices[0], vertices[3],
+ vertices[5]);
+
+ // All faces of the wedge are stored as quadrilaterals, so an
+ // additional point is inserted between v3 and v5.
+ // 4: v3, v4, v5, v53
+ faces[4] = Quadrilateral(vertices[3], vertices[4], vertices[5],
+ scalar_t(0.5) * (vertices[5] + vertices[3]));
+
+ center = scalar_t(1.0 / 6.0) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t calcVolume() const {
+ // The wedge is treated as a degenerate hexahedron here by adding
+ // two fake vertices v02 and v35.
+ vector_t diagonal(vertices[5] - vertices[0]);
+
+ return scalar_t(1.0 / 6.0) * (diagonal.dot(((vertices[1] -
+ vertices[0]).cross(vertices[2] - vertices[4])) +
+ ((vertices[3] - vertices[0]).cross(
+ vertices[4] - scalar_t(0.5) * (vertices[3] + vertices[5]))) +
+ ((scalar_t(0.5) * (vertices[0] + vertices[2]) -
+ vertices[0]).cross(scalar_t(0.5) * (vertices[3] +
+ vertices[5]) - vertices[2]))));
+ }
+
+ public:
+ std::array vertices;
+ std::array faces;
+ vector_t center;
+ scalar_t volume;
+};
+
+namespace detail {
+
+template
+struct mappings {
+ // Map edges of a hexahedron to vertices and faces.
+ static const uint32_t edge_mapping[12][2][2];
+
+ // Map vertices of a hexahedron to edges and faces.
+ // 0: local IDs of the edges intersecting at this vertex
+ // 1: 0 if the edge is pointing away from the vertex, 1 otherwise
+ // 2: faces joining at the vertex
+ static const uint32_t vertex_mapping[8][3][3];
+
+ // This mapping contains the three sets of the two edges for each of the
+ // faces joining at a vertex. The indices are mapped to the local edge IDs
+ // using the first value field of the 'vertex_mapping' table.
+ static const uint32_t face_mapping[3][2];
+};
+
+template
+const uint32_t mappings::edge_mapping[12][2][2] = {
+ { { 0, 1 }, { 0, 1 } }, { { 1, 2 }, { 0, 2 } },
+ { { 2, 3 }, { 0, 3 } }, { { 3, 0 }, { 0, 4 } },
+
+ { { 0, 4 }, { 1, 4 } }, { { 1, 5 }, { 1, 2 } },
+ { { 2, 6 }, { 2, 3 } }, { { 3, 7 }, { 3, 4 } },
+
+ { { 4, 5 }, { 1, 5 } }, { { 5, 6 }, { 2, 5 } },
+ { { 6, 7 }, { 3, 5 } }, { { 7, 4 }, { 4, 5 } }
+};
+
+template
+const uint32_t mappings::vertex_mapping[8][3][3] = {
+ { { 0, 3, 4 }, { 0, 1, 0 }, { 0, 1, 4 } },
+ { { 0, 1, 5 }, { 1, 0, 0 }, { 0, 1, 2 } },
+ { { 1, 2, 6 }, { 1, 0, 0 }, { 0, 2, 3 } },
+ { { 2, 3, 7 }, { 1, 0, 0 }, { 0, 3, 4 } },
+
+ { { 4, 8, 11 }, { 1, 0, 1 }, { 1, 4, 5 } },
+ { { 5, 8, 9 }, { 1, 1, 0 }, { 1, 2, 5 } },
+ { { 6, 9, 10 }, { 1, 1, 0 }, { 2, 3, 5 } },
+ { { 7, 10, 11 }, { 1, 1, 0 }, { 3, 4, 5 } }
+};
+
+template
+const uint32_t mappings::face_mapping[3][2] = {
+ { 0, 1 }, { 0, 2 }, { 1, 2 }
+};
+
+typedef mappings hex_mappings;
+
+} // namespace detail
+
+class Hexahedron : public detail::hex_mappings {
+ public:
+ template
+ Hexahedron(const vector_t& v0, Types... verts) : vertices{{v0,
+ verts...}}, faces(), center(), volume() {
+
+ init();
+ }
+
+ Hexahedron(const std::array& verts) : vertices(verts),
+ faces(), center(), volume() {
+
+ init();
+ }
+
+ void apply(const Transformation& t) {
+ for(auto& v : vertices)
+ v = t.scaling * (v + t.translation);
+
+ for(auto& f : faces)
+ f.apply(t);
+
+ center = scalar_t(1.0 / 8.0) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t surfaceArea() const {
+ scalar_t area(0);
+ for(const auto& f : faces)
+ area += f.area;
+
+ return area;
+ }
+
+ private:
+ void init() {
+ // 0: v3, v2, v1, v0
+ faces[0] = Quadrilateral(vertices[3], vertices[2], vertices[1],
+ vertices[0]);
+
+ // 1: v0, v1, v5, v4
+ faces[1] = Quadrilateral(vertices[0], vertices[1], vertices[5],
+ vertices[4]);
+
+ // 2: v1, v2, v6, v5
+ faces[2] = Quadrilateral(vertices[1], vertices[2], vertices[6],
+ vertices[5]);
+
+ // 3: v2, v3, v7, v6
+ faces[3] = Quadrilateral(vertices[2], vertices[3], vertices[7],
+ vertices[6]);
+
+ // 4: v3, v0, v4, v7
+ faces[4] = Quadrilateral(vertices[3], vertices[0], vertices[4],
+ vertices[7]);
+
+ // 5: v4, v5, v6, v7
+ faces[5] = Quadrilateral(vertices[4], vertices[5], vertices[6],
+ vertices[7]);
+
+ center = scalar_t(1.0 / 8.0) * std::accumulate(vertices.begin(),
+ vertices.end(), vector_t::Zero().eval());
+
+ volume = calcVolume();
+ }
+
+ scalar_t calcVolume() const {
+ vector_t diagonal(vertices[6] - vertices[0]);
+
+ return scalar_t(1.0 / 6.0) * diagonal.dot(((vertices[1] -
+ vertices[0]).cross(vertices[2] - vertices[5])) +
+ ((vertices[4] - vertices[0]).cross(
+ vertices[5] - vertices[7])) +
+ ((vertices[3] - vertices[0]).cross(
+ vertices[7] - vertices[2])));
+ }
+
+ public:
+ std::array vertices;
+ std::array faces;
+ vector_t center;
+ scalar_t volume;
+};
+
+class Sphere {
+ public:
+ Sphere(const vector_t& c, scalar_t r) : center(c), radius(r),
+ volume(scalar_t(4.0 / 3.0 * pi) * r * r * r) {
+ }
+
+ scalar_t capVolume(scalar_t h) const {
+ if(h <= scalar_t(0))
+ return scalar_t(0);
+ else if(h >= scalar_t(2) * radius)
+ return volume;
+ else
+ return scalar_t(pi / 3.0) * h * h * (scalar_t(3) * radius - h);
+ }
+
+ scalar_t capSurfaceArea(scalar_t h) const {
+ if(h <= scalar_t(0))
+ return scalar_t(0);
+ else if(h >= scalar_t(2) * radius)
+ return surfaceArea();
+ else
+ return scalar_t(2 * pi) * radius * h;
+ }
+
+ scalar_t diskArea(scalar_t h) const {
+ if(h <= scalar_t(0) || h >= scalar_t(2) * radius)
+ return scalar_t(0);
+ else
+ return pi * h * (scalar_t(2) * radius - h);
+ }
+
+ scalar_t surfaceArea() const {
+ return (scalar_t(4) * pi) * (radius * radius);
+ }
+
+ public:
+ vector_t center;
+ scalar_t radius;
+ scalar_t volume;
+};
+
+class Plane {
+ public:
+ Plane(const vector_t& c, const vector_t& n) : center(c), normal(n) {
+ }
+
+ public:
+ vector_t center;
+ vector_t normal;
+};
+
+class AABB {
+ public:
+ AABB(const vector_t& minimum = vector_t::Constant(std::numeric_limits<
+ scalar_t>::infinity()), const vector_t& maximum =
+ vector_t::Constant(-std::numeric_limits::infinity())) :
+ min(minimum), max(maximum) {
+ }
+
+ bool intersects(const AABB& aabb) const {
+ if((min.array() > aabb.max.array()).any() ||
+ (max.array() < aabb.min.array()).any())
+ return false;
+
+ return true;
+ }
+
+ AABB overlap(const AABB& aabb) const {
+ return AABB(min.cwiseMax(aabb.min), max.cwiseMin(aabb.max));
+ }
+
+ bool contains(const vector_t& p) const {
+ if((p.array() < min.array()).any() ||
+ (p.array() > max.array()).any())
+ return false;
+
+ return true;
+ }
+
+ void include(const vector_t& point) {
+ min = min.cwiseMin(point);
+ max = max.cwiseMax(point);
+ }
+
+ template
+ void include(const std::array& points) {
+ for(const auto& p : points)
+ include(p);
+ }
+
+ scalar_t volume() const {
+ vector_t size(max - min);
+
+ return size[0] * size[1] * size[2];
+ }
+
+ public:
+ vector_t min, max;
+};
+
+// Decomposition of a tetrahedron into 4 tetrahedra.
+inline void decompose(const Tetrahedron& tet, std::array&
+ tets) {
+
+ tets[0] = Tetrahedron(tet.vertices[0], tet.vertices[1], tet.vertices[2],
+ tet.center);
+
+ tets[1] = Tetrahedron(tet.vertices[0], tet.vertices[1], tet.center,
+ tet.vertices[3]);
+
+ tets[2] = Tetrahedron(tet.vertices[1], tet.vertices[2], tet.center,
+ tet.vertices[3]);
+
+ tets[3] = Tetrahedron(tet.vertices[2], tet.vertices[0], tet.center,
+ tet.vertices[3]);
+}
+
+// Decomposition of a hexahedron into 2 wedges.
+inline void decompose(const Hexahedron& hex, std::array& wedges) {
+ wedges[0] = Wedge(hex.vertices[0], hex.vertices[1], hex.vertices[2],
+ hex.vertices[4], hex.vertices[5], hex.vertices[6]);
+
+ wedges[1] = Wedge(hex.vertices[0], hex.vertices[2], hex.vertices[3],
+ hex.vertices[4], hex.vertices[6], hex.vertices[7]);
+}
+
+// Decomposition of a hexahedron into 5 tetrahedra.
+inline void decompose(const Hexahedron& hex, std::array&
+ tets) {
+
+ tets[0] = Tetrahedron(hex.vertices[0], hex.vertices[1], hex.vertices[2],
+ hex.vertices[5]);
+
+ tets[1] = Tetrahedron(hex.vertices[0], hex.vertices[2], hex.vertices[7],
+ hex.vertices[5]);
+
+ tets[2] = Tetrahedron(hex.vertices[0], hex.vertices[2], hex.vertices[3],
+ hex.vertices[7]);
+
+ tets[3] = Tetrahedron(hex.vertices[0], hex.vertices[5], hex.vertices[7],
+ hex.vertices[4]);
+
+ tets[4] = Tetrahedron(hex.vertices[2], hex.vertices[7], hex.vertices[5],
+ hex.vertices[6]);
+}
+
+// Decomposition of a hexahedron into 6 tetrahedra.
+inline void decompose(const Hexahedron& hex, std::array&
+ tets) {
+
+ tets[0] = Tetrahedron(hex.vertices[0], hex.vertices[5], hex.vertices[7],
+ hex.vertices[4]);
+
+ tets[1] = Tetrahedron(hex.vertices[0], hex.vertices[1], hex.vertices[7],
+ hex.vertices[5]);
+
+ tets[2] = Tetrahedron(hex.vertices[1], hex.vertices[6], hex.vertices[7],
+ hex.vertices[5]);
+
+ tets[3] = Tetrahedron(hex.vertices[0], hex.vertices[7], hex.vertices[2],
+ hex.vertices[3]);
+
+ tets[4] = Tetrahedron(hex.vertices[0], hex.vertices[7], hex.vertices[1],
+ hex.vertices[2]);
+
+ tets[5] = Tetrahedron(hex.vertices[1], hex.vertices[7], hex.vertices[6],
+ hex.vertices[2]);
+}
+
+inline bool contains(const Sphere& s, const vector_t& p) {
+ return (s.center - p).squaredNorm() <= s.radius * s.radius;
+}
+
+// The (convex!) polygon is assumed to be planar, making this a 2D problem.
+// Check the projection of the point onto the plane of the polygon for
+// containment within the polygon.
+template
+bool contains(const Polygon& poly, const vector_t& point) {
+ const vector_t proj(point - poly.normal.dot(point - poly.center) *
+ poly.normal);
+
+ for(size_t n = 0; n < poly.vertices.size(); ++n) {
+ const auto& v0 = poly.vertices[n];
+ const auto& v1 = poly.vertices[(n + 1) % poly.vertices.size()];
+ vector_t base(scalar_t(0.5) * (v0 + v1));
+ vector_t edge(v1 - v0);
+
+ // Note: Only the sign of the projection is of interest, so this vector
+ // does not have to be normalized.
+ vector_t dir(edge.cross(poly.normal));
+
+ // Check whether the projection of the point lies inside of the
+ // polygon.
+ if(dir.dot(proj - base) > scalar_t(0))
+ return false;
+ }
+
+ return true;
+}
+
+inline bool contains(const Tetrahedron& tet, const vector_t& p) {
+ for(const auto& f : tet.faces)
+ if(f.normal.dot(p - f.center) > scalar_t(0))
+ return false;
+
+ return true;
+}
+
+inline bool contains(const Wedge& wedge, const vector_t& p) {
+ for(const auto& f : wedge.faces)
+ if(f.normal.dot(p - f.center) > scalar_t(0))
+ return false;
+
+ return true;
+}
+
+inline bool contains(const Hexahedron& hex, const vector_t& p) {
+ for(const auto& f : hex.faces)
+ if(f.normal.dot(p - f.center) > scalar_t(0))
+ return false;
+
+ return true;
+}
+
+inline bool intersect(const Sphere& s, const Plane& p) {
+ scalar_t proj = p.normal.dot(s.center - p.center);
+
+ return proj * proj - s.radius * s.radius < scalar_t(0);
+}
+
+template
+inline bool intersect(const Sphere& s, const Polygon& poly) {
+ return intersect(s, Plane(poly.center, poly.normal)) && contains(poly,
+ s.center);
+}
+
+inline std::pair, size_t> lineSphereIntersection(const
+ vector_t& origin, const vector_t& direction, const Sphere& s) {
+
+ std::array solutions = {{
+ std::numeric_limits::infinity(),
+ std::numeric_limits::infinity()
+ }};
+
+ vector_t originRel(origin - s.center);
+ scalar_t a = direction.squaredNorm();
+
+ if(a == scalar_t(0))
+ return std::make_pair(solutions, 0);
+
+ scalar_t b = scalar_t(2) * direction.dot(originRel);
+ scalar_t c = originRel.squaredNorm() - s.radius * s.radius;
+
+ scalar_t discriminant = b * b - scalar_t(4) * a * c;
+ if(discriminant > scalar_t(0)) {
+ // Two real roots.
+ scalar_t q = scalar_t(-0.5) * (b +
+ std::copysign(std::sqrt(discriminant), b));
+
+ solutions[0] = q / a;
+ solutions[1] = c / q;
+
+ if(solutions[0] > solutions[1])
+ std::swap(solutions[0], solutions[1]);
+
+ return std::make_pair(solutions, 2);
+ } else if(std::abs(discriminant) == scalar_t(0)) {
+ // Double real root.
+ solutions[0] = (scalar_t(-0.5) * b) / a;
+ solutions[1] = solutions[0];
+
+ return std::make_pair(solutions, 1);
+ } else {
+ // No real roots.
+ return std::make_pair(solutions, 0);
+ }
+}
+
+namespace detail {
+
+// Calculate the volume of a regularized spherical wedge defined by the radius,
+// the distance of the intersection point from the center of the sphere and the
+// angle.
+inline scalar_t regularizedWedge(scalar_t r, scalar_t d, scalar_t alpha) {
+#ifndef NDEBUG
+ // Clamp slight deviations of the angle to valid range.
+ if(alpha < scalar_t(0) && alpha > -detail::tinyEpsilon)
+ alpha = scalar_t(0);
+
+ if(alpha > scalar_t(0.5 * pi) && alpha < scalar_t(0.5 * pi) + tinyEpsilon)
+ alpha = scalar_t(0.5 * pi);
+#endif
+
+ // Check the parameters for validity (debug version only).
+ assert(r > scalar_t(0));
+ assert(d >= scalar_t(0) && d <= r);
+ assert(alpha >= scalar_t(0) && alpha <= scalar_t(0.5 * pi));
+
+ const scalar_t sinAlpha = std::sin(alpha);
+ const scalar_t cosAlpha = std::cos(alpha);
+
+ const scalar_t a = d * sinAlpha;
+ const scalar_t b = std::sqrt(std::abs(r * r - d * d));
+ const scalar_t c = d * cosAlpha;
+
+ return scalar_t(1.0 / 3.0) * a * b * c +
+ a * (scalar_t(1.0 / 3.0) * a * a - r * r) * std::atan2(b, c) +
+ scalar_t(2.0 / 3.0) * r * r * r * std::atan2(sinAlpha * b,
+ cosAlpha * r);
+}
+
+// Wrapper around the above function handling correctly handling the case of
+// alpha > pi/2 and negative z.
+inline scalar_t regularizedWedge(scalar_t r, scalar_t d, scalar_t alpha,
+ scalar_t z) {
+
+ if(z >= scalar_t(0)) {
+ if(alpha > scalar_t(0.5 * pi)) {
+ scalar_t h = r - z;
+
+ return scalar_t(pi / 3.0) * h * h * (scalar_t(3) * r - h) -
+ regularizedWedge(r, d, pi - alpha);
+ } else {
+ return regularizedWedge(r, d, alpha);
+ }
+ } else {
+ scalar_t vHem = scalar_t(2.0 / 3.0 * pi) * r * r * r;
+
+ if(alpha > scalar_t(0.5 * pi)) {
+ return vHem - regularizedWedge(r, d, pi - alpha);
+ } else {
+ scalar_t h = r + z;
+ scalar_t vCap = scalar_t(pi / 3.0) * h * h * (scalar_t(3) * r - h);
+
+ return vHem - (vCap - regularizedWedge(r, d, alpha));
+ }
+ }
+}
+
+// Calculate the surface area of a regularized spherical wedge defined by the
+// radius, the distance of the intersection point from the center of the sphere
+// and the angle.
+// Ref: Gibson, K. D. & Scheraga, H. A.: Exact calculation of the volume and
+// surface area of fused hard-sphere molecules with unequal atomic radii,
+// Molecular Physics, 1987, 62, 1247-1265
+inline scalar_t regularizedWedgeArea(scalar_t r, scalar_t z, scalar_t alpha) {
+#ifndef NDEBUG
+ // Clamp slight deviations of the angle to valid range.
+ if(alpha < scalar_t(0) && alpha > -detail::tinyEpsilon)
+ alpha = scalar_t(0);
+
+ if(alpha > pi && alpha < pi + tinyEpsilon)
+ alpha = pi;
+#endif
+
+ // Check the parameters for validity (debug version only).
+ assert(r > scalar_t(0));
+ assert(z >= -r && z <= r);
+ assert(alpha >= scalar_t(0) && alpha <= pi);
+
+ if(alpha < tinyEpsilon || std::abs(r * r - z * z) <= tinyEpsilon)
+ return scalar_t(0);
+
+ const scalar_t sinAlpha = std::sin(alpha);
+ const scalar_t cosAlpha = std::cos(alpha);
+ const scalar_t factor = scalar_t(1) / std::sqrt(std::abs(r * r - z * z));
+
+ // Clamp slight deviations of the argument to acos() to valid range.
+ const scalar_t arg0 = clamp(r * cosAlpha * factor, scalar_t(-1),
+ scalar_t(1), detail::tinyEpsilon);
+
+ const scalar_t arg1 = clamp((z * cosAlpha * factor) / sinAlpha,
+ scalar_t(-1), scalar_t(1), detail::tinyEpsilon);
+
+ // Check the argument to acos() for validity (debug version only).
+ assert(scalar_t(-1) <= arg0 && arg0 <= scalar_t(1));
+ assert(scalar_t(-1) <= arg1 && arg1 <= scalar_t(1));
+
+ return scalar_t(2) * r * r * std::acos(arg0) -
+ scalar_t(2) * r * z * std::acos(arg1);
+}
+
+} // namespace detail
+
+// Depending on the dimensionality, either the volume or external surface area
+// of the general wedge is computed.
+template
+inline scalar_t generalWedge(const Sphere& s, const Plane& p0, const Plane& p1,
+ const vector_t& d) {
+
+ static_assert(Dim == 2 || Dim == 3,
+ "Invalid dimensionality, must be 2 or 3.");
+
+ scalar_t dist(d.stableNorm());
+
+ if(dist < detail::tinyEpsilon) {
+ // The wedge (almost) touches the center, the volume depends only on
+ // the angle.
+ scalar_t angle = pi - detail::angle(p0.normal, p1.normal);
+
+ if(Dim == 2) {
+ return scalar_t(2) * s.radius * s.radius * angle;
+ } else {
+ return scalar_t(2.0 / 3.0) * s.radius * s.radius * s.radius *
+ angle;
+ }
+ }
+
+ scalar_t s0 = d.dot(p0.normal);
+ scalar_t s1 = d.dot(p1.normal);
+
+ // Detect degenerated general spherical wedge that can be treated as
+ // a regularized spherical wedge.
+ if(std::abs(s0) < detail::tinyEpsilon ||
+ std::abs(s1) < detail::tinyEpsilon) {
+
+ scalar_t angle = pi - detail::angle(p0.normal, p1.normal);
+
+ if(Dim == 2) {
+ return detail::regularizedWedgeArea(s.radius,
+ std::abs(s0) > std::abs(s1) ? s0 : s1, angle);
+ } else {
+ return detail::regularizedWedge(s.radius, dist, angle,
+ std::abs(s0) > std::abs(s1) ? s0 : s1);
+ }
+ }
+
+ vector_t dUnit(d * (scalar_t(1) / dist));
+ if(dist < detail::largeEpsilon)
+ dUnit = detail::gramSchmidt(p0.normal.cross(p1.normal), dUnit)[1];
+
+ // Check the planes specify a valid setup (debug version only).
+ assert(p0.normal.dot(p1.center - p0.center) <= scalar_t(0));
+ assert(p1.normal.dot(p0.center - p1.center) <= scalar_t(0));
+
+ // Calculate the angles between the vector from the sphere center
+ // to the intersection line and the normal vectors of the two planes.
+ scalar_t alpha0 = detail::angle(p0.normal, dUnit);
+ scalar_t alpha1 = detail::angle(p1.normal, dUnit);
+
+ scalar_t dir0 = dUnit.dot((s.center + d) - p0.center);
+ scalar_t dir1 = dUnit.dot((s.center + d) - p1.center);
+
+ if(s0 >= scalar_t(0) && s1 >= scalar_t(0)) {
+ alpha0 = scalar_t(0.5 * pi) - std::copysign(alpha0, dir0);
+ alpha1 = scalar_t(0.5 * pi) - std::copysign(alpha1, dir1);
+
+ if(Dim == 2) {
+ return detail::regularizedWedgeArea(s.radius, s0, alpha0) +
+ detail::regularizedWedgeArea(s.radius, s1, alpha1);
+ } else {
+ return detail::regularizedWedge(s.radius, dist, alpha0, s0) +
+ detail::regularizedWedge(s.radius, dist, alpha1, s1);
+ }
+ } else if(s0 < scalar_t(0) && s1 < scalar_t(0)) {
+ alpha0 = scalar_t(0.5 * pi) + std::copysign(scalar_t(1), dir0) *
+ (alpha0 - pi);
+
+ alpha1 = scalar_t(0.5 * pi) + std::copysign(scalar_t(1), dir1) *
+ (alpha1 - pi);
+
+ if(Dim == 2) {
+ return s.surfaceArea() -
+ (detail::regularizedWedgeArea(s.radius, -s0, alpha0) +
+ detail::regularizedWedgeArea(s.radius, -s1, alpha1));
+ } else {
+ return s.volume - (detail::regularizedWedge(s.radius, dist, alpha0,
+ -s0) + detail::regularizedWedge(s.radius, dist, alpha1, -s1));
+ }
+ } else {
+ alpha0 = scalar_t(0.5 * pi) - std::copysign(scalar_t(1), dir0 * s0) *
+ (alpha0 - (s0 < scalar_t(0) ? pi : scalar_t(0)));
+
+ alpha1 = scalar_t(0.5 * pi) - std::copysign(scalar_t(1), dir1 * s1) *
+ (alpha1 - (s1 < scalar_t(0) ? pi : scalar_t(0)));
+
+ if(Dim == 2) {
+ scalar_t area0 = detail::regularizedWedgeArea(s.radius,
+ std::abs(s0), alpha0);
+
+ scalar_t area1 = detail::regularizedWedgeArea(s.radius,
+ std::abs(s1), alpha1);
+
+ return std::max(area0, area1) - std::min(area0, area1);
+ } else {
+ scalar_t volume0 = detail::regularizedWedge(s.radius, dist, alpha0,
+ std::abs(s0));
+
+ scalar_t volume1 = detail::regularizedWedge(s.radius, dist, alpha1,
+ std::abs(s1));
+
+ return std::max(volume0, volume1) - std::min(volume0, volume1);
+ }
+ }
+}
+
+template
+struct array_size;
+
+template
+struct array_size> {
+
+ static constexpr size_t value() {
+ return N;
+ }
+};
+
+template
+constexpr size_t nrEdges() {
+ return std::is_same::value ? 12 :
+ (std::is_same::value ? 9 :
+ (std::is_same::value ? 6 : -1));
+}
+
+// Workaround for the Intel compiler, as it does not yet support constexpr for
+// template arguments.
+template
+struct element_trait {
+ static const size_t nrVertices =
+ array_size::value();
+
+ static const size_t nrFaces =
+ array_size::value();
+};
+
+// Depending on the dimensionality, either the volume or external surface area
+// of the general wedge is computed.
+template
+scalar_t generalWedge(const Sphere& sphere, const Element& element, size_t
+ edge, const std::array, nrEdges()>&
+ intersections) {
+
+ static_assert(Dim == 2 || Dim == 3,
+ "Invalid dimensionality, must be 2 or 3.");
+
+ const auto& f0 = element.faces[Element::edge_mapping[edge][1][0]];
+ const auto& f1 = element.faces[Element::edge_mapping[edge][1][1]];
+
+ vector_t edgeCenter(scalar_t(0.5) * ((intersections[edge][0] +
+ element.vertices[Element::edge_mapping[edge][0][0]]) +
+ (intersections[edge][1] +
+ element.vertices[Element::edge_mapping[edge][0][1]])));
+
+ Plane p0(f0.center, f0.normal);
+ Plane p1(f1.center, f1.normal);
+
+ return generalWedge(sphere, p0, p1, edgeCenter - sphere.center);
+}
+
+template
+scalar_t overlap(const Sphere& sOrig, const Element& elementOrig) {
+ static_assert(std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value,
+ "Invalid element type detected.");
+
+ // Construct AABBs and perform a coarse overlap detection.
+ AABB sAABB(sOrig.center - vector_t::Constant(sOrig.radius), sOrig.center +
+ vector_t::Constant(sOrig.radius));
+
+ AABB eAABB;
+ eAABB.include(elementOrig.vertices);
+
+ if(!sAABB.intersects(eAABB))
+ return scalar_t(0);
+
+ // Use scaled and shifted versions of the sphere and the element.
+ Transformation transformation(-sOrig.center, scalar_t(1) / sOrig.radius);
+
+ Sphere s(vector_t::Zero(), scalar_t(1));
+
+ Element element(elementOrig);
+ element.apply(transformation);
+
+ // Constants: Number of vertices and faces.
+ static const size_t nrVertices = element_trait::nrVertices;
+ static const size_t nrFaces = element_trait::nrFaces;
+
+ size_t vOverlap = 0;
+ // Check whether the vertices lie on or outside of the sphere.
+ for(const auto& vertex : element.vertices)
+ if((s.center - vertex).squaredNorm() <= s.radius * s.radius)
+ ++vOverlap;
+
+ // Check for trivial case: All vertices inside of the sphere, resulting in
+ // a full overlap.
+ if(vOverlap == nrVertices)
+ return elementOrig.volume;
+
+ // Sanity check: All faces of the mesh element have to be planar.
+ for(const auto& face : element.faces)
+ if(!face.isPlanar())
+ throw std::runtime_error("Non-planer face detected in element!");
+
+ // Sets of overlapping primitives.
+ std::bitset vMarked;
+ std::bitset()> eMarked;
+ std::bitset fMarked;
+
+ // Initial value: Volume of the full sphere.
+ scalar_t result = s.volume;
+
+ // The intersection points between the single edges and the sphere, this
+ // is needed later on.
+ std::array, nrEdges()> eIntersections;
+
+ // Process all edges of the element.
+ for(size_t n = 0; n < nrEdges(); ++n) {
+ vector_t start(element.vertices[Element::edge_mapping[n][0][0]]);
+ vector_t direction(element.vertices[Element::edge_mapping[n][0][1]] -
+ start);
+
+ auto solutions = lineSphereIntersection(start, direction, s);
+
+ // No intersection between the edge and the sphere, where intersection
+ // points close to the surface of the sphere are ignored.
+ // Or:
+ // The sphere cuts the edge twice, no vertex is inside of the
+ // sphere, but the case of the edge only touching the sphere has to
+ // be avoided.
+ if(!solutions.second ||
+ (solutions.first[0] >= scalar_t(1) - detail::mediumEpsilon) ||
+ solutions.first[1] <= detail::mediumEpsilon ||
+ (solutions.first[0] > scalar_t(0) &&
+ solutions.first[1] < scalar_t(1) &&
+ (solutions.first[1] - solutions.first[0] <
+ detail::largeEpsilon))) {
+
+ continue;
+ } else {
+ vMarked[Element::edge_mapping[n][0][0]] =
+ solutions.first[0] < scalar_t(0);
+
+ vMarked[Element::edge_mapping[n][0][1]] =
+ solutions.first[1] > scalar_t(1);
+ }
+
+ // Store the two intersection points of the edge with the sphere for
+ // later usage.
+ eIntersections[n][0] = solutions.first[0] * direction;
+ eIntersections[n][1] = (solutions.first[1] - scalar_t(1)) * direction;
+ eMarked[n] = true;
+
+ // If the edge is marked as having an overlap, the two faces forming it
+ // have to be marked as well.
+ fMarked[Element::edge_mapping[n][1][0]] = true;
+ fMarked[Element::edge_mapping[n][1][1]] = true;
+ }
+
+ // Check whether the dependencies for a vertex intersection are fulfilled.
+ for(size_t n = 0; n < nrVertices; ++n) {
+ if(!vMarked[n])
+ continue;
+
+ bool edgesValid = true;
+ for(size_t eN = 0; eN < 3; ++eN) {
+ size_t edgeId = Element::vertex_mapping[n][0][eN];
+ edgesValid &= eMarked[edgeId];
+ }
+
+ // If not all three edges intersecting at this vertex where marked, the
+ // sphere is only touching.
+ if(!edgesValid)
+ vMarked[n] = false;
+ }
+
+ // Process all faces of the element, ignoring the edges as those where
+ // already checked above.
+ for(size_t n = 0; n < nrFaces; ++n)
+ if(intersect(s, element.faces[n]))
+ fMarked[n] = true;
+
+ // Trivial case: The center of the sphere overlaps the element, but the
+ // sphere does not intersect any of the faces of the element, meaning the
+ // sphere is completely contained within the element.
+ if(!fMarked.count() && contains(element, s.center))
+ return sOrig.volume;
+
+ // Spurious intersection: The initial intersection test was positive, but
+ // the detailed checks revealed no overlap.
+ if(!vMarked.count() && !eMarked.count() && !fMarked.count())
+ return scalar_t(0);
+
+ // Iterate over all the marked faces and subtract the volume of the cap cut
+ // off by the plane.
+ for(size_t n = 0; n < nrFaces; ++n) {
+ if(!fMarked[n])
+ continue;
+
+ const auto& f = element.faces[n];
+ scalar_t dist = f.normal.dot(s.center - f.center);
+ scalar_t vCap = s.capVolume(s.radius + dist);
+
+ result -= vCap;
+ }
+
+ // Handle the edges and add back the volume subtracted twice above in the
+ // processing of the faces.
+ for(size_t n = 0; n < nrEdges(); ++n) {
+ if(!eMarked[n])
+ continue;
+
+ scalar_t edgeCorrection = generalWedge<3, Element>(s, element, n,
+ eIntersections);
+
+ result += edgeCorrection;
+ }
+
+ // Handle the vertices and subtract the volume added twice above in the
+ // processing of the edges.
+ for(size_t n = 0; n < nrVertices; ++n) {
+ if(!vMarked[n])
+ continue;
+
+ // Collect the points where the three edges intersecting at this
+ // vertex intersect the sphere.
+ // Both the relative and the absolute positions are required.
+ std::array intersectionPointsRelative;
+ std::array intersectionPoints;
+ for(size_t e = 0; e < 3; ++e) {
+ auto edgeIdx = Element::vertex_mapping[n][0][e];
+ intersectionPointsRelative[e] =
+ eIntersections[edgeIdx][Element::vertex_mapping[n][1][e]];
+
+ intersectionPoints[e] = intersectionPointsRelative[e] +
+ element.vertices[n];
+ }
+
+ // This triangle is constructed by hand to have more freedom of how
+ // the normal vector is calculated.
+ Triangle coneTria;
+ coneTria.vertices = {{ intersectionPoints[0], intersectionPoints[1],
+ intersectionPoints[2] }};
+
+ coneTria.center = scalar_t(1.0 / 3.0) *
+ std::accumulate(intersectionPoints.begin(),
+ intersectionPoints.end(), vector_t::Zero().eval());
+
+ // Calculate the normal of the triangle defined by the intersection
+ // points in relative coordinates to improve accuracy.
+ // Also use double the normal precision to calculate this normal.
+ coneTria.normal = detail::triangleNormal(intersectionPointsRelative[0],
+ intersectionPointsRelative[1], intersectionPointsRelative[2]);
+
+ // The area of this triangle is never needed, so it is set to an
+ // invalid value.
+ coneTria.area = std::numeric_limits::infinity();
+
+ std::array, 3> distances;
+ for(size_t i = 0; i < 3; ++i)
+ distances[i] = std::make_pair(i,
+ intersectionPointsRelative[i].squaredNorm());
+
+ std::sort(distances.begin(), distances.end(),
+ [](const std::pair& a,
+ const std::pair& b) -> bool {
+ return a.second < b.second;
+ });
+
+ if(distances[1].second < distances[2].second * detail::largeEpsilon) {
+ // Use the general spherical wedge defined by the edge with the
+ // non-degenerated intersection point and the normals of the
+ // two faces forming it.
+ scalar_t correction = generalWedge<3, Element>(s, element,
+ Element::vertex_mapping[n][0][distances[2].first],
+ eIntersections);
+
+ result -= correction;
+
+ continue;
+ }
+
+ scalar_t tipTetVolume = scalar_t(1.0 / 6.0) * std::abs(
+ -intersectionPointsRelative[2].dot((intersectionPointsRelative[0] -
+ intersectionPointsRelative[2]).cross(
+ intersectionPointsRelative[1] - intersectionPointsRelative[2])));
+
+ // Make sure the normal points in the right direction i.e. away from
+ // the center of the element.
+ if(coneTria.normal.dot(element.center - coneTria.center) >
+ scalar_t(0)) {
+
+ coneTria.normal = -coneTria.normal;
+ }
+
+ Plane plane(coneTria.center, coneTria.normal);
+
+ scalar_t dist = coneTria.normal.dot(s.center - coneTria.center);
+ scalar_t capVolume = s.capVolume(s.radius + dist);
+
+ // The cap volume is tiny, so the corrections will be even smaller.
+ // There is no way to actually calculate them with reasonable
+ // precision, so just the volume of the tetrahedron at the tip is
+ // used.
+ if(capVolume < detail::tinyEpsilon) {
+ result -= tipTetVolume;
+ continue;
+ }
+
+ // Calculate the volume of the three spherical segments between
+ // the faces joining at the vertex and the plane through the
+ // intersection points.
+ scalar_t segmentVolume = 0;
+
+ for(size_t e = 0; e < 3; ++e) {
+ const auto& f = element.faces[Element::vertex_mapping[n][2][e]];
+ uint32_t e0 = Element::face_mapping[e][0];
+ uint32_t e1 = Element::face_mapping[e][1];
+
+ vector_t center(scalar_t(0.5) * (intersectionPoints[e0] +
+ intersectionPoints[e1]));
+
+ scalar_t wedgeVolume = generalWedge<3>(s, plane, Plane(f.center,
+ -f.normal), center - s.center);
+
+ segmentVolume += wedgeVolume;
+ }
+
+ // Calculate the volume of the cone and clamp it to zero.
+ scalar_t coneVolume = std::max(tipTetVolume + capVolume -
+ segmentVolume, scalar_t(0));
+
+ // Sanity check: detect negative cone volume.
+ assert(coneVolume > -std::sqrt(detail::tinyEpsilon));
+
+ result -= coneVolume;
+
+ // Sanity check: detect negative intermediate result.
+ assert(result > -std::sqrt(detail::tinyEpsilon));
+ }
+
+ // In case of different sized objects the error can become quite large,
+ // so a relative limit is used.
+ scalar_t maxOverlap = std::min(s.volume, element.volume);
+ const scalar_t limit(std::sqrt(std::numeric_limits::epsilon()) *
+ maxOverlap);
+
+ // Clamp tiny negative volumes to zero.
+ if(result < scalar_t(0) && result > -limit)
+ return scalar_t(0);
+
+ // Clamp results slightly too large.
+ if(result > maxOverlap && result - maxOverlap < limit)
+ return std::min(sOrig.volume, elementOrig.volume);
+
+ // Perform a sanity check on the final result (debug version only).
+ assert(result >= scalar_t(0) && result <= maxOverlap);
+
+ // Scale the overlap volume back for the original objects.
+ result = (result / s.volume) * sOrig.volume;
+
+ return result;
+}
+
+template
+scalar_t overlap(const Sphere& s, Iterator eBegin, Iterator eEnd) {
+ scalar_t sum(0);
+
+ for(Iterator it = eBegin; it != eEnd; ++it)
+ sum += overlap(s, *it);
+
+ return sum;
+}
+
+// Calculate the surface area of the sphere and the element that are contained
+// within the common or intersecting part of the geometries, respectively.
+// The returned array of size (N + 2), with N being the number of vertices,
+// holds (in this order):
+// - surface area of the region of the sphere intersecting the element
+// - for each face of the element: area contained within the sphere
+// - total surface area of the element intersecting the sphere
+template::nrFaces +
+ 2>
+auto overlapArea(const Sphere& sOrig, const Element& elementOrig) ->
+ std::array {
+
+ static_assert(NrFaces == element_trait::nrFaces + 2,
+ "Invalid number of faces for the element provided.");
+
+ static_assert(std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value,
+ "Invalid element type detected.");
+
+ // Constants: Number of vertices and faces.
+ static const size_t nrVertices = element_trait::nrVertices;
+ static const size_t nrFaces = element_trait::nrFaces;
+
+ // Initial value: Zero overlap.
+ std::array result;
+ result.fill(scalar_t(0));
+
+ // Construct AABBs and perform a coarse overlap detection.
+ AABB sAABB(sOrig.center - vector_t::Constant(sOrig.radius), sOrig.center +
+ vector_t::Constant(sOrig.radius));
+
+ AABB eAABB;
+ eAABB.include(elementOrig.vertices);
+
+ if(!sAABB.intersects(eAABB))
+ return result;
+
+ // Use scaled and shifted versions of the sphere and the element.
+ Transformation transformation(-sOrig.center, scalar_t(1) / sOrig.radius);
+
+ Sphere s(vector_t::Zero(), scalar_t(1));
+
+ Element element(elementOrig);
+ element.apply(transformation);
+
+ size_t vOverlap = 0;
+ // Check whether the vertices lie on or outside of the sphere.
+ for(const auto& vertex : element.vertices)
+ if((s.center - vertex).squaredNorm() <= s.radius * s.radius)
+ ++vOverlap;
+
+ // Check for trivial case: All vertices inside of the sphere, resulting in
+ // a full coverage of all faces.
+ if(vOverlap == nrVertices) {
+ for(size_t n = 0; n < nrFaces; ++n) {
+ result[n + 1] = elementOrig.faces[n].area;
+ result[nrFaces + 1] += elementOrig.faces[n].area;
+ }
+
+ return result;
+ }
+
+ // Sanity check: All faces of the mesh element have to be planar.
+ for(const auto& face : element.faces)
+ if(!face.isPlanar())
+ throw std::runtime_error("Non-planer face detected in element!");
+
+ // Sets of overlapping primitives.
+ std::bitset vMarked;
+ std::bitset()> eMarked;
+ std::bitset fMarked;
+
+ // The intersection points between the single edges and the sphere, this
+ // is needed later on.
+ std::array, nrEdges()> eIntersections;
+
+ // Cache the squared radius of the disk formed by the intersection between
+ // the planes defined by each face and the sphere.
+ std::array intersectionRadiusSq;
+
+ // Process all edges of the element.
+ for(size_t n = 0; n < nrEdges(); ++n) {
+ vector_t start(element.vertices[Element::edge_mapping[n][0][0]]);
+ vector_t direction(element.vertices[Element::edge_mapping[n][0][1]] -
+ start);
+
+ auto solutions = lineSphereIntersection(start, direction, s);
+
+ // No intersection between the edge and the sphere, where intersection
+ // points close to the surface of the sphere are ignored.
+ // Or:
+ // The sphere cuts the edge twice, no vertex is inside of the
+ // sphere, but the case of the edge only touching the sphere has to
+ // be avoided.
+ if(!solutions.second ||
+ solutions.first[0] >= scalar_t(1) - detail::mediumEpsilon ||
+ solutions.first[1] <= detail::mediumEpsilon ||
+ (solutions.first[0] > scalar_t(0) &&
+ solutions.first[1] < scalar_t(1) &&
+ solutions.first[1] - solutions.first[0] <
+ detail::largeEpsilon)) {
+
+ continue;
+ } else {
+ vMarked[Element::edge_mapping[n][0][0]] =
+ solutions.first[0] < scalar_t(0);
+
+ vMarked[Element::edge_mapping[n][0][1]] =
+ solutions.first[1] > scalar_t(1);
+ }
+
+ // Store the two intersection points of the edge with the sphere for
+ // later usage.
+ eIntersections[n][0] = solutions.first[0] * direction;
+ eIntersections[n][1] = (solutions.first[1] - scalar_t(1)) * direction;
+
+ eMarked[n] = true;
+
+ // If the edge is marked as having an overlap, the two faces forming it
+ // have to be marked as well.
+ fMarked[Element::edge_mapping[n][1][0]] = true;
+ fMarked[Element::edge_mapping[n][1][1]] = true;
+ }
+
+ // Check whether the dependencies for a vertex intersection are fulfilled.
+ for(size_t n = 0; n < nrVertices; ++n) {
+ if(!vMarked[n])
+ continue;
+
+ bool edgesValid = true;
+ for(size_t eN = 0; eN < 3; ++eN) {
+ size_t edgeId = Element::vertex_mapping[n][0][eN];
+ edgesValid &= eMarked[edgeId];
+ }
+
+ // If not all three edges intersecting at this vertex where marked, the
+ // sphere is only touching.
+ if(!edgesValid)
+ vMarked[n] = false;
+ }
+
+ // Process all faces of the element, ignoring the edges as those where
+ // already checked above.
+ for(size_t n = 0; n < nrFaces; ++n)
+ if(intersect(s, element.faces[n]))
+ fMarked[n] = true;
+
+ // Trivial case: The center of the sphere overlaps the element, but the
+ // sphere does not intersect any of the faces of the element, meaning the
+ // sphere is completely contained within the element.
+ if(!fMarked.count() && contains(element, s.center)) {
+ result[0] = sOrig.surfaceArea();
+
+ return result;
+ }
+
+ // Spurious intersection: The initial intersection test was positive, but
+ // the detailed checks revealed no overlap.
+ if(!vMarked.count() && !eMarked.count() && !fMarked.count())
+ return result;
+
+ // Initial value for the surface of the sphere: Surface area of the full
+ // sphere.
+ result[0] = s.surfaceArea();
+
+ // Iterate over all the marked faces and calculate the area of the disk
+ // defined by the plane as well as the cap surfaces.
+ for(size_t n = 0; n < nrFaces; ++n) {
+ if(!fMarked[n])
+ continue;
+
+ const auto& f = element.faces[n];
+ scalar_t dist = f.normal.dot(s.center - f.center);
+ result[0] -= s.capSurfaceArea(s.radius + dist);
+ result[n + 1] = s.diskArea(s.radius + dist);
+ }
+
+ // Handle the edges and subtract the area of the respective disk cut off by
+ // the edge and add back the surface area of the spherical wedge defined
+ // by the edge.
+ for(size_t n = 0; n < nrEdges(); ++n) {
+ if(!eMarked[n])
+ continue;
+
+ result[0] += generalWedge<2, Element>(s, element, n, eIntersections);
+
+ // The intersection points are relative to the vertices forming the
+ // edge.
+ const vector_t chord =
+ ((element.vertices[Element::edge_mapping[n][0][0]] +
+ eIntersections[n][0]) -
+ (element.vertices[Element::edge_mapping[n][0][1]] +
+ eIntersections[n][1]));
+
+ const scalar_t chordLength = chord.stableNorm();
+
+ // Each edge belongs to two faces, indexed via
+ // Element::edge_mapping[n][1][{0,1}].
+ for(size_t e = 0; e < 2; ++e) {
+ const auto faceIdx = Element::edge_mapping[n][1][e];
+ const auto& f = element.faces[faceIdx];
+
+ // Height of the spherical cap cut off by the plane containing the
+ // face.
+ const scalar_t dist = f.normal.dot(s.center - f.center) + s.radius;
+ intersectionRadiusSq[faceIdx] = dist * (scalar_t(2) * s.radius -
+ dist);
+
+ // Calculate the height of the triangular segment in the plane of
+ // the base.
+ const scalar_t factor = std::sqrt(std::max(scalar_t(0),
+ intersectionRadiusSq[faceIdx] - scalar_t(0.25) * chordLength *
+ chordLength));
+
+ const scalar_t theta = scalar_t(2) * std::atan2(chordLength,
+ scalar_t(2) * factor);
+
+ scalar_t area = scalar_t(0.5) * intersectionRadiusSq[faceIdx] *
+ (theta - std::sin(theta));
+
+ // FIXME: Might not be necessary to use the center of the chord.
+ const vector_t chordCenter = scalar_t(0.5) *
+ ((element.vertices[Element::edge_mapping[n][0][0]] +
+ eIntersections[n][0]) +
+ (element.vertices[Element::edge_mapping[n][0][1]] +
+ eIntersections[n][1]));
+
+ const vector_t proj(s.center - f.normal.dot(s.center - f.center) *
+ f.normal);
+
+ // If the projected sphere center and the face center fall on
+ // opposite sides of the edge, the area has to be inverted.
+ if(chord.cross(proj - chordCenter).dot(
+ chord.cross(f.center - chordCenter)) < scalar_t(0)) {
+
+ area = intersectionRadiusSq[faceIdx] * pi - area;
+ }
+
+ result[faceIdx + 1] -= area;
+ }
+ }
+
+ // Handle the vertices and add the area subtracted twice above in the
+ // processing of the edges.
+
+ // First, handle the spherical surface area of the intersection.
+ // This is to a large part code duplicated from the volume calculation.
+ // TODO: Unify the area and volume calculation to remove duplicate code.
+ for(size_t n = 0; n < nrVertices; ++n) {
+ if(!vMarked[n])
+ continue;
+
+ // Collect the points where the three edges intersecting at this
+ // vertex intersect the sphere.
+ // Both the relative and the absolute positions are required.
+ std::array intersectionPointsRelative;
+ std::array intersectionPoints;
+ for(size_t e = 0; e < 3; ++e) {
+ auto edgeIdx = Element::vertex_mapping[n][0][e];
+ intersectionPointsRelative[e] =
+ eIntersections[edgeIdx][Element::vertex_mapping[n][1][e]];
+
+ intersectionPoints[e] = intersectionPointsRelative[e] +
+ element.vertices[n];
+ }
+
+ // This triangle is constructed by hand to have more freedom of how
+ // the normal vector is calculated.
+ Triangle coneTria;
+ coneTria.vertices = {{ intersectionPoints[0], intersectionPoints[1],
+ intersectionPoints[2] }};
+
+ coneTria.center = scalar_t(1.0 / 3.0) *
+ std::accumulate(intersectionPoints.begin(),
+ intersectionPoints.end(), vector_t::Zero().eval());
+
+ // Calculate the normal of the triangle defined by the intersection
+ // points in relative coordinates to improve accuracy.
+ // Also use double the normal precision to calculate this normal.
+ coneTria.normal = detail::triangleNormal(intersectionPointsRelative[0],
+ intersectionPointsRelative[1], intersectionPointsRelative[2]);
+
+ // The area of this triangle is never needed, so it is set to an
+ // invalid value.
+ coneTria.area = std::numeric_limits::infinity();
+
+ std::array, 3> distances;
+ for(size_t i = 0; i < 3; ++i)
+ distances[i] = std::make_pair(i,
+ intersectionPointsRelative[i].squaredNorm());
+
+ std::sort(distances.begin(), distances.end(),
+ [](const std::pair& a,
+ const std::pair& b) -> bool {
+ return a.second < b.second;
+ });
+
+ if(distances[1].second < distances[2].second * detail::largeEpsilon) {
+ // Use the general spherical wedge defined by the edge with the
+ // non-degenerated intersection point and the normals of the
+ // two faces forming it.
+ scalar_t correction = generalWedge<2, Element>(s, element,
+ Element::vertex_mapping[n][0][distances[2].first],
+ eIntersections);
+
+ result[0] -= correction;
+
+ continue;
+ }
+
+ // Make sure the normal points in the right direction, i.e., away from
+ // the center of the element.
+ if(coneTria.normal.dot(element.center - coneTria.center) >
+ scalar_t(0)) {
+
+ coneTria.normal = -coneTria.normal;
+ }
+
+ Plane plane(coneTria.center, coneTria.normal);
+
+ scalar_t dist = coneTria.normal.dot(s.center - coneTria.center);
+ scalar_t capSurface = s.capSurfaceArea(s.radius + dist);
+
+ // If cap surface area is small, the corrections will be even smaller.
+ // There is no way to actually calculate them with reasonable
+ // precision, so they are just ignored.
+ if(capSurface < detail::largeEpsilon)
+ continue;
+
+ // Calculate the surface area of the three spherical segments between
+ // the faces joining at the vertex and the plane through the
+ // intersection points.
+ scalar_t segmentSurface = 0;
+ for(size_t e = 0; e < 3; ++e) {
+ const auto& f = element.faces[Element::vertex_mapping[n][2][e]];
+ uint32_t e0 = Element::face_mapping[e][0];
+ uint32_t e1 = Element::face_mapping[e][1];
+
+ vector_t center(scalar_t(0.5) * (intersectionPoints[e0] +
+ intersectionPoints[e1]));
+
+ segmentSurface += generalWedge<2>(s, plane, Plane(f.center,
+ -f.normal), center - s.center);
+ }
+
+ // Calculate the surface area of the cone and clamp it to zero.
+ scalar_t coneSurface = std::max(capSurface - segmentSurface,
+ scalar_t(0));
+
+ result[0] -= coneSurface;
+
+ // Sanity checks: detect negative/excessively large intermediate
+ // result.
+ assert(result[0] > -std::sqrt(detail::tinyEpsilon));
+ assert(result[0] < s.surfaceArea() + detail::tinyEpsilon);
+ }
+
+ // Second, correct the intersection area of the facets.
+ for(size_t n = 0; n < nrVertices; ++n) {
+ if(!vMarked[n])
+ continue;
+
+ // Iterate over all the faces joining at this vertex.
+ for(size_t f = 0; f < 3; ++f) {
+ // Determine the two edges of this face intersecting at the
+ // vertex.
+ uint32_t e0 = Element::face_mapping[f][0];
+ uint32_t e1 = Element::face_mapping[f][1];
+ std::array edgeIndices = {{
+ Element::vertex_mapping[n][0][e0],
+ Element::vertex_mapping[n][0][e1]
+ }};
+
+ // Extract the (relative) intersection points of these edges with
+ // the sphere furthest from the vertex.
+ std::array intersectionPoints = {{
+ eIntersections[edgeIndices[0]][
+ Element::vertex_mapping[n][1][e0]],
+
+ eIntersections[edgeIndices[1]][
+ Element::vertex_mapping[n][1][e1]]
+ }};
+
+ // Together with the vertex, this determines the triangle
+ // representing one part of the correction.
+ const scalar_t triaArea = scalar_t(0.5) *
+ (intersectionPoints[0].cross(
+ intersectionPoints[1])).stableNorm();
+
+ // The second component is the segment defined by the face and the
+ // intersection points.
+ const scalar_t chordLength = (intersectionPoints[0] -
+ intersectionPoints[1]).stableNorm();
+
+ const auto faceIdx = Element::vertex_mapping[n][2][f];
+
+ // TODO: Cache theta for each edge.
+ const scalar_t theta = scalar_t(2) * std::atan2(chordLength,
+ scalar_t(2) * std::sqrt(std::max(scalar_t(0),
+ intersectionRadiusSq[faceIdx] -
+ scalar_t(0.25) * chordLength * chordLength)));
+
+ scalar_t segmentArea = scalar_t(0.5) *
+ intersectionRadiusSq[faceIdx] * (theta - std::sin(theta));
+
+ // Determine if the (projected) center of the sphere lies within
+ // the triangle or not. If not, the segment area has to be
+ // corrected.
+ const vector_t d(scalar_t(0.5) * (intersectionPoints[0] +
+ intersectionPoints[1]));
+
+ const auto& face = element.faces[faceIdx];
+ const vector_t proj(s.center - face.normal.dot(s.center -
+ face.center) * face.normal);
+
+ if(d.dot((proj - element.vertices[n]) - d) > scalar_t(0)) {
+ segmentArea = intersectionRadiusSq[faceIdx] * pi - segmentArea;
+ }
+
+ result[faceIdx + 1] += triaArea + segmentArea;
+
+ // Sanity checks: detect excessively large intermediate result.
+ assert(result[faceIdx + 1] < element.faces[faceIdx].area +
+ std::sqrt(detail::largeEpsilon));
+ }
+ }
+
+ // Scale the surface areas back for the original objects and clamp
+ // values within reasonable limits.
+ const scalar_t scaling = sOrig.radius / s.radius;
+ const scalar_t sLimit(std::sqrt(std::numeric_limits::epsilon()) *
+ s.surfaceArea());
+
+ // As the precision of the area calculation deteriorates quickly with a
+ // increasing size ratio between the element and the sphere, the precision
+ // limit applied to the sphere is used as the lower limit for the facets.
+ const scalar_t fLimit(std::max(sLimit,
+ std::sqrt(std::numeric_limits::epsilon()) *
+ element.surfaceArea()));
+
+ // Sanity checks: detect negative/excessively large results for the
+ // surface area of the facets.
+#ifndef NDEBUG
+ for(size_t n = 0; n < nrFaces; ++n) {
+ assert(result[n + 1] > -fLimit);
+ assert(result[n + 1] <= element.faces[n].area + fLimit);
+ }
+#endif // NDEBUG
+
+ // Surface of the sphere.
+ result[0] = detail::clamp(result[0], scalar_t(0), s.surfaceArea(), sLimit);
+ result[0] *= (scaling * scaling);
+
+ // Surface of the mesh element.
+ for(size_t f = 0; f < nrFaces; ++f) {
+ auto& value = result[f + 1];
+ value = detail::clamp(value, scalar_t(0), element.faces[f].area,
+ fLimit);
+
+ value = value * (scaling * scaling);
+ }
+
+ result.back() = std::accumulate(result.begin() + 1, result.end() - 1,
+ scalar_t(0));
+
+ // Perform some more sanity checks on the final result (debug version
+ // only).
+ assert(scalar_t(0) <= result[0] && result[0] <= sOrig.surfaceArea());
+
+ assert(scalar_t(0) <= result.back() && result.back() <=
+ elementOrig.surfaceArea());
+
+ return result;
+}
+
+}
+#endif // OVERLAP_HPP
+
diff --git a/include/auxkernels/MDGranularPorosityAux.h b/include/auxkernels/MDGranularPorosityAux.h
new file mode 100644
index 00000000..1e282033
--- /dev/null
+++ b/include/auxkernels/MDGranularPorosityAux.h
@@ -0,0 +1,39 @@
+/**********************************************************************/
+/* DO NOT MODIFY THIS HEADER */
+/* MAGPIE - Mesoscale Atomistic Glue Program for Integrated Execution */
+/* */
+/* Copyright 2017 Battelle Energy Alliance, LLC */
+/* ALL RIGHTS RESERVED */
+/**********************************************************************/
+
+#ifndef MDGRANULARPOROSITYAUX_H
+#define MDGRANULARPOROSITYAUX_H
+
+#include "AuxKernel.h"
+
+// forward declarations
+class MDRunBase;
+class MDGranularPorosityAux;
+
+template <>
+InputParameters validParams();
+
+class MDGranularPorosityAux : public AuxKernel
+{
+public:
+ MDGranularPorosityAux(const InputParameters & params);
+ virtual ~MDGranularPorosityAux() {}
+
+ virtual Real computeValue();
+
+protected:
+ /// referene to the MDRunBase user object
+ const MDRunBase & _md_uo;
+
+ const bool _compute_packing;
+
+ /// property value that is computed only on qp = 0
+ Real _packing_fraction;
+};
+
+#endif // MDGRANULARPOROSITYAUXx_H
diff --git a/include/auxkernels/MDGranularPropertyAux.h b/include/auxkernels/MDGranularPropertyAux.h
new file mode 100644
index 00000000..e43118c6
--- /dev/null
+++ b/include/auxkernels/MDGranularPropertyAux.h
@@ -0,0 +1,45 @@
+/**********************************************************************/
+/* DO NOT MODIFY THIS HEADER */
+/* MAGPIE - Mesoscale Atomistic Glue Program for Integrated Execution */
+/* */
+/* Copyright 2017 Battelle Energy Alliance, LLC */
+/* ALL RIGHTS RESERVED */
+/**********************************************************************/
+
+#ifndef MDGRANULARPROPERTYAUX_H
+#define MDGRANULARPROPERTYAUX_H
+
+#include "AuxKernel.h"
+
+// forward declarations
+class MDRunBase;
+class MDGranularPropertyAux;
+
+template <>
+InputParameters validParams();
+
+class MDGranularPropertyAux : public AuxKernel
+{
+public:
+ MDGranularPropertyAux(const InputParameters & params);
+ virtual ~MDGranularPropertyAux() {}
+
+ virtual Real computeValue();
+
+ static MooseEnum mdAveragingType();
+
+protected:
+ /// referene to the MDRunBase user object
+ const MDRunBase & _md_uo;
+
+ /// the type of average to be computed
+ MooseEnum _average_type;
+
+ /// ID of the desired MD property
+ unsigned int _property_id;
+
+ /// property value that is computed only on qp = 0
+ Real _property_value;
+};
+
+#endif // MDGRANULARPROPERTYAUX_H
diff --git a/include/auxkernels/MDNParticleAux.h b/include/auxkernels/MDNParticleAux.h
index 8e7f6b23..5b01c4bc 100644
--- a/include/auxkernels/MDNParticleAux.h
+++ b/include/auxkernels/MDNParticleAux.h
@@ -28,6 +28,8 @@ class MDNParticleAux : public AuxKernel
protected:
const MDRunBase & _md_uo;
+
+ std::vector _particles;
};
#endif // MDNPARTICLEAUX_H
diff --git a/include/userobjects/LAMMPSFileRunner.h b/include/userobjects/LAMMPSFileRunner.h
index f02f2a87..f1a24eab 100644
--- a/include/userobjects/LAMMPSFileRunner.h
+++ b/include/userobjects/LAMMPSFileRunner.h
@@ -55,6 +55,9 @@ class LAMMPSFileRunner : public MDRunBase
/// column of x, y, z coordinate in LAMMPS files
std::vector _pos_columns;
+ /// column of properties in LAMMPS files
+ std::vector _prop_columns;
+
/// Conversion from FEM time to MD time_stamp
Function * _time_conversion;
};
diff --git a/include/userobjects/MDRunBase.h b/include/userobjects/MDRunBase.h
index aab5a016..e56b4dee 100644
--- a/include/userobjects/MDRunBase.h
+++ b/include/userobjects/MDRunBase.h
@@ -9,6 +9,9 @@
#ifndef MDRUNBASE_H
#define MDRUNBASE_H
+#define N_MD_PROPERTIES 8
+
+#include "overlap.hpp"
#include "GeneralUserObject.h"
#include "KDTree.h"
#include "libmesh/bounding_box.h"
@@ -36,24 +39,37 @@ class MDRunBase : public GeneralUserObject
/// particle's position
std::vector pos;
- /// particle's velocity
- std::vector vel;
-
/// the id of particle in the MD calculation
std::vector id;
/// the mesh element the particles are in
std::vector elem_id;
+
+ /// data attached to each particle
+ std::vector> properties;
};
/// access to the MDParticles
const MDParticles & particles() const { return _md_particles; }
+ // check if the stored particles are granular
+ bool isGranular() const { return _granular; }
+
/// access to the element to particle map
- const std::vector elemParticles(unique_id_type elem_id) const;
+ void elemParticles(unique_id_type elem_id, std::vector & elem_particles) const;
+
+ /// access the element to granular map
+ void granularElementVolumes(unique_id_type elem_id,
+ std::vector> & gran_vol) const;
+
+ /// access to MD particle's properties
+ Real particleProperty(unsigned int j, unsigned int prop_id) const;
+
+ /// accessor for md properties that are collected by this UO
+ MultiMooseEnum properties() const { return _properties; }
/// List of quantities to get from MD simulation
- MultiMooseEnum getMDQuantities() const;
+ static MultiMooseEnum mdParticleProperties();
protected:
/// call back function to update the particle list
@@ -65,6 +81,27 @@ class MDRunBase : public GeneralUserObject
/// map MDParticles to elements
void mapMDParticles();
+ /// update candidates for
+ void updateElementGranularVolumes();
+
+ /// helper function to contruct hexahedron
+ OVERLAP::Hexahedron overlapHex(const Elem * elem) const;
+
+ /// helper function to contruct unit hexahedron
+ OVERLAP::Hexahedron overlapUnitHex() const;
+
+ /// helper function to contruct tetrahedron
+ OVERLAP::Tetrahedron overlapTet(const Elem * elem) const;
+
+ /// helper function to construct unit tetrahedron
+ OVERLAP::Tetrahedron overlapUnitTet() const;
+
+ /// Properties that are requested from MD simulation
+ MultiMooseEnum _properties;
+
+ /// do the MD particles have extent?
+ bool _granular;
+
/// The Mesh we're using
MooseMesh & _mesh;
@@ -78,19 +115,25 @@ class MDRunBase : public GeneralUserObject
std::vector _bbox;
/// total number of particles
- unsigned int _n_particles;
+ unsigned int _n_particles = 0;
/// number of local particles
- unsigned int _n_local_particles;
+ unsigned int _n_local_particles = 0;
/// stores the location of
MDParticles _md_particles;
- /// a map from elem pointer to particles in this element
+ /// a map from elem unique id to particles in this element
std::map> _elem_particles;
+ /// a map from element unique id to std::vector of pair(MD particle id, volume of gran. particle in this element)
+ std::map>> _elem_granular_volumes;
+
/// A KDTree object to handle md_particles
std::unique_ptr _kd_tree;
+
+ /// maximum granular radius
+ Real _max_granular_radius;
};
#endif // MDRUNBASE_H
diff --git a/magpie.mk b/magpie.mk
index 9f185fd4..c90954ad 100644
--- a/magpie.mk
+++ b/magpie.mk
@@ -18,3 +18,5 @@ endif
include $(MAGPIE_DIR)/contrib/gsl.mk
include $(MAGPIE_DIR)/contrib/fftw3.mk
+
+app_INCLUDES += -I $(MAGPIE_DIR)/contrib/overlap
diff --git a/src/auxkernels/MDGranularPorosityAux.C b/src/auxkernels/MDGranularPorosityAux.C
new file mode 100644
index 00000000..f866b6cc
--- /dev/null
+++ b/src/auxkernels/MDGranularPorosityAux.C
@@ -0,0 +1,61 @@
+/**********************************************************************/
+/* DO NOT MODIFY THIS HEADER */
+/* MAGPIE - Mesoscale Atomistic Glue Program for Integrated Execution */
+/* */
+/* Copyright 2017 Battelle Energy Alliance, LLC */
+/* ALL RIGHTS RESERVED */
+/**********************************************************************/
+
+#include "MDGranularPorosityAux.h"
+#include "MDRunBase.h"
+
+registerMooseObject("MagpieApp", MDGranularPorosityAux);
+
+template <>
+InputParameters
+validParams()
+{
+ InputParameters params = validParams();
+ params.addRequiredParam("user_object", "Name of MD runner UserObject");
+ params.addParam("compute_packing_fraction", false, "Whether to compute porosity or packing fraction.");
+ params.addClassDescription(
+ "Computes porosity or packing fraction (1 - porosity) and injects it into and aux variable.");
+ return params;
+}
+
+MDGranularPorosityAux::MDGranularPorosityAux(const InputParameters & parameters)
+ : AuxKernel(parameters),
+ _md_uo(getUserObject("user_object")),
+ _compute_packing(getParam("compute_packing_fraction"))
+{
+ // ensure MD particles are granular
+ if (!_md_uo.isGranular())
+ mooseError("user_object stores non-granular particles.");
+
+ // ensure variable is elemental
+ if (isNodal())
+ mooseError("MDGranularPorosityAux only permits elemental variables.");
+}
+
+Real
+MDGranularPorosityAux::computeValue()
+{
+ if (_qp == 0)
+ {
+ _packing_fraction = 0.0;
+
+ // get the overlapping MD particles
+ std::vector> gran_vol;
+ _md_uo.granularElementVolumes(_current_elem->unique_id(), gran_vol);
+
+ // add the overlapping volumes
+ for (auto & p : gran_vol)
+ _packing_fraction += p.second;
+
+ // divide by element volume
+ _packing_fraction /= _current_elem->volume();
+ }
+ if (_compute_packing)
+ return _packing_fraction;
+ return 1 - _packing_fraction;
+}
diff --git a/src/auxkernels/MDGranularPropertyAux.C b/src/auxkernels/MDGranularPropertyAux.C
new file mode 100644
index 00000000..06e336b0
--- /dev/null
+++ b/src/auxkernels/MDGranularPropertyAux.C
@@ -0,0 +1,92 @@
+/**********************************************************************/
+/* DO NOT MODIFY THIS HEADER */
+/* MAGPIE - Mesoscale Atomistic Glue Program for Integrated Execution */
+/* */
+/* Copyright 2017 Battelle Energy Alliance, LLC */
+/* ALL RIGHTS RESERVED */
+/**********************************************************************/
+
+#include "MDGranularPropertyAux.h"
+#include "MDRunBase.h"
+
+registerMooseObject("MagpieApp", MDGranularPropertyAux);
+
+template <>
+InputParameters
+validParams()
+{
+ InputParameters params = validParams();
+ params.addRequiredParam("user_object", "Name of MD runner UserObject");
+ params.addParam("md_particle_property",
+ MDRunBase::mdParticleProperties(),
+ "Property that is injected into auxiliary variable.");
+ params.addParam("average_type",
+ MDGranularPropertyAux::mdAveragingType(),
+ "The type of average to be taken: "
+ "granular_sum|granular_densitygranular_interstitial_density.");
+ params.addClassDescription(
+ "Injects properties collected for MD particles from MDRunBase object user_object "
+ "into auxiliary variable.");
+ return params;
+}
+
+MDGranularPropertyAux::MDGranularPropertyAux(const InputParameters & parameters)
+ : AuxKernel(parameters),
+ _md_uo(getUserObject("user_object")),
+ _average_type(getParam("average_type"))
+{
+ // check length of MultiMooseEnum parameter, get it and check that UO has it
+ MultiMooseEnum mme = getParam("md_particle_property");
+ if (mme.size() != 1)
+ mooseError("md_particle_property must contain a single property.");
+ _property_id = mme.get(0);
+ if (!_md_uo.properties().contains(mme))
+ mooseError("Property ", _property_id, " not available from user_object.");
+
+ // ensure MD particles are granular
+ if (!_md_uo.isGranular())
+ mooseError("user_object stores non-granular particles.");
+
+ // ensure variable is elemental
+ if (isNodal())
+ mooseError("MDGranularPropertyAux only permits elemental variables.");
+}
+
+Real
+MDGranularPropertyAux::computeValue()
+{
+ if (_qp == 0)
+ {
+ _property_value = 0.0;
+
+ // get the overlapping MD particles
+ std::vector> gran_vol;
+ _md_uo.granularElementVolumes(_current_elem->unique_id(), gran_vol);
+
+ // loop over the overlapping MD particles and add property value
+ Real denominator = 0;
+ for (auto & p : gran_vol)
+ {
+ _property_value += p.second * _md_uo.particleProperty(p.first, _property_id);
+ denominator += p.second;
+ }
+
+ // compute property value depending on what average type is requested
+ if (_average_type == 1)
+ _property_value /= _current_elem->volume();
+ else if (_average_type == 2)
+ {
+ if (denominator == 0.0)
+ _property_value = 0.0;
+ else
+ _property_value /= denominator;
+ }
+ }
+ return _property_value;
+}
+
+MooseEnum
+MDGranularPropertyAux::mdAveragingType()
+{
+ return MooseEnum("granular_sum=0 granular_density=1 granular_interstitial_density=2");
+}
diff --git a/src/auxkernels/MDNParticleAux.C b/src/auxkernels/MDNParticleAux.C
index 4d446028..c53f7b90 100644
--- a/src/auxkernels/MDNParticleAux.C
+++ b/src/auxkernels/MDNParticleAux.C
@@ -30,5 +30,6 @@ MDNParticleAux::MDNParticleAux(const InputParameters & parameters)
Real
MDNParticleAux::computeValue()
{
- return _md_uo.elemParticles(_current_elem->unique_id()).size();
+ _md_uo.elemParticles(_current_elem->unique_id(), _particles);
+ return _particles.size();
}
diff --git a/src/userobjects/LAMMPSFileRunner.C b/src/userobjects/LAMMPSFileRunner.C
index 1785f23a..d709eb4c 100644
--- a/src/userobjects/LAMMPSFileRunner.C
+++ b/src/userobjects/LAMMPSFileRunner.C
@@ -29,7 +29,9 @@ validParams()
"a sequence.");
params.addRequiredParam>(
"xyz_columns", "Column ids of the x, y, and z coordinates of particles.");
-
+ std::vector empty = {};
+ params.addParam>(
+ "property_columns", empty, "Column ids of the properties.");
params.addParam("time_conversion",
"A conversion from FEM simulation time to MD time stamps.");
params.addClassDescription("Allows coupling FEM calculations to LAMMPS dump files.");
@@ -41,6 +43,7 @@ LAMMPSFileRunner::LAMMPSFileRunner(const InputParameters & parameters)
_time_sequence(getParam("time_sequence")),
_lammps_file(getParam("lammps_file")),
_pos_columns(getParam>("xyz_columns")),
+ _prop_columns(getParam>("property_columns")),
_time_conversion(nullptr)
{
;
@@ -50,6 +53,9 @@ LAMMPSFileRunner::LAMMPSFileRunner(const InputParameters & parameters)
if (isParamValid("time_conversion"))
_time_conversion = &getFunction("time_conversion");
+
+ if (_properties.size() != _prop_columns.size())
+ mooseError("properties array must have same length as property_columns");
}
void
@@ -86,6 +92,10 @@ LAMMPSFileRunner::updateParticleList()
// update the mapping to mesh if mesh has changed or ...
mapMDParticles();
+
+ // update the granular candidates as well if necessary
+ if (_granular)
+ updateElementGranularVolumes();
}
void
@@ -180,10 +190,12 @@ LAMMPSFileRunner::readLAMMPSFile(FileName filename)
// zero out the position & id vector
position.clear();
_md_particles.id.clear();
+ _md_particles.properties.clear();
// size the md particle vector considering not all processors have all particles
position.reserve(std::ceil(2.0 * _n_particles / _nproc));
_md_particles.id.reserve(std::ceil(2.0 * _n_particles / _nproc));
+ _md_particles.properties.reserve(std::ceil(2.0 * _n_particles / _nproc));
// read particle entries
_n_local_particles = 0;
@@ -195,6 +207,23 @@ LAMMPSFileRunner::readLAMMPSFile(FileName filename)
std::vector elements;
MooseUtils::tokenize(line, elements, 1, " ");
+#if DEBUG
+ // check that enough columns exist in debug
+ // find largest requested column
+ unsigned int largest_column = 0;
+ for (unsigned int i = 0; i < _dim; ++i)
+ if (_pos_columns[i] > largest_column)
+ largest_column = _pos_columns[i];
+
+ for (unsigned int i = 0; i < _prop_columns.size(); ++i)
+ if (_prop_columns[i] > largest_column)
+ largest_column = _prop_columns[i];
+
+ if (largest_column >= elements.size())
+ mooseError("Error reading ", filename, " on line ", line);
+#endif
+
+ // read location
Point pos;
for (unsigned int i = 0; i < _dim; ++i)
{
@@ -202,6 +231,15 @@ LAMMPSFileRunner::readLAMMPSFile(FileName filename)
sstr >> pos(i);
}
+ // read properties
+ std::array props;
+ for (unsigned int i = 0; i < _prop_columns.size(); ++i)
+ {
+ unsigned int prop_id = _properties.get(i);
+ std::stringstream sstr(elements[_prop_columns[i]]);
+ sstr >> props[prop_id];
+ }
+
// check if this particle is in this processor BBox
if (!_bbox[processor_id()].contains_point(pos))
continue;
@@ -209,6 +247,7 @@ LAMMPSFileRunner::readLAMMPSFile(FileName filename)
++_n_local_particles;
position.push_back(pos);
_md_particles.id.push_back(j);
+ _md_particles.properties.push_back(props);
}
file.close();
@@ -264,10 +303,12 @@ LAMMPSFileRunner::readLAMMPSFileHistory(std::pair filenames,
// zero out the position & id vector
position.clear();
_md_particles.id.clear();
+ _md_particles.properties.clear();
// size the md particle vector considering not all processors have all particles
position.reserve(std::ceil(2.0 * _n_particles / _nproc));
_md_particles.id.reserve(std::ceil(2.0 * _n_particles / _nproc));
+ _md_particles.properties.reserve(std::ceil(2.0 * _n_particles / _nproc));
// read particle entries
_n_local_particles = 0;
@@ -282,6 +323,22 @@ LAMMPSFileRunner::readLAMMPSFileHistory(std::pair filenames,
std::getline(file_before, line_before);
MooseUtils::tokenize(line_before, elements, 1, " ");
+#if DEBUG
+ // check that enough columns exist in debug
+ // find largest requested column
+ unsigned int largest_column = 0;
+ for (unsigned int i = 0; i < _dim; ++i)
+ if (_pos_columns[i] > largest_column)
+ largest_column = _pos_columns[i];
+
+ for (unsigned int i = 0; i < _prop_columns.size(); ++i)
+ if (_prop_columns[i] > largest_column)
+ largest_column = _prop_columns[i];
+
+ if (largest_column >= elements.size())
+ mooseError("Error reading ", filenames.first, " on line ", line_before);
+#endif
+
Point pos_before;
for (unsigned int i = 0; i < _dim; ++i)
{
@@ -289,6 +346,15 @@ LAMMPSFileRunner::readLAMMPSFileHistory(std::pair filenames,
sstr >> pos_before(i);
}
+ // read properties from before file
+ std::array props_before;
+ for (unsigned int i = 0; i < _prop_columns.size(); ++i)
+ {
+ unsigned int prop_id = _properties.get(i);
+ std::stringstream sstr(elements[_prop_columns[i]]);
+ sstr >> props_before[prop_id];
+ }
+
// get the MD particle from the after file
elements.clear();
std::string line_after;
@@ -296,6 +362,10 @@ LAMMPSFileRunner::readLAMMPSFileHistory(std::pair